#!/usr/bin/env python3
#
-#===- clang-tidy-diff.py - ClangTidy Diff Checker -----------*- python -*--===#
+# ===- clang-tidy-diff.py - ClangTidy Diff Checker -----------*- python -*--===#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
-#===-----------------------------------------------------------------------===#
+# ===-----------------------------------------------------------------------===#
r"""
ClangTidy Diff Checker
from pathlib import Path
try:
- import yaml
+ import yaml
except ImportError:
- yaml = None
+ yaml = None
-is_py2 = sys.version[0] == '2'
+is_py2 = sys.version[0] == "2"
if is_py2:
import Queue as queue
def run_tidy(task_queue, lock, timeout):
- watchdog = None
- while True:
- command = task_queue.get()
- try:
- proc = subprocess.Popen(command,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
-
- if timeout is not None:
- watchdog = threading.Timer(timeout, proc.kill)
- watchdog.start()
-
- stdout, stderr = proc.communicate()
-
- with lock:
- sys.stdout.write(stdout.decode('utf-8') + '\n')
- sys.stdout.flush()
- if stderr:
- sys.stderr.write(stderr.decode('utf-8') + '\n')
- sys.stderr.flush()
- except Exception as e:
- with lock:
- sys.stderr.write('Failed: ' + str(e) + ': '.join(command) + '\n')
- finally:
- with lock:
- if not (timeout is None or watchdog is None):
- if not watchdog.is_alive():
- sys.stderr.write('Terminated by timeout: ' +
- ' '.join(command) + '\n')
- watchdog.cancel()
- task_queue.task_done()
+ watchdog = None
+ while True:
+ command = task_queue.get()
+ try:
+ proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ if timeout is not None:
+ watchdog = threading.Timer(timeout, proc.kill)
+ watchdog.start()
+
+ stdout, stderr = proc.communicate()
+
+ with lock:
+ sys.stdout.write(stdout.decode("utf-8") + "\n")
+ sys.stdout.flush()
+ if stderr:
+ sys.stderr.write(stderr.decode("utf-8") + "\n")
+ sys.stderr.flush()
+ except Exception as e:
+ with lock:
+ sys.stderr.write("Failed: " + str(e) + ": ".join(command) + "\n")
+ finally:
+ with lock:
+ if not (timeout is None or watchdog is None):
+ if not watchdog.is_alive():
+ sys.stderr.write("Terminated by timeout: " + " ".join(command) + "\n")
+ watchdog.cancel()
+ task_queue.task_done()
def start_workers(max_tasks, tidy_caller, task_queue, lock, timeout):
- for _ in range(max_tasks):
- t = threading.Thread(target=tidy_caller, args=(task_queue, lock, timeout))
- t.daemon = True
- t.start()
+ for _ in range(max_tasks):
+ t = threading.Thread(target=tidy_caller, args=(task_queue, lock, timeout))
+ t.daemon = True
+ t.start()
def merge_replacement_files(tmpdir, mergefile):
- """Merge all replacement files in a directory into a single file"""
- # The fixes suggested by clang-tidy >= 4.0.0 are given under
- # the top level key 'Diagnostics' in the output yaml files
- mergekey = "Diagnostics"
- merged = []
- for replacefile in glob.iglob(os.path.join(tmpdir, '*.yaml')):
- with open(replacefile, 'r') as f:
- content = yaml.safe_load(f)
- if not content:
- continue # Skip empty files.
- merged.extend(content.get(mergekey, []))
-
- if merged:
- # MainSourceFile: The key is required by the definition inside
- # include/clang/Tooling/ReplacementsYaml.h, but the value
- # is actually never used inside clang-apply-replacements,
- # so we set it to '' here.
- output = {'MainSourceFile': '', mergekey: merged}
- with open(mergefile, 'w') as out:
- yaml.safe_dump(output, out)
- else:
- # Empty the file:
- open(mergefile, 'w').close()
+ """Merge all replacement files in a directory into a single file"""
+ # The fixes suggested by clang-tidy >= 4.0.0 are given under
+ # the top level key 'Diagnostics' in the output yaml files
+ mergekey = "Diagnostics"
+ merged = []
+ for replacefile in glob.iglob(os.path.join(tmpdir, "*.yaml")):
+ with open(replacefile, "r") as f:
+ content = yaml.safe_load(f)
+ if not content:
+ continue # Skip empty files.
+ merged.extend(content.get(mergekey, []))
+
+ if merged:
+ # MainSourceFile: The key is required by the definition inside
+ # include/clang/Tooling/ReplacementsYaml.h, but the value
+ # is actually never used inside clang-apply-replacements,
+ # so we set it to '' here.
+ output = {"MainSourceFile": "", mergekey: merged}
+ with open(mergefile, "w") as out:
+ yaml.safe_dump(output, out)
+ else:
+ # Empty the file:
+ open(mergefile, "w").close()
def main():
- parser = argparse.ArgumentParser(description=
- 'Run clang-tidy against changed files, and '
- 'output diagnostics only for modified '
- 'lines.')
- parser.add_argument('-clang-tidy-binary', metavar='PATH',
- default='clang-tidy',
- help='path to clang-tidy binary')
- parser.add_argument('-p', metavar='NUM', default=0,
- help='strip the smallest prefix containing P slashes')
- parser.add_argument('-regex', metavar='PATTERN', default=None,
- help='custom pattern selecting file paths to check '
- '(case sensitive, overrides -iregex)')
- parser.add_argument('-iregex', metavar='PATTERN', default=
- r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)',
- help='custom pattern selecting file paths to check '
- '(case insensitive, overridden by -regex)')
- parser.add_argument('-j', type=int, default=1,
- help='number of tidy instances to be run in parallel.')
- parser.add_argument('-timeout', type=int, default=None,
- help='timeout per each file in seconds.')
- parser.add_argument('-fix', action='store_true', default=False,
- help='apply suggested fixes')
- parser.add_argument('-checks',
- help='checks filter, when not specified, use clang-tidy '
- 'default',
- default='')
- parser.add_argument('-use-color', action='store_true',
- help='Use colors in output')
- parser.add_argument('-path', dest='build_path',
- help='Path used to read a compile command database.')
- if yaml:
- parser.add_argument('-export-fixes', metavar='FILE', dest='export_fixes',
- help='Create a yaml file to store suggested fixes in, '
- 'which can be applied with clang-apply-replacements.')
- parser.add_argument('-extra-arg', dest='extra_arg',
- action='append', default=[],
- help='Additional argument to append to the compiler '
- 'command line.')
- parser.add_argument('-extra-arg-before', dest='extra_arg_before',
- action='append', default=[],
- help='Additional argument to prepend to the compiler '
- 'command line.')
- parser.add_argument('-quiet', action='store_true', default=False,
- help='Run clang-tidy in quiet mode')
- parser.add_argument('-load', dest='plugins',
- action='append', default=[],
- help='Load the specified plugin in clang-tidy.')
-
- clang_tidy_args = []
- argv = sys.argv[1:]
- if '--' in argv:
- clang_tidy_args.extend(argv[argv.index('--'):])
- argv = argv[:argv.index('--')]
-
- args = parser.parse_args(argv)
-
- # Extract changed lines for each file.
- filename = None
- lines_by_file = {}
- for line in sys.stdin:
- match = re.search('^\+\+\+\ \"?(.*?/){%s}([^ \t\n\"]*)' % args.p, line)
- if match:
- filename = match.group(2)
- if filename is None:
- continue
-
- if args.regex is not None:
- if not re.match('^%s$' % args.regex, filename):
- continue
- else:
- if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE):
- continue
-
- match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
- if match:
- start_line = int(match.group(1))
- line_count = 1
- if match.group(3):
- line_count = int(match.group(3))
- if line_count == 0:
- continue
- end_line = start_line + line_count - 1
- lines_by_file.setdefault(filename, []).append([start_line, end_line])
-
- if not any(lines_by_file):
- print("No relevant changes found.")
- sys.exit(0)
-
- max_task_count = args.j
- if max_task_count == 0:
- max_task_count = multiprocessing.cpu_count()
- max_task_count = min(len(lines_by_file), max_task_count)
-
- tmpdir = None
- if yaml and args.export_fixes:
- tmpdir = tempfile.mkdtemp()
-
- # Tasks for clang-tidy.
- task_queue = queue.Queue(max_task_count)
- # A lock for console output.
- lock = threading.Lock()
-
- # Run a pool of clang-tidy workers.
- start_workers(max_task_count, run_tidy, task_queue, lock, args.timeout)
-
- # Form the common args list.
- common_clang_tidy_args = []
- if args.fix:
- common_clang_tidy_args.append('-fix')
- if args.checks != '':
- common_clang_tidy_args.append('-checks=' + args.checks)
- if args.quiet:
- common_clang_tidy_args.append('-quiet')
- if args.build_path is not None:
- common_clang_tidy_args.append('-p=%s' % args.build_path)
- if args.use_color:
- common_clang_tidy_args.append('--use-color')
- for arg in args.extra_arg:
- common_clang_tidy_args.append('-extra-arg=%s' % arg)
- for arg in args.extra_arg_before:
- common_clang_tidy_args.append('-extra-arg-before=%s' % arg)
- for plugin in args.plugins:
- common_clang_tidy_args.append('-load=%s' % plugin)
-
- for name in lines_by_file:
- line_filter_json = json.dumps(
- # clang-tidy only supports filenames in -line-filter, not paths
- [{"name": Path(name).name, "lines": lines_by_file[name]}],
- separators=(',', ':'))
-
- # Run clang-tidy on files containing changes.
- command = [args.clang_tidy_binary]
- command.append('-line-filter=' + line_filter_json)
+ parser = argparse.ArgumentParser(
+ description="Run clang-tidy against changed files, and output diagnostics only for modified lines."
+ )
+ parser.add_argument("-clang-tidy-binary", metavar="PATH", default="clang-tidy", help="path to clang-tidy binary")
+ parser.add_argument("-p", metavar="NUM", default=0, help="strip the smallest prefix containing P slashes")
+ parser.add_argument(
+ "-regex",
+ metavar="PATTERN",
+ default=None,
+ help="custom pattern selecting file paths to check (case sensitive, overrides -iregex)",
+ )
+ parser.add_argument(
+ "-iregex",
+ metavar="PATTERN",
+ default=r".*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)",
+ help="custom pattern selecting file paths to check (case insensitive, overridden by -regex)",
+ )
+ parser.add_argument("-j", type=int, default=1, help="number of tidy instances to be run in parallel.")
+ parser.add_argument("-timeout", type=int, default=None, help="timeout per each file in seconds.")
+ parser.add_argument("-fix", action="store_true", default=False, help="apply suggested fixes")
+ parser.add_argument("-checks", help="checks filter, when not specified, use clang-tidy default", default="")
+ parser.add_argument("-use-color", action="store_true", help="Use colors in output")
+ parser.add_argument("-path", dest="build_path", help="Path used to read a compile command database.")
+ if yaml:
+ parser.add_argument(
+ "-export-fixes",
+ metavar="FILE",
+ dest="export_fixes",
+ help="Create a yaml file to store suggested fixes in, which can be applied with clang-apply-replacements.",
+ )
+ parser.add_argument(
+ "-extra-arg",
+ dest="extra_arg",
+ action="append",
+ default=[],
+ help="Additional argument to append to the compiler command line.",
+ )
+ parser.add_argument(
+ "-extra-arg-before",
+ dest="extra_arg_before",
+ action="append",
+ default=[],
+ help="Additional argument to prepend to the compiler command line.",
+ )
+ parser.add_argument("-quiet", action="store_true", default=False, help="Run clang-tidy in quiet mode")
+ parser.add_argument(
+ "-load", dest="plugins", action="append", default=[], help="Load the specified plugin in clang-tidy."
+ )
+
+ clang_tidy_args = []
+ argv = sys.argv[1:]
+ if "--" in argv:
+ clang_tidy_args.extend(argv[argv.index("--") :])
+ argv = argv[: argv.index("--")]
+
+ args = parser.parse_args(argv)
+
+ # Extract changed lines for each file.
+ filename = None
+ lines_by_file = {}
+ for line in sys.stdin:
+ match = re.search('^\+\+\+\ "?(.*?/){%s}([^ \t\n"]*)' % args.p, line)
+ if match:
+ filename = match.group(2)
+ if filename is None:
+ continue
+
+ if args.regex is not None:
+ if not re.match("^%s$" % args.regex, filename):
+ continue
+ else:
+ if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE):
+ continue
+
+ match = re.search("^@@.*\+(\d+)(,(\d+))?", line)
+ if match:
+ start_line = int(match.group(1))
+ line_count = 1
+ if match.group(3):
+ line_count = int(match.group(3))
+ if line_count == 0:
+ continue
+ end_line = start_line + line_count - 1
+ lines_by_file.setdefault(filename, []).append([start_line, end_line])
+
+ if not any(lines_by_file):
+ print("No relevant changes found.")
+ sys.exit(0)
+
+ max_task_count = args.j
+ if max_task_count == 0:
+ max_task_count = multiprocessing.cpu_count()
+ max_task_count = min(len(lines_by_file), max_task_count)
+
+ tmpdir = None
+ if yaml and args.export_fixes:
+ tmpdir = tempfile.mkdtemp()
+
+ # Tasks for clang-tidy.
+ task_queue = queue.Queue(max_task_count)
+ # A lock for console output.
+ lock = threading.Lock()
+
+ # Run a pool of clang-tidy workers.
+ start_workers(max_task_count, run_tidy, task_queue, lock, args.timeout)
+
+ # Form the common args list.
+ common_clang_tidy_args = []
+ if args.fix:
+ common_clang_tidy_args.append("-fix")
+ if args.checks != "":
+ common_clang_tidy_args.append("-checks=" + args.checks)
+ if args.quiet:
+ common_clang_tidy_args.append("-quiet")
+ if args.build_path is not None:
+ common_clang_tidy_args.append("-p=%s" % args.build_path)
+ if args.use_color:
+ common_clang_tidy_args.append("--use-color")
+ for arg in args.extra_arg:
+ common_clang_tidy_args.append("-extra-arg=%s" % arg)
+ for arg in args.extra_arg_before:
+ common_clang_tidy_args.append("-extra-arg-before=%s" % arg)
+ for plugin in args.plugins:
+ common_clang_tidy_args.append("-load=%s" % plugin)
+
+ for name in lines_by_file:
+ line_filter_json = json.dumps(
+ # clang-tidy only supports filenames in -line-filter, not paths
+ [{"name": Path(name).name, "lines": lines_by_file[name]}],
+ separators=(",", ":"),
+ )
+
+ # Run clang-tidy on files containing changes.
+ command = [args.clang_tidy_binary]
+ command.append("-line-filter=" + line_filter_json)
+ if yaml and args.export_fixes:
+ # Get a temporary file. We immediately close the handle so clang-tidy can
+ # overwrite it.
+ (handle, tmp_name) = tempfile.mkstemp(suffix=".yaml", dir=tmpdir)
+ os.close(handle)
+ command.append("-export-fixes=" + tmp_name)
+ command.extend(common_clang_tidy_args)
+ command.append(name)
+ command.extend(clang_tidy_args)
+
+ task_queue.put(command)
+
+ # Wait for all threads to be done.
+ task_queue.join()
+
if yaml and args.export_fixes:
- # Get a temporary file. We immediately close the handle so clang-tidy can
- # overwrite it.
- (handle, tmp_name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir)
- os.close(handle)
- command.append('-export-fixes=' + tmp_name)
- command.extend(common_clang_tidy_args)
- command.append(name)
- command.extend(clang_tidy_args)
-
- task_queue.put(command)
-
- # Wait for all threads to be done.
- task_queue.join()
-
- if yaml and args.export_fixes:
- print('Writing fixes to ' + args.export_fixes + ' ...')
- try:
- merge_replacement_files(tmpdir, args.export_fixes)
- except Exception:
- sys.stderr.write('Error exporting fixes.\n')
- traceback.print_exc()
-
- if tmpdir:
- shutil.rmtree(tmpdir)
-
-
-if __name__ == '__main__':
- main()
+ print("Writing fixes to " + args.export_fixes + " ...")
+ try:
+ merge_replacement_files(tmpdir, args.export_fixes)
+ except Exception:
+ sys.stderr.write("Error exporting fixes.\n")
+ traceback.print_exc()
+
+ if tmpdir:
+ shutil.rmtree(tmpdir)
+
+
+if __name__ == "__main__":
+ main()
def create_argument_parser():
"""Create command-line argument parser."""
- parser = argparse.ArgumentParser(
- description="Convert clang-tidy output to Github Actions"
- )
+ parser = argparse.ArgumentParser(description="Convert clang-tidy output to Github Actions")
parser.add_argument(
"--fixes-file",
type=str,
full_filename = filename
full_filename = Path(full_filename)
- full_filename = (
- full_filename.as_posix()
- if full_filename.is_absolute()
- else os.path.join(directory, filename)
- )
+ full_filename = full_filename.as_posix() if full_filename.is_absolute() else os.path.join(directory, filename)
try:
file_contents = helpers.load_file(full_filename)
def create_argument_parser():
"""Create command-line argument parser."""
- parser = argparse.ArgumentParser(
- description="Filter git diff files that are not in the product"
- )
+ parser = argparse.ArgumentParser(description="Filter git diff files that are not in the product")
parser.add_argument(
"--product",
type=str,
DEBUG = False
+
def debug_print(string):
if DEBUG:
print(string)
+
def get_relative_to_product_source_dir(product, target):
- if product == 'auth':
+ if product == "auth":
# authoritative or tool
return target
- if product == 'recursor':
- return os.path.join('pdns', 'recursordist', target)
- if product == 'dnsdist':
- return os.path.join('pdns', 'dnsdistdist', target)
+ if product == "recursor":
+ return os.path.join("pdns", "recursordist", target)
+ if product == "dnsdist":
+ return os.path.join("pdns", "dnsdistdist", target)
return None
+
def remove_meson_dist_path(product, target):
# target looks like this: /tmp/dnsdist-meson-dist-build/meson-dist/dnsdist-0.0.0-git1/xsk.hh
path = Path(target)
- index = path.parts.index('meson-dist')
+ index = path.parts.index("meson-dist")
# skip up to meson-dist and the directory below that,
# so we now have: xsk.hh
relevant = str(path.relative_to(path.parents[len(path.parts) - (index + 3)]))
# get rid of the distdir path, to get file paths as they are in the repository
# if we are building from meson, it might look like this:
# /__w/pdns/pdns/pdns/dnsdistdist/dnsdist-0.0.0-git1/config.h
- if f'pdns-{version}' in target:
+ if f"pdns-{version}" in target:
# authoritative or tool
- authPath = os.path.join(repositoryRoot, f'pdns-{version}')
+ authPath = os.path.join(repositoryRoot, f"pdns-{version}")
relativeToAuth = os.path.relpath(target, authPath)
- target = get_relative_to_product_source_dir('auth', relativeToAuth)
+ target = get_relative_to_product_source_dir("auth", relativeToAuth)
return target
- if f'pdns-recursor-{version}' in target:
- recPath = os.path.join(repositoryRoot, 'pdns', 'recursordist', f'pdns-recursor-{version}')
+ if f"pdns-recursor-{version}" in target:
+ recPath = os.path.join(repositoryRoot, "pdns", "recursordist", f"pdns-recursor-{version}")
relativeToRec = os.path.relpath(target, recPath)
- return get_relative_to_product_source_dir('recursor', relativeToRec)
- if f'dnsdist-{version}' in target:
- distPath = os.path.join(repositoryRoot, 'pdns', 'dnsdistdist', f'dnsdist-{version}')
+ return get_relative_to_product_source_dir("recursor", relativeToRec)
+ if f"dnsdist-{version}" in target:
+ distPath = os.path.join(repositoryRoot, "pdns", "dnsdistdist", f"dnsdist-{version}")
relativeToDist = os.path.relpath(target, distPath)
- target = get_relative_to_product_source_dir('dnsdist', relativeToDist)
+ target = get_relative_to_product_source_dir("dnsdist", relativeToDist)
return target
# let's assume we already have a full path to the repository, like
relativeToDist = os.path.relpath(target, distPath)
return relativeToDist
+
def process():
repositoryRoot = os.path.realpath(sys.argv[1])
product = sys.argv[2]
version = sys.argv[3]
inputFile = sys.argv[4]
outputFile = sys.argv[5]
- with open(inputFile, mode='r', encoding='utf-8') as inputFilePtr:
- with open(outputFile, mode='w', encoding='utf-8') as outputFilePtr:
+ with open(inputFile, mode="r", encoding="utf-8") as inputFilePtr:
+ with open(outputFile, mode="w", encoding="utf-8") as outputFilePtr:
for line in inputFilePtr:
- if not line.startswith('SF:'):
+ if not line.startswith("SF:"):
outputFilePtr.write(line)
continue
- parts = line.split(':')
+ parts = line.split(":")
if len(parts) != 2:
outputFilePtr.write(line)
continue
source_file = parts[1].rstrip()
# get rid of symbolic links
target = os.path.realpath(source_file)
- debug_print(f'- Got source_file={source_file}, target={target}')
+ debug_print(f"- Got source_file={source_file}, target={target}")
- if '/meson-dist/' in target:
+ if "/meson-dist/" in target:
# this is a file that comes from a meson dist tarball
target = remove_meson_dist_path(product, target)
- debug_print(f'meson-dist -> target={target}')
+ debug_print(f"meson-dist -> target={target}")
else:
target = remove_dist_dir_path(repositoryRoot, version, target)
- debug_print(f'dist dir -> target={target}')
+ debug_print(f"dist dir -> target={target}")
if target is None:
continue
# we need to properly map symbolic links
fullPath = os.path.join(repositoryRoot, target)
- debug_print(f'fullPath is {fullPath}')
+ debug_print(f"fullPath is {fullPath}")
if os.path.islink(fullPath):
# get the link target
realPath = os.path.realpath(fullPath)
# and make it relative again
target = os.path.relpath(realPath, repositoryRoot)
- debug_print(f'=> final target is {target}')
+ debug_print(f"=> final target is {target}")
outputFilePtr.write(f"SF:{target}\n")
-if __name__ == '__main__':
+
+if __name__ == "__main__":
process()
import getpass
argp = argparse.ArgumentParser()
-argp.add_argument('--oneline', action='store_true',
- help='Make one-lined changelog entries (for 4.0 and older)')
-argp.add_argument('--username',
- help='Use the specified username for Basic Authentication to the GitHub API, allowing a higher rate limit')
-argp.add_argument('--access_token',
- help='Use API access token instead of username & password combination')
-argp.add_argument('pullrequest', metavar='PULL_REQUEST', nargs='+',
- help='Make changelogs for these Pull Request #\'s')
+argp.add_argument("--oneline", action="store_true", help="Make one-lined changelog entries (for 4.0 and older)")
+argp.add_argument(
+ "--username",
+ help="Use the specified username for Basic Authentication to the GitHub API, allowing a higher rate limit",
+)
+argp.add_argument("--access_token", help="Use API access token instead of username & password combination")
+argp.add_argument("pullrequest", metavar="PULL_REQUEST", nargs="+", help="Make changelogs for these Pull Request #'s")
arguments = argp.parse_args()
-ticket_regex = re.compile(r'(?:[Cc]loses|[Ff]ixes)? #(\d+)')
+ticket_regex = re.compile(r"(?:[Cc]loses|[Ff]ixes)? #(\d+)")
-out = ''
+out = ""
httpAuth = None
if arguments.username:
password = getpass.getpass("GitHub password for '" + arguments.username + "': ")
access_token = arguments.access_token
for pr in sorted(arguments.pullrequest):
- if pr[0] == '#':
+ if pr[0] == "#":
pr = pr[1:]
try:
if access_token:
- res = requests.get('https://api.github.com/repos/PowerDNS/pdns/pulls/'
- '{}'.format(pr),
- headers={'Authorization': 'token ' + access_token})
+ res = requests.get(
+ "https://api.github.com/repos/PowerDNS/pdns/pulls/{}".format(pr),
+ headers={"Authorization": "token " + access_token},
+ )
else:
- res = requests.get('https://api.github.com/repos/PowerDNS/pdns/pulls/'
- '{}'.format(pr), auth=httpAuth)
+ res = requests.get("https://api.github.com/repos/PowerDNS/pdns/pulls/{}".format(pr), auth=httpAuth)
pr_info = res.json()
except (requests.exceptions.HTTPError, ValueError) as e:
print(e)
sys.exit(1)
if arguments.oneline:
- out += '- `#{pr} <{url}>`__: {title}'.format(
- pr=pr, url=pr_info['html_url'], title=pr_info['title']
- )
+ out += "- `#{pr} <{url}>`__: {title}".format(pr=pr, url=pr_info["html_url"], title=pr_info["title"])
else:
- out += ' .. change::\n' + \
- ' :tags: XXXXXX\n' + \
- ' :pullreq: {}\n'.format(pr)
- body = pr_info.get('body', None)
- if pr_info.get('message', None) and not body:
+ out += " .. change::\n" + " :tags: XXXXXX\n" + " :pullreq: {}\n".format(pr)
+ body = pr_info.get("body", None)
+ if pr_info.get("message", None) and not body:
# A bit blunt but better than we had.
- print('{}'.format(pr_info['message']))
+ print("{}".format(pr_info["message"]))
sys.exit(1)
elif body:
tickets = re.findall(ticket_regex, body)
if len(tickets):
- out += ' :tickets: {}\n'.format(', '.join(tickets))
- out += '\n {}'.format(pr_info['title'][0].capitalize() + pr_info['title'][1:])
+ out += " :tickets: {}\n".format(", ".join(tickets))
+ out += "\n {}".format(pr_info["title"][0].capitalize() + pr_info["title"][1:])
- if pr_info['user']['login'].lower() not in ['habbie',
- 'rgacogne',
- 'aerique',
- 'chbruyand',
- 'omoerbeek',
- 'miodvallat']:
+ if pr_info["user"]["login"].lower() not in [
+ "habbie",
+ "rgacogne",
+ "aerique",
+ "chbruyand",
+ "omoerbeek",
+ "miodvallat",
+ ]:
try:
if access_token:
- user_info = requests.get(pr_info['user']['url'],
- headers={'Authorization': 'token ' + access_token}).json()
+ user_info = requests.get(
+ pr_info["user"]["url"], headers={"Authorization": "token " + access_token}
+ ).json()
else:
- user_info = requests.get(pr_info['user']['url'], auth=httpAuth).json()
+ user_info = requests.get(pr_info["user"]["url"], auth=httpAuth).json()
except (requests.exceptions.HTTPError, ValueError) as e:
print(e)
sys.exit(1)
- if 'name' in user_info:
- out += ' ({})'.format(user_info['name'])
+ if "name" in user_info:
+ out += " ({})".format(user_info["name"])
else:
- out += ' (@{})'.format(user_info['login'])
- out += '\n'
+ out += " (@{})".format(user_info["login"])
+ out += "\n"
if not arguments.oneline:
- out += '\n'
+ out += "\n"
print(out)
def get_commits(pr):
try:
- res = requests.get('https://api.github.com/repos/PowerDNS/pdns/pulls/'
- '{}/commits'.format(pr)).json()
- return [c['sha'] for c in res]
+ res = requests.get("https://api.github.com/repos/PowerDNS/pdns/pulls/{}/commits".format(pr)).json()
+ return [c["sha"] for c in res]
except (ValueError, requests.exceptions.HTTPError) as e:
print(e)
sys.exit(1)
a = argparse.ArgumentParser()
action = a.add_mutually_exclusive_group(required=True)
action.add_argument(
- '-b', '--backport-unto', metavar='REF', nargs=1, help='Backport, using '
- 'cherry-pick, all commits from PULL_REQUEST onto REF. This is done on a '
+ "-b",
+ "--backport-unto",
+ metavar="REF",
+ nargs=1,
+ help="Backport, using "
+ "cherry-pick, all commits from PULL_REQUEST onto REF. This is done on a "
'branch called "backport-PULL_REQUEST-to-basename(REF)". When the cherry-pick fails, solve '
- 'the conflict as usual and run "git cherry-pick --continue --allow-empty"')
+ 'the conflict as usual and run "git cherry-pick --continue --allow-empty"',
+)
action.add_argument(
- '-m', '--merge-into', metavar='REF', nargs=1, help='Take the backport-'
- 'PULL_REQUEST branch and merge it into REF')
-a.add_argument(
- 'pull_request', metavar='PULL_REQUEST', type=int,
- help='The PR number to backport')
+ "-m", "--merge-into", metavar="REF", nargs=1, help="Take the backport-PULL_REQUEST branch and merge it into REF"
+)
+a.add_argument("pull_request", metavar="PULL_REQUEST", type=int, help="The PR number to backport")
args = a.parse_args()
if args.backport_unto:
- command = ['git', 'checkout', '-b',
- 'backport-{}-to-{}'.format(args.pull_request, args.backport_unto[0].split('/')[-1]), args.backport_unto[0]]
+ command = [
+ "git",
+ "checkout",
+ "-b",
+ "backport-{}-to-{}".format(args.pull_request, args.backport_unto[0].split("/")[-1]),
+ args.backport_unto[0],
+ ]
run_command(command)
commits = get_commits(args.pull_request)
- command = ['git', 'cherry-pick', '-x', '--allow-empty'] + commits
+ command = ["git", "cherry-pick", "-x", "--allow-empty"] + commits
run_command(command)
if args.merge_into:
- command = ['git', 'checkout', args.merge_into[0]]
+ command = ["git", "checkout", args.merge_into[0]]
run_command(command)
- command = ['git', 'merge', '--no-ff',
- 'backport-{}-to-{}'.format(args.pull_request, args.merge_into[0].split('/')[-1]), '-m',
- 'Backport #{}'.format(args.pull_request)]
+ command = [
+ "git",
+ "merge",
+ "--no-ff",
+ "backport-{}-to-{}".format(args.pull_request, args.merge_into[0].split("/")[-1]),
+ "-m",
+ "Backport #{}".format(args.pull_request),
+ ]
run_command(command)
# Globals
-g_version = '1.0.4'
+g_version = "1.0.4"
g_verbose = False
-g_env = Environment(
- loader=FileSystemLoader('templates/')
-)
+g_env = Environment(loader=FileSystemLoader("templates/"))
-g_dockerfile = 'Dockerfile.'
+g_dockerfile = "Dockerfile."
g_run_output = False
# Init Functions
+
def init_argparser():
- parser = argparse.ArgumentParser(description='Generate Docker files to ' +
- 'test PowerDNS repositories.')
- parser.add_argument('release', metavar='RELEASE',
- choices=[# Authoritative Server
- 'auth-48', 'auth-49', 'auth-50',
- 'auth-master',
- # Recursor
- 'rec-48', 'rec-49', 'rec-50', 'rec-51', 'rec-52', 'rec-53',
- 'rec-54', 'rec-master',
- # DNSDist
- 'dnsdist-17', 'dnsdist-18', 'dnsdist-19', 'dnsdist-20',
- 'dnsdist-21', 'dnsdist-master'],
- help='the release to generate Docker files for: ' +
- '%(choices)s')
- parser.add_argument('--run-output', action='store_true',
- help='always show output from running a container')
- parser.add_argument('--test', action='store_true',
- help='test the release')
- parser.add_argument('--test-aarch64', action='store_true',
- help='test the release for ARM64')
- parser.add_argument('--verbose', action='store_true',
- help='verbose output')
- parser.add_argument('--version', action='store_true',
- help='print version')
+ parser = argparse.ArgumentParser(description="Generate Docker files to " + "test PowerDNS repositories.")
+ parser.add_argument(
+ "release",
+ metavar="RELEASE",
+ choices=[ # Authoritative Server
+ "auth-48",
+ "auth-49",
+ "auth-50",
+ "auth-master",
+ # Recursor
+ "rec-48",
+ "rec-49",
+ "rec-50",
+ "rec-51",
+ "rec-52",
+ "rec-53",
+ "rec-54",
+ "rec-master",
+ # DNSDist
+ "dnsdist-17",
+ "dnsdist-18",
+ "dnsdist-19",
+ "dnsdist-20",
+ "dnsdist-21",
+ "dnsdist-master",
+ ],
+ help="the release to generate Docker files for: " + "%(choices)s",
+ )
+ parser.add_argument("--run-output", action="store_true", help="always show output from running a container")
+ parser.add_argument("--test", action="store_true", help="test the release")
+ parser.add_argument("--test-aarch64", action="store_true", help="test the release for ARM64")
+ parser.add_argument("--verbose", action="store_true", help="verbose output")
+ parser.add_argument("--version", action="store_true", help="print version")
return parser
# Release File Functions
-def write_dockerfile (os, os_version, release):
- tpl = g_env.get_template('Dockerfile-{}.jinja2'.format(os))
- if os == 'raspbian':
- os_image = 'resin/rpi-raspbian'
- elif os == 'el':
- os_image = 'oraclelinux'
+def write_dockerfile(os, os_version, release):
+ tpl = g_env.get_template("Dockerfile-{}.jinja2".format(os))
+
+ if os == "raspbian":
+ os_image = "resin/rpi-raspbian"
+ elif os == "el":
+ os_image = "oraclelinux"
else:
os_image = os
- if release.startswith('auth-'):
- if os in ('el'):
- pkg = 'pdns'
+ if release.startswith("auth-"):
+ if os in ("el"):
+ pkg = "pdns"
else:
- pkg = 'pdns-server'
- cmd = 'pdns_server'
- elif release.startswith('rec-'):
- pkg = 'pdns-recursor'
- cmd = 'pdns_recursor'
- elif release.startswith('dnsdist-'):
- pkg = 'dnsdist'
- cmd = 'dnsdist'
-
- with open('{}{}.{}-{}'.format(g_dockerfile, release, os, os_version), 'w') as f:
+ pkg = "pdns-server"
+ cmd = "pdns_server"
+ elif release.startswith("rec-"):
+ pkg = "pdns-recursor"
+ cmd = "pdns_recursor"
+ elif release.startswith("dnsdist-"):
+ pkg = "dnsdist"
+ cmd = "dnsdist"
+
+ with open("{}{}.{}-{}".format(g_dockerfile, release, os, os_version), "w") as f:
# This comment was in the template for the `--nobest` part but that makes
# the template look even more different than the final output, so:
#
# > When should the logic be in the code and when in the template? :shrug:
# > I prefer it to be in the code but I also do not want to add extra vars
# > and logic to the code unless necessary.
- f.write(tpl.render({ "os": os,
- "os_image": os_image,
- "os_version": os_version,
- "release": release,
- "cmd": cmd,
- "pkg": pkg }))
+ f.write(
+ tpl.render(
+ {"os": os, "os_image": os_image, "os_version": os_version, "release": release, "cmd": cmd, "pkg": pkg}
+ )
+ )
-def write_list_file (os, os_version, release):
- tpl = g_env.get_template('pdns-list.jinja2')
+def write_list_file(os, os_version, release):
+ tpl = g_env.get_template("pdns-list.jinja2")
- with open('pdns.list.{}.{}-{}'.format(release, os, os_version), 'w') as f:
- f.write(tpl.render({ "os": os,
- "os_version": os_version,
- "release": release }))
+ with open("pdns.list.{}.{}-{}".format(release, os, os_version), "w") as f:
+ f.write(tpl.render({"os": os, "os_version": os_version, "release": release}))
-def write_pkg_pin_file (release):
- tpl = g_env.get_template('pkg-pin.jinja2')
+def write_pkg_pin_file(release):
+ tpl = g_env.get_template("pkg-pin.jinja2")
- if release.startswith('auth-') or release.startswith('rec-'):
- pkg = 'pdns-'
- elif release.startswith('dnsdist-'):
- pkg = 'dnsdist'
+ if release.startswith("auth-") or release.startswith("rec-"):
+ pkg = "pdns-"
+ elif release.startswith("dnsdist-"):
+ pkg = "dnsdist"
- with open('pkg-pin', 'w') as f:
- f.write(tpl.render({ "pkg": pkg }))
+ with open("pkg-pin", "w") as f:
+ f.write(tpl.render({"pkg": pkg}))
-def write_release_files (release):
+def write_release_files(release):
if g_verbose:
print("Writing release files...")
- if release in ['auth-48', 'auth-49', 'auth-50', 'auth-master',
- 'rec-48', 'rec-49', 'rec-50', 'rec-51', 'rec-52', 'rec-53',
- 'rec-54', 'rec-master',
- 'dnsdist-17', 'dnsdist-18', 'dnsdist-19', 'dnsdist-20',
- 'dnsdist-21', 'dnsdist-master']:
+ if release in [
+ "auth-48",
+ "auth-49",
+ "auth-50",
+ "auth-master",
+ "rec-48",
+ "rec-49",
+ "rec-50",
+ "rec-51",
+ "rec-52",
+ "rec-53",
+ "rec-54",
+ "rec-master",
+ "dnsdist-17",
+ "dnsdist-18",
+ "dnsdist-19",
+ "dnsdist-20",
+ "dnsdist-21",
+ "dnsdist-master",
+ ]:
write_pkg_pin_file(release)
- write_dockerfile('el', '8', release)
- write_dockerfile('el', '9', release)
- write_dockerfile('debian', 'bullseye', release)
- write_list_file('debian', 'bullseye', release)
- if not release in ['auth-50', 'rec-54', 'rec-53', 'dnsdist-20', 'dnsdist-21']:
- write_dockerfile('ubuntu', 'focal', release)
- write_list_file('ubuntu', 'focal', release)
- write_dockerfile('ubuntu', 'jammy', release)
- write_list_file('ubuntu', 'jammy', release)
-
- if release in ['auth-50', 'auth-master',
- 'rec-53', 'rec-54', 'rec-master',
- 'dnsdist-20', 'dnsdist-21', 'dnsdist-master']:
- write_dockerfile('el', '10', release)
-
- if release in ['auth-48', 'auth-49', 'auth-50', 'auth-master',
- 'rec-48', 'rec-49', 'rec-50', 'rec-51', 'rec-52', 'rec-53',
- 'rec-54', 'rec-master',
- 'dnsdist-19', 'dnsdist-20', 'dnsdist-21', 'dnsdist-master']:
- write_dockerfile('debian', 'bookworm', release)
- write_list_file('debian', 'bookworm', release)
-
- if release in ['auth-49', 'auth-50', 'auth-master',
- 'rec-53', 'rec-54', 'rec-master',
- 'dnsdist-20', 'dnsdist-21', 'dnsdist-master']:
- write_dockerfile('debian', 'trixie', release)
- write_list_file('debian', 'trixie', release)
-
- if release in ['auth-49', 'auth-50', 'auth-master',
- 'rec-50', 'rec-51', 'rec-52', 'rec-53', 'rec-54', 'rec-master',
- 'dnsdist-19', 'dnsdist-20', 'dnsdist-21', 'dnsdist-master']:
- write_dockerfile('ubuntu', 'noble', release)
- write_list_file('ubuntu', 'noble', release)
+ write_dockerfile("el", "8", release)
+ write_dockerfile("el", "9", release)
+ write_dockerfile("debian", "bullseye", release)
+ write_list_file("debian", "bullseye", release)
+ if not release in ["auth-50", "rec-54", "rec-53", "dnsdist-20", "dnsdist-21"]:
+ write_dockerfile("ubuntu", "focal", release)
+ write_list_file("ubuntu", "focal", release)
+ write_dockerfile("ubuntu", "jammy", release)
+ write_list_file("ubuntu", "jammy", release)
+
+ if release in [
+ "auth-50",
+ "auth-master",
+ "rec-53",
+ "rec-54",
+ "rec-master",
+ "dnsdist-20",
+ "dnsdist-21",
+ "dnsdist-master",
+ ]:
+ write_dockerfile("el", "10", release)
+
+ if release in [
+ "auth-48",
+ "auth-49",
+ "auth-50",
+ "auth-master",
+ "rec-48",
+ "rec-49",
+ "rec-50",
+ "rec-51",
+ "rec-52",
+ "rec-53",
+ "rec-54",
+ "rec-master",
+ "dnsdist-19",
+ "dnsdist-20",
+ "dnsdist-21",
+ "dnsdist-master",
+ ]:
+ write_dockerfile("debian", "bookworm", release)
+ write_list_file("debian", "bookworm", release)
+
+ if release in [
+ "auth-49",
+ "auth-50",
+ "auth-master",
+ "rec-53",
+ "rec-54",
+ "rec-master",
+ "dnsdist-20",
+ "dnsdist-21",
+ "dnsdist-master",
+ ]:
+ write_dockerfile("debian", "trixie", release)
+ write_list_file("debian", "trixie", release)
+
+ if release in [
+ "auth-49",
+ "auth-50",
+ "auth-master",
+ "rec-50",
+ "rec-51",
+ "rec-52",
+ "rec-53",
+ "rec-54",
+ "rec-master",
+ "dnsdist-19",
+ "dnsdist-20",
+ "dnsdist-21",
+ "dnsdist-master",
+ ]:
+ write_dockerfile("ubuntu", "noble", release)
+ write_list_file("ubuntu", "noble", release)
+
# Test Release Functions
-def build (dockerfile, arch='x86_64'):
+
+def build(dockerfile, arch="x86_64"):
# Maybe create `determine_tag` function.
if len(str(dockerfile)) <= len(g_dockerfile):
- print('Unable to determine tag for {}'.format(dockerfile))
+ print("Unable to determine tag for {}".format(dockerfile))
return (None, None)
- tag = str(dockerfile)[len(g_dockerfile):]
- print('Building Docker image using {}...'.format(dockerfile))
+ tag = str(dockerfile)[len(g_dockerfile) :]
+ print("Building Docker image using {}...".format(dockerfile))
if g_verbose:
- print(' - tag = {}'.format(tag))
- if arch == 'x86_64':
- cp = subprocess.run(['docker', 'build', '--no-cache', '--pull',
- '--file', dockerfile, '--tag', tag, '.'],
- capture_output=not(g_verbose))
+ print(" - tag = {}".format(tag))
+ if arch == "x86_64":
+ cp = subprocess.run(
+ ["docker", "build", "--no-cache", "--pull", "--file", dockerfile, "--tag", tag, "."],
+ capture_output=not (g_verbose),
+ )
# not very subtle
- elif arch == 'aarch64':
- cp = subprocess.run(['docker', 'build', '--platform', 'linux/arm64/v8',
- '--no-cache', '--pull', '--file', dockerfile,
- '--tag', tag, '.'],
- capture_output=not(g_verbose))
+ elif arch == "aarch64":
+ cp = subprocess.run(
+ [
+ "docker",
+ "build",
+ "--platform",
+ "linux/arm64/v8",
+ "--no-cache",
+ "--pull",
+ "--file",
+ dockerfile,
+ "--tag",
+ tag,
+ ".",
+ ],
+ capture_output=not (g_verbose),
+ )
# FIXME write failed output to log
if cp.returncode != 0:
- print('Error building {}: {}'.format(tag, repr(cp.returncode)))
- return ( tag, cp.returncode )
- return ( tag, cp.returncode )
+ print("Error building {}: {}".format(tag, repr(cp.returncode)))
+ return (tag, cp.returncode)
+ return (tag, cp.returncode)
-def run (tag, arch='x86_64'):
+def run(tag, arch="x86_64"):
if g_run_output:
capture_run_output = False
else:
- capture_run_output = not(g_verbose)
- print('Running Docker container tagged {}...'.format(tag))
- if arch == 'x86_64':
- cp = subprocess.run(['docker', 'run', tag],
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ capture_run_output = not (g_verbose)
+ print("Running Docker container tagged {}...".format(tag))
+ if arch == "x86_64":
+ cp = subprocess.run(["docker", "run", tag], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# not very subtle
- elif arch == 'aarch64':
- cp = subprocess.run(['docker', 'run', '--platform', 'linux/arm64/v8',
- tag],
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- _version = re.search(r'(PowerDNS Authoritative Server|PowerDNS Recursor|' +
- r'dnsdist) (\d+\.\d+\.\d+(-\w+)?[^ ]*)',
- cp.stdout.decode())
- _features = re.search(r'[Ff]eatures: (.*)', cp.stdout.decode())
+ elif arch == "aarch64":
+ cp = subprocess.run(
+ ["docker", "run", "--platform", "linux/arm64/v8", tag], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+ )
+ _version = re.search(
+ r"(PowerDNS Authoritative Server|PowerDNS Recursor|" + r"dnsdist) (\d+\.\d+\.\d+(-\w+)?[^ ]*)",
+ cp.stdout.decode(),
+ )
+ _features = re.search(r"[Ff]eatures: (.*)", cp.stdout.decode())
version = features = None
if _version:
# for some reason 99 is returned on `cmd --version` :shrug:
if cp.returncode != 0 and cp.returncode != 99:
# FIXME write failed output to log
- print('Error running {}: {}'.format(tag, repr(cp.returncode)))
+ print("Error running {}: {}".format(tag, repr(cp.returncode)))
return cp.returncode, version, features
-def collect_dockerfiles (release):
+def collect_dockerfiles(release):
if g_verbose:
- print('Collecting release files for {}...'.format(release))
- p = Path('.')
- files = list(p.glob('{}{}.*'.format(g_dockerfile, release)))
+ print("Collecting release files for {}...".format(release))
+ p = Path(".")
+ files = list(p.glob("{}{}.*".format(g_dockerfile, release)))
if g_verbose:
for file in files:
- print(' - {}'.format(file))
+ print(" - {}".format(file))
return files
-def test_release (release, arch='x86_64'):
+def test_release(release, arch="x86_64"):
# sorted because we want determinism
dockerfiles = sorted(collect_dockerfiles(release))
failed_builds = []
failed_runs = []
returned_versions = []
- print('=== testing {} ({}) ==='.format(release, arch))
+ print("=== testing {} ({}) ===".format(release, arch))
for df in dockerfiles:
- if arch == 'aarch64' and not release in ['auth-50', 'auth-49',
- 'rec-49', 'rec-50', 'rec-51', 'rec-52', 'rec-53', 'rec-54', 'rec-master',
- 'dnsdist-19', 'dnsdist-20', 'dnsdist-21', 'dnsdist-master']:
+ if arch == "aarch64" and not release in [
+ "auth-50",
+ "auth-49",
+ "rec-49",
+ "rec-50",
+ "rec-51",
+ "rec-52",
+ "rec-53",
+ "rec-54",
+ "rec-master",
+ "dnsdist-19",
+ "dnsdist-20",
+ "dnsdist-21",
+ "dnsdist-master",
+ ]:
continue
if g_verbose:
- print('--- {} ---'.format(df))
+ print("--- {} ---".format(df))
(tag, returncode) = build(df, arch)
if returncode != 0:
- print('Skipping running {} due to build error: {}'
- .format(df, returncode))
+ print("Skipping running {} due to build error: {}".format(df, returncode))
failed_builds.append((str(df), returncode))
elif tag is None:
- print('Skipping running {} due to undetermined tag.'.format(df))
+ print("Skipping running {} due to undetermined tag.".format(df))
failed_builds.append((str(df), returncode))
else:
(returncode, return_version, return_features) = run(tag, arch)
failed_runs.append((tag, returncode))
if return_version:
returned_versions.append((tag, return_version, return_features))
- print('Test done.')
+ print("Test done.")
if len(failed_builds) > 0:
- print('- failed builds:')
+ print("- failed builds:")
for fb in failed_builds:
- print(' - {}'.format(fb))
+ print(" - {}".format(fb))
if len(failed_runs) > 0:
- print('- failed runs:')
+ print("- failed runs:")
for fr in failed_runs:
- print(' - {}'.format(fr))
+ print(" - {}".format(fr))
if len(returned_versions) > 0:
- print('- returned versions:')
+ print("- returned versions:")
for rv in returned_versions:
- print(' - {}: {} ({})'.format(rv[0], rv[1], rv[2]))
+ print(" - {}: {} ({})".format(rv[0], rv[1], rv[2]))
else:
- print('- ERROR: no returned versions (unsupported product?)')
+ print("- ERROR: no returned versions (unsupported product?)")
# Main Program
args = parser.parse_args()
if args.version:
- print('generate-repo-files v' + g_version)
+ print("generate-repo-files v" + g_version)
sys.exit(0)
if args.verbose:
test_release(args.release)
if args.test_aarch64:
- test_release(args.release, 'aarch64')
+ test_release(args.release, "aarch64")
import re
import sys
-REGEX = re.compile(r'(?s)[a-z0-9][a-z0-9_]+_SOURCES ?= ?\\.*?^$', re.MULTILINE)
+REGEX = re.compile(r"(?s)[a-z0-9][a-z0-9_]+_SOURCES ?= ?\\.*?^$", re.MULTILINE)
def test_sources(fname) -> int:
ret = 0
for match in matches:
lines = match.split(" \\\n\t")
- elem = lines[0].rstrip(' =')
+ elem = lines[0].rstrip(" =")
lines = lines[1:]
sorted_lines = sorted(lines)
if sorted_lines != lines:
ret = 1
- print(f'Source files for {elem} in {fname} is not sorted properly'
- .format(elem=elem, fname=fname))
+ print(f"Source files for {elem} in {fname} is not sorted properly".format(elem=elem, fname=fname))
return ret
This script uses dnf to generate a Software Bill of Materials
(SBOM) in CycloneDX JSON format.
"""
+
import datetime
import json
import os
import dnf
+
def licenseToSPDXIdentifier(licenseName):
licenseMap = {
- 'BSD': 'BSD-3-Clause',
- 'GPLv2': 'GPL-2.0-only',
- 'GPLv2+': 'GPL-2.0-or-later',
- 'LGPLv2+': 'LGPL-2.0-or-later',
- 'MIT': 'MIT',
- 'OpenLDAP': 'OLDAP-2.8',
- }
+ "BSD": "BSD-3-Clause",
+ "GPLv2": "GPL-2.0-only",
+ "GPLv2+": "GPL-2.0-or-later",
+ "LGPLv2+": "LGPL-2.0-or-later",
+ "MIT": "MIT",
+ "OpenLDAP": "OLDAP-2.8",
+ }
if licenseName in licenseMap:
return licenseMap[licenseName]
return None
+
def getPackageDatabase():
with dnf.Base() as db:
conf = db.conf
- conf.installroot = '/'
- conf.substitutions.update_from_etc('/')
+ conf.installroot = "/"
+ conf.substitutions.update_from_etc("/")
db.read_all_repos()
- db.fill_sack(load_system_repo='auto', load_available_repos=True)
+ db.fill_sack(load_system_repo="auto", load_available_repos=True)
query = db.sack.query()
return query.installed()
+
def getPURL(pkg):
# from https://github.com/package-url/purl-spec/blob/main/types-doc/rpm-definition.md
# pkg:rpm/<namespace>/<name>@<version>?<qualifiers>#<subpath>
name = pkg.name
version = pkg.version
- if hasattr(pkg, 'cargo'):
- return f'pkg:cargo/{name}@{version}'
+ if hasattr(pkg, "cargo"):
+ return f"pkg:cargo/{name}@{version}"
vendor = pkg.vendor.lower()
- if vendor == 'oracle america':
- vendor = 'oracle'
- elif vendor == 'rocky enterprise software foundation':
- vendor = 'rocky'
- elif vendor == 'fedora project':
- vendor = 'fedora'
+ if vendor == "oracle america":
+ vendor = "oracle"
+ elif vendor == "rocky enterprise software foundation":
+ vendor = "rocky"
+ elif vendor == "fedora project":
+ vendor = "fedora"
if pkg.release:
- version += '-' + pkg.release
- qualifiers = ''
- if hasattr(pkg, 'arch'):
+ version += "-" + pkg.release
+ qualifiers = ""
+ if hasattr(pkg, "arch"):
if len(qualifiers) != 0:
- qualifiers += '&'
- qualifiers += 'arch=' + pkg.arch
+ qualifiers += "&"
+ qualifiers += "arch=" + pkg.arch
if pkg.epoch != 0:
if len(qualifiers) != 0:
- qualifiers += '&'
- qualifiers += 'epoch=' + str(pkg.epoch)
- return f'pkg:rpm/{vendor}/{name}@{version}?{qualifiers}'
+ qualifiers += "&"
+ qualifiers += "epoch=" + str(pkg.epoch)
+ return f"pkg:rpm/{vendor}/{name}@{version}?{qualifiers}"
+
def getPackageInformations(pkgDB, packageName):
matches = pkgDB.filter(name=packageName).run()
if len(matches) == 0:
- print(f'-> Package {packageName} not found')
+ print(f"-> Package {packageName} not found")
return None
return matches[0]
+
def getPackageVersion(pkg):
if pkg.release:
- version = (pkg.version if pkg.epoch == 0 else str(pkg.epoch) + ':' + pkg.version) + '-' + pkg.release
+ version = (pkg.version if pkg.epoch == 0 else str(pkg.epoch) + ":" + pkg.version) + "-" + pkg.release
else:
- version = (pkg.version if pkg.epoch == 0 else str(pkg.epoch) + ':' + pkg.version)
- if hasattr(pkg, 'arch'):
- version += '.' + pkg.arch
+ version = pkg.version if pkg.epoch == 0 else str(pkg.epoch) + ":" + pkg.version
+ if hasattr(pkg, "arch"):
+ version += "." + pkg.arch
return version
+
def getLibraryBOMReference(pkg):
- return 'lib:' + pkg.name + '_' + getPackageVersion(pkg)
+ return "lib:" + pkg.name + "_" + getPackageVersion(pkg)
+
def addDependencyToSBOM(sbom, pkg, seen_deps):
version = getPackageVersion(pkg)
seen_deps[bom_ref] = True
- component = { 'name': pkg.name, 'bom-ref': bom_ref, 'type': 'library', 'version': version}
-
- if hasattr(pkg, 'vendor') and pkg.vendor is not None:
- component['supplier'] = {'name': pkg.vendor}
- if hasattr(pkg, 'publisher') and pkg.publisher is not None:
- component['publisher'] = pkg.publisher
- if hasattr(pkg, 'author') and pkg.author is not None:
- component['author'] = pkg.author
- if hasattr(pkg, 'purl'):
- component['purl'] = pkg.purl
- if hasattr(pkg, 'externalReferences'):
- component['externalReferences'] = pkg.externalReferences
+ component = {"name": pkg.name, "bom-ref": bom_ref, "type": "library", "version": version}
+
+ if hasattr(pkg, "vendor") and pkg.vendor is not None:
+ component["supplier"] = {"name": pkg.vendor}
+ if hasattr(pkg, "publisher") and pkg.publisher is not None:
+ component["publisher"] = pkg.publisher
+ if hasattr(pkg, "author") and pkg.author is not None:
+ component["author"] = pkg.author
+ if hasattr(pkg, "purl"):
+ component["purl"] = pkg.purl
+ if hasattr(pkg, "externalReferences"):
+ component["externalReferences"] = pkg.externalReferences
spdx_license = licenseToSPDXIdentifier(pkg.license)
if spdx_license is None:
- component['licenses'] = [{'license': {'name': pkg.license}}]
+ component["licenses"] = [{"license": {"name": pkg.license}}]
else:
- component['licenses'] = [{'license': {'id': spdx_license}}]
- if hasattr(pkg, 'sha256') and pkg.sha256 is not None:
- component['hashes'] = [{'alg': 'SHA-256', 'content': pkg.sha256}]
- component['purl'] = getPURL(pkg)
+ component["licenses"] = [{"license": {"id": spdx_license}}]
+ if hasattr(pkg, "sha256") and pkg.sha256 is not None:
+ component["hashes"] = [{"alg": "SHA-256", "content": pkg.sha256}]
+ component["purl"] = getPURL(pkg)
- sbom['components'].append(component)
+ sbom["components"].append(component)
return True
+
def processDependencies(pkg_db, sbom, appInfos, depRelations, seen_deps):
for require in appInfos.requires:
- if hasattr(require, 'name'):
- depName = require.name.split('(')[0]
+ if hasattr(require, "name"):
+ depName = require.name.split("(")[0]
depSpec = require.name
else:
# hawkey.Reldep, el-8
- depName = str(require).split('(', maxsplit=1)[0]
+ depName = str(require).split("(", maxsplit=1)[0]
depSpec = require
- if depName in ['/bin/sh', 'config', 'ld-linux-x86-64.so.2', 'rpmlib', 'rtld']:
+ if depName in ["/bin/sh", "config", "ld-linux-x86-64.so.2", "rpmlib", "rtld"]:
continue
if depName in seen_deps:
continue
flags = []
matches = pkg_db.filter(*flags, provides__glob=[depSpec]).run()
if len(matches) == 0:
- print(f'Unable to find a match for {depName}')
+ print(f"Unable to find a match for {depName}")
continue
if len(matches) > 1:
- print(f'Got {len(matches)} matches for {depName}')
+ print(f"Got {len(matches)} matches for {depName}")
dep = matches[0]
depRef = getLibraryBOMReference(dep)
if addDependencyToSBOM(sbom, dep, seen_deps):
- depRelations['pkg:' + appInfos.name].append(depRef)
+ depRelations["pkg:" + appInfos.name].append(depRef)
+
class StaticLibDep:
def __init__(self, name, version, description, purl, external_refs, author, license_, sha256):
self.sha256 = sha256
self.cargo = True
+
def mergeLibSBOM(sbom, appInfos, lib_sbom_path, depRelations, seen_deps):
with open(lib_sbom_path, encoding="utf-8") as fd:
lib_sbom_data = json.load(fd)
- component = lib_sbom_data['metadata']['component']
- main_component_name = component['name']
- pkg = StaticLibDep(main_component_name, component['version'], component['description'], component.get('purl'), component.get('externalReferences') or [], component.get('author') or None, component['licenses'][0]['expression'], component['hashes'][0]['content'] if 'hashes' in component else None)
+ component = lib_sbom_data["metadata"]["component"]
+ main_component_name = component["name"]
+ pkg = StaticLibDep(
+ main_component_name,
+ component["version"],
+ component["description"],
+ component.get("purl"),
+ component.get("externalReferences") or [],
+ component.get("author") or None,
+ component["licenses"][0]["expression"],
+ component["hashes"][0]["content"] if "hashes" in component else None,
+ )
addDependencyToSBOM(sbom, pkg, seen_deps)
- depRef = 'lib:' + pkg.name
- depRelations['pkg:' + appInfos.name].append(depRef)
+ depRef = "lib:" + pkg.name
+ depRelations["pkg:" + appInfos.name].append(depRef)
- sub_components = lib_sbom_data['components']
+ sub_components = lib_sbom_data["components"]
for component in sub_components:
- pkg = StaticLibDep(component['name'], component['version'], None, component.get('purl'), component.get('externalReferences') or [], component.get('author') or None, component['licenses'][0]['expression'], component['hashes'][0]['content'] if 'hashes' in component else None)
+ pkg = StaticLibDep(
+ component["name"],
+ component["version"],
+ None,
+ component.get("purl"),
+ component.get("externalReferences") or [],
+ component.get("author") or None,
+ component["licenses"][0]["expression"],
+ component["hashes"][0]["content"] if "hashes" in component else None,
+ )
addDependencyToSBOM(sbom, pkg, seen_deps)
depRef = getLibraryBOMReference(pkg)
- if not 'lib:' + main_component_name in depRelations:
- depRelations['lib:' + main_component_name] = []
- depRelations['lib:' + main_component_name].append(depRef)
+ if not "lib:" + main_component_name in depRelations:
+ depRelations["lib:" + main_component_name] = []
+ depRelations["lib:" + main_component_name].append(depRef)
+
def addAdditionalLibraryToSBOM(depFile, sbom, appInfos, depRelations, seen_deps):
with open(depFile, encoding="utf-8") as depDataFile:
depData = json.load(depDataFile)
- pkg = StaticLibDep(os.path.splitext(os.path.basename(depFile))[0], depData['version'], None, None, [], None, depData.get('license') or None, None)
- pkg.supplier = 'PowerDNS.COM BV'
- if 'publisher' in depData:
- pkg.publisher = depData['publisher']
- if 'SHA256SUM' in depData:
- pkg.sha256 = depData['SHA256SUM']
- elif 'SHA256SUM_x86_64' in depData:
- pkg.sha256 = depData['SHA256SUM_x86_64']
- if 'cargo-based' in depData:
- pkg.cargo = depData['cargo-based']
-
- depRef = 'lib:' + pkg.name
+ pkg = StaticLibDep(
+ os.path.splitext(os.path.basename(depFile))[0],
+ depData["version"],
+ None,
+ None,
+ [],
+ None,
+ depData.get("license") or None,
+ None,
+ )
+ pkg.supplier = "PowerDNS.COM BV"
+ if "publisher" in depData:
+ pkg.publisher = depData["publisher"]
+ if "SHA256SUM" in depData:
+ pkg.sha256 = depData["SHA256SUM"]
+ elif "SHA256SUM_x86_64" in depData:
+ pkg.sha256 = depData["SHA256SUM_x86_64"]
+ if "cargo-based" in depData:
+ pkg.cargo = depData["cargo-based"]
+
+ depRef = "lib:" + pkg.name
addDependencyToSBOM(sbom, pkg, seen_deps)
- depRelations['pkg:' + appInfos.name].append(depRef)
+ depRelations["pkg:" + appInfos.name].append(depRef)
+
def processAdditionalDependencies(sbom, appInfos, additionalDeps, depRelations, seen_deps):
for additionalDepFile in additionalDeps:
- if additionalDepFile.endswith('cdx.json'):
+ if additionalDepFile.endswith("cdx.json"):
mergeLibSBOM(sbom, appInfos, additionalDepFile, depRelations, seen_deps)
else:
addAdditionalLibraryToSBOM(additionalDepFile, sbom, appInfos, depRelations, seen_deps)
+
def generateSBOM(packageName, additionalDeps):
- sbom = { 'bomFormat': 'CycloneDX', 'specVersion': '1.5', 'version': 1 }
- sbom['serialNumber'] = 'urn:uuid:' + str(uuid.uuid4())
+ sbom = {"bomFormat": "CycloneDX", "specVersion": "1.5", "version": 1}
+ sbom["serialNumber"] = "urn:uuid:" + str(uuid.uuid4())
depRelations = {}
pkg_db = getPackageDatabase()
appName = packageName
appInfos = getPackageInformations(pkg_db, packageName)
- component = { 'name': appName, 'bom-ref': 'pkg:' + appName, 'type': 'application'}
+ component = {"name": appName, "bom-ref": "pkg:" + appName, "type": "application"}
version = appInfos.version
- qualifiers = ''
+ qualifiers = ""
if appInfos.release:
- version += '-' + appInfos.release
+ version += "-" + appInfos.release
version_without_epoch_or_arch = version
- if hasattr(appInfos, 'arch'):
- version += '.' + appInfos.arch
+ if hasattr(appInfos, "arch"):
+ version += "." + appInfos.arch
if len(qualifiers) != 0:
- qualifiers += '&'
- qualifiers += 'arch=' + appInfos.arch
+ qualifiers += "&"
+ qualifiers += "arch=" + appInfos.arch
if appInfos.epoch != 0:
- version = str(appInfos.epoch) + ':' + version
+ version = str(appInfos.epoch) + ":" + version
if len(qualifiers) != 0:
- qualifiers += '&'
- qualifiers += 'epoch=' + str(appInfos.epoch)
+ qualifiers += "&"
+ qualifiers += "epoch=" + str(appInfos.epoch)
- component['version'] = version
+ component["version"] = version
- component['supplier'] = {'name': appInfos.vendor if appInfos.vendor != '<NULL>' else 'PowerDNS.COM BV', 'url': ['https://www.powerdns.com']}
- component['licenses'] = [{'license': {'id': licenseToSPDXIdentifier(appInfos.license)}}]
- component['purl'] = f'pkg:rpm/powerdns/{appName}@{version_without_epoch_or_arch}?{qualifiers}'
+ component["supplier"] = {
+ "name": appInfos.vendor if appInfos.vendor != "<NULL>" else "PowerDNS.COM BV",
+ "url": ["https://www.powerdns.com"],
+ }
+ component["licenses"] = [{"license": {"id": licenseToSPDXIdentifier(appInfos.license)}}]
+ component["purl"] = f"pkg:rpm/powerdns/{appName}@{version_without_epoch_or_arch}?{qualifiers}"
- depRelations['pkg:' + appName] = []
+ depRelations["pkg:" + appName] = []
- sbom['metadata'] = { 'timestamp': datetime.datetime.now(tz=datetime.timezone.utc).isoformat(),
- 'authors': [{'name': 'PowerDNS.COM BV'}],
- 'component': component }
- sbom['components'] = []
- sbom['dependencies'] = []
+ sbom["metadata"] = {
+ "timestamp": datetime.datetime.now(tz=datetime.timezone.utc).isoformat(),
+ "authors": [{"name": "PowerDNS.COM BV"}],
+ "component": component,
+ }
+ sbom["components"] = []
+ sbom["dependencies"] = []
seen_deps = {}
processDependencies(pkg_db, sbom, appInfos, depRelations, seen_deps)
processAdditionalDependencies(sbom, appInfos, additionalDeps, depRelations, seen_deps)
for pkg, deps in depRelations.items():
- sbom['dependencies'].append({'ref': pkg, 'dependsOn': deps})
+ sbom["dependencies"].append({"ref": pkg, "dependsOn": deps})
return sbom
+
if __name__ == "__main__":
if len(sys.argv) < 3:
- sys.exit(f'Usage: {sys.argv[0]} <output file> <package> [static dependencies ...]')
+ sys.exit(f"Usage: {sys.argv[0]} <output file> <package> [static dependencies ...]")
staticDeps = []
if len(sys.argv) > 3:
import tempfile
import re
+
def main():
if len(sys.argv) != 4:
- print(f'Usage: {sys.argv[0]} <path/to/Cargo.toml> <package name> <version to set>')
+ print(f"Usage: {sys.argv[0]} <path/to/Cargo.toml> <package name> <version to set>")
sys.exit(1)
file_name = sys.argv[1]
version = sys.argv[3]
# convert the version so that it conforms to Rust rules: x.x.x-whatever
- version = re.sub(r'([0-9]+\.[0-9]+\.[0-9]+)\.', r'\1-', version)
- with tempfile.NamedTemporaryFile(mode='w+t', encoding='utf-8', delete=False) as generated_fp:
- with open(file_name, 'r', encoding='utf-8') as cargo_file:
+ version = re.sub(r"([0-9]+\.[0-9]+\.[0-9]+)\.", r"\1-", version)
+ with tempfile.NamedTemporaryFile(mode="w+t", encoding="utf-8", delete=False) as generated_fp:
+ with open(file_name, "r", encoding="utf-8") as cargo_file:
in_rust_package_section = False
for line in cargo_file:
- if line.startswith('['):
+ if line.startswith("["):
in_rust_package_section = False
elif line == f'name = "{package_name}"\n':
in_rust_package_section = True
elif in_rust_package_section and line.startswith("version ="):
- generated_fp.write(f"version = \"{version}\"\n")
+ generated_fp.write(f'version = "{version}"\n')
continue
generated_fp.write(line)
shutil.move(generated_fp.name, file_name)
-if __name__ == '__main__':
+
+if __name__ == "__main__":
main()
import struct
import sys
+
def readRecord(fp, withTimestamp):
if withTimestamp:
return False
queryID = struct.unpack("!H", data)[0]
- qname = ''
+ qname = ""
while True:
labelLen = struct.unpack("B", fp.read(1))[0]
if labelLen == 0:
break
label = fp.read(labelLen)
- if qname != '':
- qname = qname + '.'
+ if qname != "":
+ qname = qname + "."
qname = qname + label.decode()
qtype = struct.unpack("H", fp.read(2))[0]
elif addrType == socket.AF_INET6:
addr = socket.inet_ntop(socket.AF_INET6, fp.read(16))
else:
- print('Unsupported address type %d, skipping this record' % (int(addrType)))
+ print("Unsupported address type %d, skipping this record" % (int(addrType)))
return False
port = struct.unpack("!H", fp.read(2))[0]
if withTimestamp:
- print('[%u.%u] Packet from %s:%d for %s %s with id %d' % (tv_sec, tv_nsec, addr, port, qname, qtype, queryID))
+ print("[%u.%u] Packet from %s:%d for %s %s with id %d" % (tv_sec, tv_nsec, addr, port, qname, qtype, queryID))
else:
- print('Packet from %s:%d for %s %s with id %d' % (addr, port, qname, qtype, queryID))
+ print("Packet from %s:%d for %s %s with id %d" % (addr, port, qname, qtype, queryID))
return True
+
def readLogFile(filename, withTimestamps):
- with open(filename, mode='rb') as fp:
+ with open(filename, mode="rb") as fp:
while True:
if not readRecord(fp, withTimestamps):
break
+
if __name__ == "__main__":
- if len(sys.argv) != 2 and (len(sys.argv) != 3 or sys.argv[2] != 'with-timestamps'):
- sys.exit('Usage: %s <path to log file> [with-timestamps]' % (sys.argv[0]))
+ if len(sys.argv) != 2 and (len(sys.argv) != 3 or sys.argv[2] != "with-timestamps"):
+ sys.exit("Usage: %s <path to log file> [with-timestamps]" % (sys.argv[0]))
readLogFile(sys.argv[1], len(sys.argv) == 3)
try:
import google.protobuf.json_format
import opentelemetry.proto.trace.v1.trace_pb2
+
opentelemetryAvailable = True
except Exception:
opentelemetryAvailable = False
class PDNSPBConnHandler(object):
-
def __init__(self, conn, oturl, printjson):
self._conn = conn
self._oturl = oturl
self._printjson = printjson
messageTypeToStringMap = {
- dnsmessage_pb2.PBDNSMessage.UNKNOWN: 'Unknown',
- dnsmessage_pb2.PBDNSMessage.QNAME: 'QName',
- dnsmessage_pb2.PBDNSMessage.CLIENTIP: 'Client IP',
- dnsmessage_pb2.PBDNSMessage.RESPONSEIP: 'Response IP',
- dnsmessage_pb2.PBDNSMessage.NSDNAME: 'NS DName',
- dnsmessage_pb2.PBDNSMessage.NSIP: 'NS IP',
+ dnsmessage_pb2.PBDNSMessage.UNKNOWN: "Unknown",
+ dnsmessage_pb2.PBDNSMessage.QNAME: "QName",
+ dnsmessage_pb2.PBDNSMessage.CLIENTIP: "Client IP",
+ dnsmessage_pb2.PBDNSMessage.RESPONSEIP: "Response IP",
+ dnsmessage_pb2.PBDNSMessage.NSDNAME: "NS DName",
+ dnsmessage_pb2.PBDNSMessage.NSIP: "NS IP",
}
def run(self):
break
(datalen,) = struct.unpack("!H", data)
- data = b''
+ data = b""
remaining = datalen
while remaining > 0:
msg = dnsmessage_pb2.PBDNSMessage()
try:
msg.ParseFromString(data)
- if opentelemetryAvailable and self._oturl is not None and msg.HasField('openTelemetryData'):
+ if opentelemetryAvailable and self._oturl is not None and msg.HasField("openTelemetryData"):
self.postOT(msg.openTelemetryData)
if msg.type == dnsmessage_pb2.PBDNSMessage.DNSQueryType:
self.printQueryMessage(msg)
elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSIncomingResponseType:
self.printIncomingResponseMessage(msg)
else:
- print('Discarding unsupported message type %d' % (msg.type))
+ print("Discarding unsupported message type %d" % (msg.type))
except google.protobuf.message.DecodeError as exp:
- print('Error parsing message of size %d: %s' % (datalen, str(exp)))
+ print("Error parsing message of size %d: %s" % (datalen, str(exp)))
break
self._conn.close()
def postOT(self, msg):
print("- Posting OTData to " + self._oturl)
- headers = {'Content-Type': 'application/x-protobuf'}
- answer = requests.post(self._oturl, data = msg, headers = headers)
- print(' - ' + str(answer))
+ headers = {"Content-Type": "application/x-protobuf"}
+ answer = requests.post(self._oturl, data=msg, headers=headers)
+ print(" - " + str(answer))
def printQueryMessage(self, message):
- self.printSummary(message, 'Query')
+ self.printSummary(message, "Query")
self.printQuery(message)
self.printOT(message)
def printOutgoingQueryMessage(self, message):
- self.printSummary(message, 'Query (O)')
+ self.printSummary(message, "Query (O)")
self.printQuery(message)
self.printOT(message)
def printResponseMessage(self, message):
- self.printSummary(message, 'Response')
+ self.printSummary(message, "Response")
self.printQuery(message)
self.printResponse(message)
self.printOT(message)
def printIncomingResponseMessage(self, message):
- self.printSummary(message, 'Response (I)')
+ self.printSummary(message, "Response (I)")
self.printQuery(message)
self.printResponse(message)
self.printOT(message)
def printQuery(self, message):
- if message.HasField('question'):
+ if message.HasField("question"):
qclass = 1
- if message.question.HasField('qClass'):
+ if message.question.HasField("qClass"):
qclass = message.question.qClass
- print("- Question: %d, %d, %s" % (qclass,
- message.question.qType,
- message.question.qName))
+ print("- Question: %d, %d, %s" % (qclass, message.question.qType, message.question.qName))
def convertKV(self, parent, key, value):
if isinstance(value, dict):
for key1, value1 in enumerate(value):
self.convertKV(value, key1, value1)
else:
- if key in ('trace_id', 'span_id', 'parent_span_id'):
+ if key in ("trace_id", "span_id", "parent_span_id"):
parent[key] = binascii.hexlify(base64.b64decode(value)).decode("utf-8")
def convertIDs(self, values):
def printOT(self, msg):
if self._printjson:
if opentelemetryAvailable:
- if msg.HasField('openTelemetryData'):
+ if msg.HasField("openTelemetryData"):
json_string = None
otmsg = opentelemetry.proto.trace.v1.trace_pb2.TracesData()
otmsg.ParseFromString(msg.openTelemetryData)
json_string = json.dumps(values, indent=True)
print("- openTelemetry: " + json_string)
else:
- print("- openTelemetry decoding not available, see the comments in ProtoBuffer.py to make it available.")
+ print(
+ "- openTelemetry decoding not available, see the comments in ProtoBuffer.py to make it available."
+ )
@staticmethod
def getAppliedPolicyTypeAsString(polType):
@staticmethod
def getEventAsString(event):
- descr = dnsmessage_pb2.PBDNSMessage.DESCRIPTOR
- return descr.EnumValueName('EventType', event);
+ descr = dnsmessage_pb2.PBDNSMessage.DESCRIPTOR
+ return descr.EnumValueName("EventType", event)
@staticmethod
def getTransportAsString(transport):
- descr = dnsmessage_pb2.PBDNSMessage.DESCRIPTOR
- return descr.EnumValueName('SocketProtocol', transport);
+ descr = dnsmessage_pb2.PBDNSMessage.DESCRIPTOR
+ return descr.EnumValueName("SocketProtocol", transport)
def printResponse(self, message):
if message.trace:
print("- Event Trace:")
for event in message.trace:
ev = self.getEventAsString(event.event)
- if event.event == dnsmessage_pb2.PBDNSMessage.CustomEvent and event.HasField('custom'):
+ if event.event == dnsmessage_pb2.PBDNSMessage.CustomEvent and event.HasField("custom"):
ev += ":" + event.custom
- ev += '(' + str(event.ts)
- valstr = ''
- if event.HasField('boolVal'):
- valstr = str(event.boolVal)
- elif event.HasField('intVal'):
- valstr = str(event.intVal)
- elif event.HasField('stringVal'):
- valstr = event.stringVal
- elif event.HasField('bytesVal'):
- valstr = binascii.hexlify(event.bytesVal)
+ ev += "(" + str(event.ts)
+ valstr = ""
+ if event.HasField("boolVal"):
+ valstr = str(event.boolVal)
+ elif event.HasField("intVal"):
+ valstr = str(event.intVal)
+ elif event.HasField("stringVal"):
+ valstr = event.stringVal
+ elif event.HasField("bytesVal"):
+ valstr = binascii.hexlify(event.bytesVal)
if len(valstr) > 0:
- valstr = ',' + valstr
+ valstr = "," + valstr
if not event.start:
- startstr = ',done'
+ startstr = ",done"
else:
- startstr = ''
+ startstr = ""
print("\t- %s%s%s)" % (ev, valstr, startstr))
- if message.HasField('response'):
+ if message.HasField("response"):
response = message.response
- if response.HasField('queryTimeSec'):
- datestr = datetime.datetime.fromtimestamp(response.queryTimeSec).strftime('%Y-%m-%d %H:%M:%S')
- if response.HasField('queryTimeUsec'):
- datestr = datestr + '.' + ("%06d" % response.queryTimeUsec)
+ if response.HasField("queryTimeSec"):
+ datestr = datetime.datetime.fromtimestamp(response.queryTimeSec).strftime("%Y-%m-%d %H:%M:%S")
+ if response.HasField("queryTimeUsec"):
+ datestr = datestr + "." + ("%06d" % response.queryTimeUsec)
print("- Query time: %s" % (datestr))
- policystr = ''
- if response.HasField('appliedPolicy') and response.appliedPolicy:
- policystr = ', Applied policy: ' + response.appliedPolicy
- if response.HasField('appliedPolicyType'):
- policystr = policystr + ' (' + self.getAppliedPolicyTypeAsString(response.appliedPolicyType) + ')'
- if response.HasField('appliedPolicyTrigger'):
- policystr = policystr + ', Trigger = ' + response.appliedPolicyTrigger
- if response.HasField('appliedPolicyHit'):
- policystr = policystr + ', Hit = ' + response.appliedPolicyHit
-
- tagsstr = ''
+ policystr = ""
+ if response.HasField("appliedPolicy") and response.appliedPolicy:
+ policystr = ", Applied policy: " + response.appliedPolicy
+ if response.HasField("appliedPolicyType"):
+ policystr = policystr + " (" + self.getAppliedPolicyTypeAsString(response.appliedPolicyType) + ")"
+ if response.HasField("appliedPolicyTrigger"):
+ policystr = policystr + ", Trigger = " + response.appliedPolicyTrigger
+ if response.HasField("appliedPolicyHit"):
+ policystr = policystr + ", Hit = " + response.appliedPolicyHit
+
+ tagsstr = ""
if response.tags:
- tagsstr = ', Tags: ' + ','.join(response.tags)
+ tagsstr = ", Tags: " + ",".join(response.tags)
rrscount = len(response.rrs)
- print("- Response Code: %d, RRs: %d%s%s" % (response.rcode,
- rrscount,
- policystr,
- tagsstr))
+ print("- Response Code: %d, RRs: %d%s%s" % (response.rcode, rrscount, policystr, tagsstr))
for rr in response.rrs:
rrclass = 1
- rdatastr = ''
- rrudr = 'N/A'
- if rr.HasField('class'):
- rrclass = getattr(rr, 'class')
+ rdatastr = ""
+ rrudr = "N/A"
+ if rr.HasField("class"):
+ rrclass = getattr(rr, "class")
rrtype = rr.type
- if rr.HasField('udr'):
+ if rr.HasField("udr"):
rrudr = str(rr.udr)
- if (rrclass == 1 or rrclass == 255) and rr.HasField('rdata'):
+ if (rrclass == 1 or rrclass == 255) and rr.HasField("rdata"):
if rrtype == 1:
rdatastr = socket.inet_ntop(socket.AF_INET, rr.rdata)
elif rrtype in (5, 35, 64, 65):
elif rrtype == 28:
rdatastr = socket.inet_ntop(socket.AF_INET6, rr.rdata)
- print("\t - %d, %d, %s, %d, %s, %s" % (rrclass,
- rrtype,
- rr.name,
- rr.ttl,
- rdatastr,
- rrudr))
+ print("\t - %d, %d, %s, %d, %s, %s" % (rrclass, rrtype, rr.name, rr.ttl, rdatastr, rrudr))
def printSummary(self, msg, typestr):
- datestr = datetime.datetime.fromtimestamp(msg.timeSec).strftime('%Y-%m-%d %H:%M:%S')
- if msg.HasField('timeUsec'):
- datestr = datestr + '.' + ("%06d" % msg.timeUsec)
- ipfromstr = 'N/A'
- iptostr = 'N/A'
- toportstr = ''
- fromportstr = ''
- fromvalue = getattr(msg, 'from')
+ datestr = datetime.datetime.fromtimestamp(msg.timeSec).strftime("%Y-%m-%d %H:%M:%S")
+ if msg.HasField("timeUsec"):
+ datestr = datestr + "." + ("%06d" % msg.timeUsec)
+ ipfromstr = "N/A"
+ iptostr = "N/A"
+ toportstr = ""
+ fromportstr = ""
+ fromvalue = getattr(msg, "from")
if msg.socketFamily == dnsmessage_pb2.PBDNSMessage.INET:
- if msg.HasField('from'):
+ if msg.HasField("from"):
ipfromstr = socket.inet_ntop(socket.AF_INET, fromvalue)
- if msg.HasField('to'):
+ if msg.HasField("to"):
iptostr = socket.inet_ntop(socket.AF_INET, msg.to)
else:
- if msg.HasField('from'):
- ipfromstr = '[' + socket.inet_ntop(socket.AF_INET6, fromvalue) + ']'
- if msg.HasField('to'):
- iptostr = '[' + socket.inet_ntop(socket.AF_INET6, msg.to) + ']'
+ if msg.HasField("from"):
+ ipfromstr = "[" + socket.inet_ntop(socket.AF_INET6, fromvalue) + "]"
+ if msg.HasField("to"):
+ iptostr = "[" + socket.inet_ntop(socket.AF_INET6, msg.to) + "]"
protostr = self.getTransportAsString(msg.socketProtocol)
- if msg.HasField('fromPort'):
- fromportstr = ':' + str(msg.fromPort) + ' '
+ if msg.HasField("fromPort"):
+ fromportstr = ":" + str(msg.fromPort) + " "
- if msg.HasField('toPort'):
- toportstr = ':' + str(msg.toPort) + ' '
+ if msg.HasField("toPort"):
+ toportstr = ":" + str(msg.toPort) + " "
messageidstr = binascii.hexlify(bytearray(msg.messageId))
- serveridstr = 'N/A'
- if msg.HasField('serverIdentity'):
+ serveridstr = "N/A"
+ if msg.HasField("serverIdentity"):
serveridstr = msg.serverIdentity
- initialrequestidstr = ''
- if msg.HasField('initialRequestId'):
- initialrequestidstr = ', initial uuid: %s ' % (binascii.hexlify(bytearray(msg.initialRequestId)))
+ initialrequestidstr = ""
+ if msg.HasField("initialRequestId"):
+ initialrequestidstr = ", initial uuid: %s " % (binascii.hexlify(bytearray(msg.initialRequestId)))
- requestorstr = '(N/A)'
+ requestorstr = "(N/A)"
requestor = self.getRequestorSubnet(msg)
if requestor:
- requestorstr = ' (' + requestor + ')'
+ requestorstr = " (" + requestor + ")"
- deviceId = 'N/A'
- if msg.HasField('deviceId'):
+ deviceId = "N/A"
+ if msg.HasField("deviceId"):
deviceId = binascii.hexlify(bytearray(msg.deviceId))
- deviceName = 'N/A'
- if msg.HasField('deviceName'):
+ deviceName = "N/A"
+ if msg.HasField("deviceName"):
deviceName = msg.deviceName
- requestorId = 'N/A'
- if msg.HasField('requestorId'):
+ requestorId = "N/A"
+ if msg.HasField("requestorId"):
requestorId = msg.requestorId
- nod = 'N/A';
- if msg.HasField('newlyObservedDomain'):
+ nod = "N/A"
+ if msg.HasField("newlyObservedDomain"):
nod = str(msg.newlyObservedDomain)
- workerId = 'N/A'
- if msg.HasField('workerId'):
- workerId = str(msg.workerId)
+ workerId = "N/A"
+ if msg.HasField("workerId"):
+ workerId = str(msg.workerId)
- pcCacheHit = 'N/A'
- if msg.HasField('packetCacheHit'):
- pcCacheHit = str(msg.packetCacheHit)
+ pcCacheHit = "N/A"
+ if msg.HasField("packetCacheHit"):
+ pcCacheHit = str(msg.packetCacheHit)
- outgoingQs = 'N/A'
- if msg.HasField('outgoingQueries'):
- outgoingQs = str(msg.outgoingQueries)
+ outgoingQs = "N/A"
+ if msg.HasField("outgoingQueries"):
+ outgoingQs = str(msg.outgoingQueries)
headerFlags = "N/A"
- if msg.HasField('headerFlags'):
+ if msg.HasField("headerFlags"):
headerFlags = "0x%04X" % socket.ntohs(msg.headerFlags)
ednsVersion = "N/A"
- if msg.HasField('ednsVersion'):
+ if msg.HasField("ednsVersion"):
ednsVersion = "0x%08X" % socket.ntohl(msg.ednsVersion)
ede = "N/A"
- if msg.HasField('ede'):
+ if msg.HasField("ede"):
ede = str(msg.ede)
edeText = "N/A"
- if msg.HasField('edeText'):
+ if msg.HasField("edeText"):
edeText = msg.edeText
openTelemetryDataLen = "N/A"
- if opentelemetryAvailable and msg.HasField('openTelemetryData'):
+ if opentelemetryAvailable and msg.HasField("openTelemetryData"):
openTelemetryDataLen = str(len(msg.openTelemetryData))
openTelemetryTraceID = "N/A"
- if msg.HasField('openTelemetryTraceID'):
+ if msg.HasField("openTelemetryTraceID"):
openTelemetryTraceID = binascii.hexlify(msg.openTelemetryTraceID)
- print('[%s] %s of size %d: %s%s%s -> %s%s(%s) id: %d uuid: %s%s '
- 'requestorid: %s deviceid: %s devicename: %s serverid: %s nod: %s workerId: %s pcCacheHit: %s outgoingQueries: %s headerFlags: %s ednsVersion: %s ede: %s edeText: %s otTraceID: %s openTelemetryData: len %s' %
- (datestr,
- typestr,
- msg.inBytes,
- ipfromstr,
- fromportstr,
- requestorstr,
- iptostr,
- toportstr,
- protostr,
- msg.id,
- messageidstr,
- initialrequestidstr,
- requestorId,
- deviceId,
- deviceName,
- serveridstr,
- nod,
- workerId,
- pcCacheHit,
- outgoingQs,
- headerFlags,
- ednsVersion,
- ede,
- edeText,
- openTelemetryTraceID,
- openTelemetryDataLen))
+ print(
+ "[%s] %s of size %d: %s%s%s -> %s%s(%s) id: %d uuid: %s%s "
+ "requestorid: %s deviceid: %s devicename: %s serverid: %s nod: %s workerId: %s pcCacheHit: %s outgoingQueries: %s headerFlags: %s ednsVersion: %s ede: %s edeText: %s otTraceID: %s openTelemetryData: len %s"
+ % (
+ datestr,
+ typestr,
+ msg.inBytes,
+ ipfromstr,
+ fromportstr,
+ requestorstr,
+ iptostr,
+ toportstr,
+ protostr,
+ msg.id,
+ messageidstr,
+ initialrequestidstr,
+ requestorId,
+ deviceId,
+ deviceName,
+ serveridstr,
+ nod,
+ workerId,
+ pcCacheHit,
+ outgoingQs,
+ headerFlags,
+ ednsVersion,
+ ede,
+ edeText,
+ openTelemetryTraceID,
+ openTelemetryDataLen,
+ )
+ )
for mt in msg.meta:
- values = ''
+ values = ""
for entry in mt.value.stringVal:
- values = ', '.join([values, entry]) if values != '' else entry
+ values = ", ".join([values, entry]) if values != "" else entry
for entry in mt.value.intVal:
- values = ', '.join([values, str(entry)]) if values != '' else str(entry)
+ values = ", ".join([values, str(entry)]) if values != "" else str(entry)
- print('- (meta) %s -> %s' % (mt.key, values))
+ print("- (meta) %s -> %s" % (mt.key, values))
def getRequestorSubnet(self, msg):
requestorstr = None
- if msg.HasField('originalRequestorSubnet'):
+ if msg.HasField("originalRequestorSubnet"):
if len(msg.originalRequestorSubnet) == 4:
- requestorstr = socket.inet_ntop(socket.AF_INET,
- msg.originalRequestorSubnet)
+ requestorstr = socket.inet_ntop(socket.AF_INET, msg.originalRequestorSubnet)
elif len(msg.originalRequestorSubnet) == 16:
- requestorstr = socket.inet_ntop(socket.AF_INET6,
- msg.originalRequestorSubnet)
+ requestorstr = socket.inet_ntop(socket.AF_INET6, msg.originalRequestorSubnet)
return requestorstr
-class PDNSPBListener(object):
+class PDNSPBListener(object):
def __init__(self, addr, port, oturl, printjson):
self._oturl = oturl
self._printjson = printjson
- res = socket.getaddrinfo(addr, port, socket.AF_UNSPEC,
- socket.SOCK_STREAM, 0,
- socket.AI_PASSIVE)
+ res = socket.getaddrinfo(addr, port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
if len(res) != 1:
print("Error parsing the supplied address")
sys.exit(1)
(conn, _) = self._sock.accept()
handler = PDNSPBConnHandler(conn, self._oturl, self._printjson)
- thread = threading.Thread(name='Connection Handler',
- target=PDNSPBConnHandler.run,
- args=[handler])
+ thread = threading.Thread(name="Connection Handler", target=PDNSPBConnHandler.run, args=[handler])
thread.daemon = True
thread.start()
if __name__ == "__main__":
parser = argparse.ArgumentParser(
- prog='ProtobufLogger',
- description='Listens for and prints dnsmessage.proto messages and optionally posts OT Trace data to a collector URL',
- epilog='URL is an optional url of a OpenTelemetry Trace collector endpoint'
+ prog="ProtobufLogger",
+ description="Listens for and prints dnsmessage.proto messages and optionally posts OT Trace data to a collector URL",
+ epilog="URL is an optional url of a OpenTelemetry Trace collector endpoint",
)
- parser.add_argument('-json', action='store_true')
- parser.add_argument('address')
- parser.add_argument('port')
- parser.add_argument('-url')
- args = parser.parse_args();
+ parser.add_argument("-json", action="store_true")
+ parser.add_argument("address")
+ parser.add_argument("port")
+ parser.add_argument("-url")
+ args = parser.parse_args()
PDNSPBListener(args.address, args.port, args.url, args.json).run()
sys.exit(0)
import dns
import unittest
+
class AssertEqualDNSMessageMixin(unittest.TestCase):
def assertEqualDNSMessage(self, first, second, msg=None):
if not first == second:
- a = str(first).split('\n')
- b = str(second).split('\n')
+ a = str(first).split("\n")
+ b = str(second).split("\n")
- diff = '\n'.join(
- difflib.unified_diff(
- a,
- b,
- fromfile='first',
- tofile='second',
- n=max(len(a), len(b)),
- lineterm=""
- )
+ diff = "\n".join(
+ difflib.unified_diff(a, b, fromfile="first", tofile="second", n=max(len(a), len(b)), lineterm="")
)
standardMsg = "%s != %s:\n%s" % (repr(first), repr(second), diff)
import netaddr
import socket
+
class DNSQuery(ct.Structure):
- _fields_ = [
- ("qname", ct.c_uint8 * 255),
- ("qtype", ct.c_uint16)
- ]
+ _fields_ = [("qname", ct.c_uint8 * 255), ("qtype", ct.c_uint16)]
+
class PacketInfo(ct.Structure):
- _fields_ = [
- ("ipv4_src", ct.c_uint32),
- ("ipv6_src", ct.c_uint8 * 16),
- ("query", DNSQuery)
- ]
+ _fields_ = [("ipv4_src", ct.c_uint32), ("ipv6_src", ct.c_uint8 * 16), ("query", DNSQuery)]
+
def decode_qname(qname_array):
qname = ""
else:
length = int(qname_byte)
if qname != "":
- qname += '.'
+ qname += "."
else:
qname += chr(int(qname_byte))
length -= 1
return qname
+
def print_event(cpu, data, size):
event = ct.cast(data, ct.POINTER(PacketInfo)).contents
if event.ipv4_src != 0:
src_ip = str(netaddr.IPAddress(socket.htonl(event.ipv4_src)))
else:
- src_ip = str(netaddr.IPAddress(sum([byte << 8*(15-index) for index, byte in enumerate(event.ipv6_src)]), 6))
+ src_ip = str(netaddr.IPAddress(sum([byte << 8 * (15 - index) for index, byte in enumerate(event.ipv6_src)]), 6))
qtype = INV_QTYPES[socket.htons(event.query.qtype)]
qname = decode_qname(event.query.qname)
print(f"{src_ip}|{qtype}|{qname}")
-QTYPES = {'LOC': 29, '*': 255, 'IXFR': 251, 'UINFO': 100, 'NSEC3': 50, 'AAAA': 28, 'CNAME': 5, 'MINFO': 14, 'EID': 31, 'GPOS': 27, 'X25': 19, 'HINFO': 13, 'CAA': 257, 'NULL': 10, 'DNSKEY': 48, 'DS': 43, 'ISDN': 20, 'SOA': 6, 'RP': 17, 'UID': 101, 'TALINK': 58, 'TKEY': 249, 'PX': 26, 'NSAP-PTR': 23, 'TXT': 16, 'IPSECKEY': 45, 'DNAME': 39, 'MAILA': 254, 'AFSDB': 18, 'SSHFP': 44, 'NS': 2, 'PTR': 12, 'SPF': 99, 'TA': 32768, 'A': 1, 'NXT': 30, 'AXFR': 252, 'RKEY': 57, 'KEY': 25, 'NIMLOC': 32, 'A6': 38, 'TLSA': 52, 'MG': 8, 'HIP': 55, 'NSEC': 47, 'GID': 102, 'SRV': 33, 'DLV': 32769, 'NSEC3PARAM': 51, 'UNSPEC': 103, 'TSIG': 250, 'ATMA': 34, 'RRSIG': 46, 'OPT': 41, 'MD': 3, 'NAPTR': 35, 'MF': 4, 'MB': 7, 'DHCID': 49, 'MX': 15, 'MAILB': 253, 'CERT': 37, 'NINFO': 56, 'APL': 42, 'MR': 9, 'SIG': 24, 'WKS': 11, 'KX': 36, 'NSAP': 22, 'RT': 21, 'SINK': 40}
+
+QTYPES = {
+ "LOC": 29,
+ "*": 255,
+ "IXFR": 251,
+ "UINFO": 100,
+ "NSEC3": 50,
+ "AAAA": 28,
+ "CNAME": 5,
+ "MINFO": 14,
+ "EID": 31,
+ "GPOS": 27,
+ "X25": 19,
+ "HINFO": 13,
+ "CAA": 257,
+ "NULL": 10,
+ "DNSKEY": 48,
+ "DS": 43,
+ "ISDN": 20,
+ "SOA": 6,
+ "RP": 17,
+ "UID": 101,
+ "TALINK": 58,
+ "TKEY": 249,
+ "PX": 26,
+ "NSAP-PTR": 23,
+ "TXT": 16,
+ "IPSECKEY": 45,
+ "DNAME": 39,
+ "MAILA": 254,
+ "AFSDB": 18,
+ "SSHFP": 44,
+ "NS": 2,
+ "PTR": 12,
+ "SPF": 99,
+ "TA": 32768,
+ "A": 1,
+ "NXT": 30,
+ "AXFR": 252,
+ "RKEY": 57,
+ "KEY": 25,
+ "NIMLOC": 32,
+ "A6": 38,
+ "TLSA": 52,
+ "MG": 8,
+ "HIP": 55,
+ "NSEC": 47,
+ "GID": 102,
+ "SRV": 33,
+ "DLV": 32769,
+ "NSEC3PARAM": 51,
+ "UNSPEC": 103,
+ "TSIG": 250,
+ "ATMA": 34,
+ "RRSIG": 46,
+ "OPT": 41,
+ "MD": 3,
+ "NAPTR": 35,
+ "MF": 4,
+ "MB": 7,
+ "DHCID": 49,
+ "MX": 15,
+ "MAILB": 253,
+ "CERT": 37,
+ "NINFO": 56,
+ "APL": 42,
+ "MR": 9,
+ "SIG": 24,
+ "WKS": 11,
+ "KX": 36,
+ "NSAP": 22,
+ "RT": 21,
+ "SINK": 40,
+}
INV_QTYPES = {v: k for k, v in QTYPES.items()}
# Main
break
if progs[ct.c_int(0)]:
- del(progs[ct.c_int(0)])
+ del progs[ct.c_int(0)]
if progs[ct.c_int(1)]:
- del(progs[ct.c_int(1)])
+ del progs[ct.c_int(1)]
from bcc import BPF
# Constants
-QTYPES = {'*': 65535,
- 'LOC': 29,
- 'ANY': 255,
- 'IXFR': 251,
- 'UINFO': 100,
- 'NSEC3': 50,
- 'AAAA': 28,
- 'CNAME': 5,
- 'MINFO': 14,
- 'EID': 31,
- 'GPOS': 27,
- 'X25': 19,
- 'HINFO': 13,
- 'CAA': 257,
- 'NULL': 10,
- 'DNSKEY': 48,
- 'DS': 43,
- 'ISDN': 20,
- 'SOA': 6,
- 'RP': 17,
- 'UID': 101,
- 'TALINK': 58,
- 'TKEY': 249,
- 'PX': 26,
- 'NSAP-PTR': 23,
- 'TXT': 16,
- 'IPSECKEY': 45,
- 'DNAME': 39,
- 'MAILA': 254,
- 'AFSDB': 18,
- 'SSHFP': 44,
- 'NS': 2,
- 'PTR': 12,
- 'SPF': 99,
- 'TA': 32768,
- 'A': 1,
- 'NXT': 30,
- 'AXFR': 252,
- 'RKEY': 57,
- 'KEY': 25,
- 'NIMLOC': 32,
- 'A6': 38,
- 'TLSA': 52,
- 'MG': 8,
- 'HIP': 55,
- 'NSEC': 47,
- 'GID': 102,
- 'SRV': 33,
- 'DLV': 32769,
- 'NSEC3PARAM': 51,
- 'UNSPEC': 103,
- 'TSIG': 250,
- 'ATMA': 34,
- 'RRSIG': 46,
- 'OPT': 41,
- 'MD': 3,
- 'NAPTR': 35,
- 'MF': 4,
- 'MB': 7,
- 'DHCID': 49,
- 'MX': 15,
- 'MAILB': 253,
- 'CERT': 37,
- 'NINFO': 56,
- 'APL': 42,
- 'MR': 9,
- 'SIG': 24,
- 'WKS': 11,
- 'KX': 36,
- 'NSAP': 22,
- 'RT': 21,
- 'SINK': 40
+QTYPES = {
+ "*": 65535,
+ "LOC": 29,
+ "ANY": 255,
+ "IXFR": 251,
+ "UINFO": 100,
+ "NSEC3": 50,
+ "AAAA": 28,
+ "CNAME": 5,
+ "MINFO": 14,
+ "EID": 31,
+ "GPOS": 27,
+ "X25": 19,
+ "HINFO": 13,
+ "CAA": 257,
+ "NULL": 10,
+ "DNSKEY": 48,
+ "DS": 43,
+ "ISDN": 20,
+ "SOA": 6,
+ "RP": 17,
+ "UID": 101,
+ "TALINK": 58,
+ "TKEY": 249,
+ "PX": 26,
+ "NSAP-PTR": 23,
+ "TXT": 16,
+ "IPSECKEY": 45,
+ "DNAME": 39,
+ "MAILA": 254,
+ "AFSDB": 18,
+ "SSHFP": 44,
+ "NS": 2,
+ "PTR": 12,
+ "SPF": 99,
+ "TA": 32768,
+ "A": 1,
+ "NXT": 30,
+ "AXFR": 252,
+ "RKEY": 57,
+ "KEY": 25,
+ "NIMLOC": 32,
+ "A6": 38,
+ "TLSA": 52,
+ "MG": 8,
+ "HIP": 55,
+ "NSEC": 47,
+ "GID": 102,
+ "SRV": 33,
+ "DLV": 32769,
+ "NSEC3PARAM": 51,
+ "UNSPEC": 103,
+ "TSIG": 250,
+ "ATMA": 34,
+ "RRSIG": 46,
+ "OPT": 41,
+ "MD": 3,
+ "NAPTR": 35,
+ "MF": 4,
+ "MB": 7,
+ "DHCID": 49,
+ "MX": 15,
+ "MAILB": 253,
+ "CERT": 37,
+ "NINFO": 56,
+ "APL": 42,
+ "MR": 9,
+ "SIG": 24,
+ "WKS": 11,
+ "KX": 36,
+ "NSAP": 22,
+ "RT": 21,
+ "SINK": 40,
}
INV_QTYPES = {v: k for k, v in QTYPES.items()}
-ACTIONS = {1 : 'DROP', 2 : 'TC'}
+ACTIONS = {1: "DROP", 2: "TC"}
DROP_ACTION = 1
TC_ACTION = 2
# The list of blocked IPv4, IPv6 and QNames
-#Â IP format : (IPAddress, Action)
-#Â CIDR format : (IPAddress/cidr, Action)
-#Â QName format : (QName, QType, Action)
+# IP format : (IPAddress, Action)
+# CIDR format : (IPAddress/cidr, Action)
+# QName format : (QName, QType, Action)
blocked_ipv4 = [("192.0.2.1", TC_ACTION)]
blocked_ipv6 = [("2001:db8::1", TC_ACTION)]
blocked_cidr4 = [("192.0.1.1/24", TC_ACTION)]
blocked_cidr6 = [("2001:db8::1/128", TC_ACTION)]
-blocked_qnames = [("localhost", "A", DROP_ACTION),
- ("test.com", "*", TC_ACTION)]
+blocked_qnames = [("localhost", "A", DROP_ACTION), ("test.com", "*", TC_ACTION)]
# Main
-parser = argparse.ArgumentParser(description='XDP helper for DNSDist')
-parser.add_argument('--interface', '-i', type=str, default=[], action='append',
- help='The interface(s) on which the filter will be attached')
-parser.add_argument('--maps-size', '-m', type=int, default=1024,
- help='Maximum number of entries in the eBPF maps')
-parser.add_argument('--number-of-queues', '-q', type=int, default=64,
- help='Maximum number of network queues in XSK (AF_XDP) mode')
-parser.add_argument('--xsk', action='store_true', default=False,
- help='Enable XSK (AF_XDP) mode')
+parser = argparse.ArgumentParser(description="XDP helper for DNSDist")
+parser.add_argument(
+ "--interface",
+ "-i",
+ type=str,
+ default=[],
+ action="append",
+ help="The interface(s) on which the filter will be attached",
+)
+parser.add_argument("--maps-size", "-m", type=int, default=1024, help="Maximum number of entries in the eBPF maps")
+parser.add_argument(
+ "--number-of-queues", "-q", type=int, default=64, help="Maximum number of network queues in XSK (AF_XDP) mode"
+)
+parser.add_argument("--xsk", action="store_true", default=False, help="Enable XSK (AF_XDP) mode")
parameters = parser.parse_args()
-cflag = [f'-DDDIST_MAX_NUMBER_OF_QUEUES={parameters.number_of_queues}',
- f'-DDDIST_MAPS_SIZE={parameters.maps_size}']
+cflag = [f"-DDDIST_MAX_NUMBER_OF_QUEUES={parameters.number_of_queues}", f"-DDDIST_MAPS_SIZE={parameters.maps_size}"]
interfaces = set(parameters.interface)
if len(interfaces) == 0:
- interfaces = ['eth0']
+ interfaces = ["eth0"]
if parameters.xsk:
for interface in interfaces:
- print(f'Enabling XSK (AF_XDP) on {interface}..')
- cflag.append('-DUseXsk')
+ print(f"Enabling XSK (AF_XDP) on {interface}..")
+ cflag.append("-DUseXsk")
else:
ports = [53]
- ports_str = ', '.join(str(port) for port in ports)
+ ports_str = ", ".join(str(port) for port in ports)
for interface in interfaces:
- print(f'Enabling XDP on {interface} and ports {ports_str}..')
- IN_DNS_PORT_SET = "||".join("COMPARE_PORT((x),"+str(i)+")" for i in ports)
+ print(f"Enabling XDP on {interface} and ports {ports_str}..")
+ IN_DNS_PORT_SET = "||".join("COMPARE_PORT((x)," + str(i) + ")" for i in ports)
cflag.append(r"-DIN_DNS_PORT_SET(x)=(" + IN_DNS_PORT_SET + r")")
xdp = BPF(src_file="xdp-filter.ebpf.src", cflags=cflag)
for ip in blocked_ipv6:
print(f"Blocking {ip}")
ipv6_int = int(netaddr.IPAddress(ip[0]).value)
- ipv6_bytes = bytearray([(ipv6_int & (255 << 8*(15-i))) >> (8*(15-i)) for i in range(16)])
+ ipv6_bytes = bytearray([(ipv6_int & (255 << 8 * (15 - i))) >> (8 * (15 - i)) for i in range(16)])
key = (ct.c_uint8 * 16).from_buffer(ipv6_bytes)
leaf = v6filter.Leaf()
leaf.counter = 0
network = netaddr.IPNetwork(item[0])
key.cidr = network.prefixlen
ipv6_int = int(network.network.value)
- ipv6_bytes = bytearray([(ipv6_int & (255 << 8*(15-i))) >> (8*(15-i)) for i in range(16)])
+ ipv6_bytes = bytearray([(ipv6_int & (255 << 8 * (15 - i))) >> (8 * (15 - i)) for i in range(16)])
key.addr.in6_u.u6_addr8 = (ct.c_uint8 * 16).from_buffer(ipv6_bytes)
leaf = cidr6filter.Leaf()
leaf.counter = 0
print(f"Blocking {qname}")
key = qnamefilter.Key()
qn = bytearray()
- for sub in qname[0].split('.'):
+ for sub in qname[0].split("."):
qn.append(len(sub))
for ch in sub:
qn.append(ord(ch))
print(f"- {str(addr)}/{str(item[0].cidr)} ({ACTIONS[item[1].action]}): {item[1].counter}")
for item in cidr6filter.items():
- print(f"- {str(socket.inet_ntop(socket.AF_INET6, item[0].addr))}/{str(item[0].cidr)} ({ACTIONS[item[1].action]}): {item[1].counter}")
+ print(
+ f"- {str(socket.inet_ntop(socket.AF_INET6, item[0].addr))}/{str(item[0].cidr)} ({ACTIONS[item[1].action]}): {item[1].counter}"
+ )
if qnamefilter:
print("Blocked query names:")
for item in qnamefilter.items():
- print(f"- {''.join(map(chr, item[0].qname)).strip()}/{INV_QTYPES[item[0].qtype]} ({ACTIONS[item[1].action]}): {item[1].counter}")
+ print(
+ f"- {''.join(map(chr, item[0].qname)).strip()}/{INV_QTYPES[item[0].qtype]} ({ACTIONS[item[1].action]}): {item[1].counter}"
+ )
if parameters.xsk and (xsk_destinations4 or xsk_destinations6):
print("Content of the AF_XDP (XSK) routing maps:")
import sys
import jinja2
-program = sys.argv[0].split('-')[0]
+program = sys.argv[0].split("-")[0]
product = os.path.basename(program)
apienvvar = None
apiconftemplate = None
-templateroot = '/etc/powerdns/templates.d'
-templatedestination = ''
+templateroot = "/etc/powerdns/templates.d"
+templatedestination = ""
args = []
-suffix='.conf' # default suffix, rec uses .yml
+suffix = ".conf" # default suffix, rec uses .yml
-if product == 'pdns_recursor':
- args = ['--disable-syslog']
- apienvvar = 'PDNS_RECURSOR_API_KEY'
- suffix = '.yml'
+if product == "pdns_recursor":
+ args = ["--disable-syslog"]
+ apienvvar = "PDNS_RECURSOR_API_KEY"
+ suffix = ".yml"
apiconftemplate = """webservice:
webserver: true
api_key: '{{ apikey }}'
allow_from: [0.0.0.0/0]
password: '{{ apikey }}'
"""
- templatedestination = '/etc/powerdns/recursor.d'
-elif product == 'pdns_server':
- args = ['--disable-syslog']
- apienvvar = 'PDNS_AUTH_API_KEY'
+ templatedestination = "/etc/powerdns/recursor.d"
+elif product == "pdns_server":
+ args = ["--disable-syslog"]
+ apienvvar = "PDNS_AUTH_API_KEY"
apiconftemplate = """webserver
api
api-key={{ apikey }}
webserver-allow-from=0.0.0.0/0
webserver-password={{ apikey }}
"""
- templatedestination = '/etc/powerdns/pdns.d'
-elif product == 'dnsdist':
- args = ['--supervised', '--disable-syslog']
- apienvvar = 'DNSDIST_API_KEY'
+ templatedestination = "/etc/powerdns/pdns.d"
+elif product == "dnsdist":
+ args = ["--supervised", "--disable-syslog"]
+ apienvvar = "DNSDIST_API_KEY"
apiconftemplate = """webserver("0.0.0.0:8083")
setWebserverConfig({password='{{ apikey }}', apiKey='{{ apikey }}', acl='0.0.0.0/0'})
controlSocket('0.0.0.0:5199')
setKey('{{ apikey }}')
setConsoleACL('0.0.0.0/0')
"""
- templateroot = '/etc/dnsdist/templates.d'
- templatedestination = '/etc/dnsdist/conf.d'
+ templateroot = "/etc/dnsdist/templates.d"
+ templatedestination = "/etc/dnsdist/conf.d"
-debug = os.getenv("DEBUG_CONFIG", 'no').lower() == 'yes'
+debug = os.getenv("DEBUG_CONFIG", "no").lower() == "yes"
apikey = os.getenv(apienvvar)
if apikey is not None:
webserver_conf = jinja2.Template(apiconftemplate).render(apikey=apikey)
- conffile = os.path.join(templatedestination, '_api' + suffix)
- with open(conffile, 'w') as f:
+ conffile = os.path.join(templatedestination, "_api" + suffix)
+ with open(conffile, "w") as f:
f.write(webserver_conf)
if debug:
print("Created {} with content:\n{}\n".format(conffile, webserver_conf))
-templates = os.getenv('TEMPLATE_FILES')
+templates = os.getenv("TEMPLATE_FILES")
if templates is not None:
- for templateFile in templates.split(','):
+ for templateFile in templates.split(","):
template = None
- with open(os.path.join(templateroot, templateFile + '.j2')) as f:
+ with open(os.path.join(templateroot, templateFile + ".j2")) as f:
template = jinja2.Template(f.read())
rendered = template.render(os.environ)
target = os.path.join(templatedestination, templateFile + suffix)
- with open(target, 'w') as f:
+ with open(target, "w") as f:
f.write(rendered)
if debug:
print("Created {} with content:\n{}\n".format(target, rendered))
-os.execv(program, [program]+args+sys.argv[1:])
+os.execv(program, [program] + args + sys.argv[1:])
#
# needs_sphinx = '1.0'
-sys.path.append(str(Path('.').resolve()))
+sys.path.append(str(Path(".").resolve()))
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
-#extensions = []
-#extensions = ['redjack.sphinx.lua', 'sphinxcontrib.httpdomain', 'sphinxjsondomain']
-extensions = ['sphinxcontrib.openapi',
- 'sphinxcontrib.fulltoc', 'changelog', 'depfile']
+# extensions = []
+# extensions = ['redjack.sphinx.lua', 'sphinxcontrib.httpdomain', 'sphinxjsondomain']
+extensions = ["sphinxcontrib.openapi", "sphinxcontrib.fulltoc", "changelog", "depfile"]
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ".rst"
# The master toctree document.
-master_doc = 'indexTOC'
+master_doc = "indexTOC"
# General information about the project.
-project = 'PowerDNS Authoritative Server'
-copyright = 'PowerDNS.COM BV'
-author = 'PowerDNS.COM BV'
+project = "PowerDNS Authoritative Server"
+copyright = "PowerDNS.COM BV"
+author = "PowerDNS.COM BV"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-#version = '4.2'
+# version = '4.2'
# The full version, including alpha/beta/rc tags.
-#release = '4.1.1-pre'
+# release = '4.1.1-pre'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
-exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store',
- '.venv',
- 'security-advisories/security-policy.rst',
- 'common/secpoll.rst',
- 'common/api/*']
+exclude_patterns = [
+ "_build",
+ "Thumbs.db",
+ ".DS_Store",
+ ".venv",
+ "security-advisories/security-policy.rst",
+ "common/secpoll.rst",
+ "common/api/*",
+]
# The name of the Pygments (syntax highlighting) style to use.
-highlight_language = 'none'
-pygments_style = 'sphinx'
+highlight_language = "none"
+pygments_style = "sphinx"
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
changelog_render_pullreq = "https://github.com/PowerDNS/pdns/pull/%s"
changelog_render_changeset = "https://github.com/PowerDNS/pdns/commit/%s"
-changelog_sections = ['New Features', 'Removed Features', 'Improvements', 'Bug Fixes']
-changelog_inner_tag_sort = ['Internals', 'API', 'Tools', 'ALIAS', 'DNSUpdate', 'BIND', 'MySQL', 'Postgresql', 'LDAP', 'GeoIP', 'Remote']
+changelog_sections = ["New Features", "Removed Features", "Improvements", "Bug Fixes"]
+changelog_inner_tag_sort = [
+ "Internals",
+ "API",
+ "Tools",
+ "ALIAS",
+ "DNSUpdate",
+ "BIND",
+ "MySQL",
+ "Postgresql",
+ "LDAP",
+ "GeoIP",
+ "Remote",
+]
changelog_hide_tags_in_entry = True
# a list of builtin themes.
#
html_theme_path = guzzle_sphinx_theme.html_theme_path()
-html_theme = 'guzzle_sphinx_theme'
+html_theme = "guzzle_sphinx_theme"
extensions.append("guzzle_sphinx_theme")
# Set the name of the project to appear in the sidebar
"project_nav_name": "PowerDNS Authoritative Server",
}
-html_favicon = 'common/favicon.ico'
+html_favicon = "common/favicon.ico"
-html_sidebars = { '**': ['logo-text.html', 'searchbox.html', 'relations.html', 'localtoc.html', 'sourcelink.html'] }
+html_sidebars = {"**": ["logo-text.html", "searchbox.html", "relations.html", "localtoc.html", "sourcelink.html"]}
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-html_style = 'pdns.css'
+html_static_path = ["_static"]
+html_style = "pdns.css"
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
-htmlhelp_basename = 'PowerDNSAuthoritativedoc'
+htmlhelp_basename = "PowerDNSAuthoritativedoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
- 'maxlistdepth' : '8',
+ "maxlistdepth": "8",
# The paper size ('letterpaper' or 'a4paper').
#
- 'papersize': 'a4paper',
-
+ "papersize": "a4paper",
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
-
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
-
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, 'PowerDNS-Authoritative.tex', 'PowerDNS Authoritative Server Documentation',
- 'PowerDNS.COM BV', 'manual'),
+ (
+ master_doc,
+ "PowerDNS-Authoritative.tex",
+ "PowerDNS Authoritative Server Documentation",
+ "PowerDNS.COM BV",
+ "manual",
+ ),
]
-latex_logo = 'common/powerdns-logo-500px.png'
+latex_logo = "common/powerdns-logo-500px.png"
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
descriptions = {
- 'calidns': 'A DNS recursor testing tool',
- 'dnsbulktest': 'A debugging tool for intermittent resolver failures',
- 'dnsgram': 'A debugging tool for intermittent resolver failures',
- 'dnspcap2calidns': 'A tool to convert PCAPs of DNS traffic to calidns input',
- 'dnspcap2protobuf': 'A tool to convert PCAPs of DNS traffic to PowerDNS Protobuf',
- 'dnsreplay': 'A PowerDNS nameserver debugging tool',
- 'dnsscan': 'List the amount of queries per qtype in a pcap',
- 'dnsscope': 'A PowerDNS nameserver debugging tool',
- 'dnstcpbench': 'tool to perform TCP benchmarking of nameservers',
- 'dnswasher': 'A PowerDNS nameserver debugging tool',
- 'dumresp': 'A dumb DNS responder',
- 'ixfrdist': 'An IXFR/AXFR-only server that re-distributes zones',
- 'ixplore': 'A tool that provides insights into IXFRs',
- 'nproxy': 'DNS notification proxy',
- 'nsec3dig': 'Show and validate NSEC3 proofs',
- 'pdns_control': 'Control the PowerDNS nameserver',
- 'pdns_notify': 'A simple DNS NOTIFY sender',
- 'pdns_server': 'The PowerDNS Authoritative Nameserver',
- 'pdnsutil': 'PowerDNS record and DNSSEC command and control',
- 'saxfr': 'Perform AXFRs and show information about it',
- 'sdig': 'Perform a DNS query and show the results',
- 'zone2json': 'convert BIND zones to JSON',
- 'zone2ldap': 'convert zonefiles to ldif',
- 'zone2sql': 'convert BIND zones to SQL',
+ "calidns": "A DNS recursor testing tool",
+ "dnsbulktest": "A debugging tool for intermittent resolver failures",
+ "dnsgram": "A debugging tool for intermittent resolver failures",
+ "dnspcap2calidns": "A tool to convert PCAPs of DNS traffic to calidns input",
+ "dnspcap2protobuf": "A tool to convert PCAPs of DNS traffic to PowerDNS Protobuf",
+ "dnsreplay": "A PowerDNS nameserver debugging tool",
+ "dnsscan": "List the amount of queries per qtype in a pcap",
+ "dnsscope": "A PowerDNS nameserver debugging tool",
+ "dnstcpbench": "tool to perform TCP benchmarking of nameservers",
+ "dnswasher": "A PowerDNS nameserver debugging tool",
+ "dumresp": "A dumb DNS responder",
+ "ixfrdist": "An IXFR/AXFR-only server that re-distributes zones",
+ "ixplore": "A tool that provides insights into IXFRs",
+ "nproxy": "DNS notification proxy",
+ "nsec3dig": "Show and validate NSEC3 proofs",
+ "pdns_control": "Control the PowerDNS nameserver",
+ "pdns_notify": "A simple DNS NOTIFY sender",
+ "pdns_server": "The PowerDNS Authoritative Nameserver",
+ "pdnsutil": "PowerDNS record and DNSSEC command and control",
+ "saxfr": "Perform AXFRs and show information about it",
+ "sdig": "Perform a DNS query and show the results",
+ "zone2json": "convert BIND zones to JSON",
+ "zone2ldap": "convert zonefiles to ldif",
+ "zone2sql": "convert BIND zones to SQL",
}
man_pages = []
-for f in glob.glob('manpages/*.1.rst'):
- srcname = '.'.join(f.split('.')[:-1])
- destname = srcname.split('/')[-1][:-2]
- man_pages.append((srcname, destname, descriptions.get(destname, ''),
- [author], 1))
-man_pages.append(('manpages/ixfrdist.yml.5', 'ixfrdist.yml',
- 'The ixfrdist configuration file', [author], 5))
+for f in glob.glob("manpages/*.1.rst"):
+ srcname = ".".join(f.split(".")[:-1])
+ destname = srcname.split("/")[-1][:-2]
+ man_pages.append((srcname, destname, descriptions.get(destname, ""), [author], 1))
+man_pages.append(("manpages/ixfrdist.yml.5", "ixfrdist.yml", "The ixfrdist configuration file", [author], 5))
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
-#texinfo_documents = [
+# texinfo_documents = [
# (master_doc, 'PowerDNSRecursor', 'PowerDNS Recursor Documentation',
# author, 'PowerDNSRecursor', 'One line description of project.',
# 'Miscellaneous'),
-#]
-
+# ]
# -- Options for Epub output ----------------------------------------------
# epub_uid = ''
# A list of files that should not be packed into the epub file.
-epub_exclude_files = ['search.html']
+epub_exclude_files = ["search.html"]
-depfile = 'sphinx.d'
-depfile_stamp = 'sphinx.stamp'
+depfile = "sphinx.d"
+depfile_stamp = "sphinx.stamp"
# See the COPYING file in the top-level directory.
"""depfile is a Sphinx extension that writes a dependency file for
- an external build system"""
+an external build system"""
import os
import sys
from pathlib import Path
-__version__ = '1.0'
+__version__ = "1.0"
+
def get_infiles(env):
for x in env.found_docs:
yield str(env.doc2path(x))
- yield from ((os.path.join(env.srcdir, dep)
- for dep in env.dependencies[x]))
+ yield from ((os.path.join(env.srcdir, dep) for dep in env.dependencies[x]))
for mod in sys.modules.values():
- if hasattr(mod, '__file__'):
+ if hasattr(mod, "__file__"):
if mod.__file__:
yield mod.__file__
# this is perhaps going to include unused files:
for static_path in env.config.html_static_path + env.config.templates_path:
- for path in Path(static_path).rglob('*'):
+ for path in Path(static_path).rglob("*"):
yield str(path)
# also include kdoc script
- #yield str(env.config.kerneldoc_bin[1])
+ # yield str(env.config.kerneldoc_bin[1])
def write_depfile(app, exception):
# its timestamp does not necessarily change when the contents change.
# So create a timestamp file.
if env.config.depfile_stamp:
- with open(env.config.depfile_stamp, 'w') as f:
- print('depfile.py: Writing ' + env.config.depfile_stamp)
+ with open(env.config.depfile_stamp, "w") as f:
+ print("depfile.py: Writing " + env.config.depfile_stamp)
- with open(env.config.depfile, 'w') as f:
- print('depfile.py: Writing ' + env.config.depfile)
+ with open(env.config.depfile, "w") as f:
+ print("depfile.py: Writing " + env.config.depfile)
print((env.config.depfile_stamp or app.outdir) + ": \\", file=f)
print(*get_infiles(env), file=f)
for x in get_infiles(env):
print(x + ":", file=f)
+
def setup(app):
- app.add_config_value('depfile', None, 'env')
- app.add_config_value('depfile_stamp', None, 'env')
- app.connect('build-finished', write_depfile)
+ app.add_config_value("depfile", None, "env")
+ app.add_config_value("depfile_stamp", None, "env")
+ app.connect("build-finished", write_depfile)
- return dict(
- version = __version__,
- parallel_read_safe = True,
- parallel_write_safe = True
- )
+ return dict(version=__version__, parallel_read_safe=True, parallel_write_safe=True)
sphinx_build = venv_directory.joinpath("bin").joinpath("sphinx-build")
if args.pdf_name:
- build_args = [
- sphinx_build,
- "-M",
- "latexpdf",
- source_directory,
- '.'
- ]
+ build_args = [sphinx_build, "-M", "latexpdf", source_directory, "."]
else:
build_args = [
sphinx_build,
target_directory,
]
subprocess.run(
- build_args + files, # if files is empty, it means do all files
- check=True
+ build_args + files, # if files is empty, it means do all files
+ check=True,
)
if args.pdf_name:
- os.rename(build_root.joinpath('latex').joinpath(args.pdf_name), args.pdf_name)
+ os.rename(build_root.joinpath("latex").joinpath(args.pdf_name), args.pdf_name)
+
def create_argument_parser():
"""Create command-line argument parser."""
- parser = argparse.ArgumentParser(
- description="Build html and pdf docs for PowerDNS open source products"
- )
+ parser = argparse.ArgumentParser(description="Build html and pdf docs for PowerDNS open source products")
parser.add_argument(
"--build-root",
type=Path,
target_directory,
]
+ files,
- check=True
+ check=True,
)
def create_argument_parser():
"""Create command-line argument parser."""
- parser = argparse.ArgumentParser(
- description="Build man pages for PowerDNS open source products"
- )
+ parser = argparse.ArgumentParser(description="Build man pages for PowerDNS open source products")
parser.add_argument(
"--build-root",
type=Path,
# define a simple $domain
ID_DOMAIN = {
- 1: 'unit.test.',
+ 1: "unit.test.",
}
DOMAINS = {
- 'unit.test.': {
- 'id': 1,
- 'ttl': 300,
- 'name': 'unit.test.',
- 'notified_serial': 0,
- 'meta': {},
- 'keys': {},
- 'rr': {
- 'unit.test.' : {
- 'SOA': ["ns.unit.test. hostmaster.unit.test. 1 2 3 4 5"],
- 'NS': ["ns1.unit.test.", "ns2.unit.test."],
+ "unit.test.": {
+ "id": 1,
+ "ttl": 300,
+ "name": "unit.test.",
+ "notified_serial": 0,
+ "meta": {},
+ "keys": {},
+ "rr": {
+ "unit.test.": {
+ "SOA": ["ns.unit.test. hostmaster.unit.test. 1 2 3 4 5"],
+ "NS": ["ns1.unit.test.", "ns2.unit.test."],
},
- 'ns1.unit.test.': {
- 'A': ["10.0.0.1"]
- },
- 'ns2.unit.test.': {
- 'A': ["10.0.0.2"]
- },
- 'empty.unit.test.': {}
+ "ns1.unit.test.": {"A": ["10.0.0.1"]},
+ "ns2.unit.test.": {"A": ["10.0.0.2"]},
+ "empty.unit.test.": {},
},
- 'kind': 'native',
+ "kind": "native",
},
- 'master.test.': {
- 'id': 2,
- 'ttl': 300,
- 'name': 'master.test.',
- 'notified_serial': 2,
- 'meta': {},
- 'keys': {},
- 'rr': {
- 'master.test.': {
- 'SOA': ["ns.master.test. hostmaster.master.test. 1 2 3 4 5"],
+ "master.test.": {
+ "id": 2,
+ "ttl": 300,
+ "name": "master.test.",
+ "notified_serial": 2,
+ "meta": {},
+ "keys": {},
+ "rr": {
+ "master.test.": {
+ "SOA": ["ns.master.test. hostmaster.master.test. 1 2 3 4 5"],
}
},
- 'kind': 'master',
+ "kind": "master",
},
}
TSIG_KEYS = {
- 'test.': {
- 'name': 'test.',
- 'algorithm': 'NULL.',
- 'content': 'NULL',
+ "test.": {
+ "name": "test.",
+ "algorithm": "NULL.",
+ "content": "NULL",
}
}
-MASTERS = {
- 'ns1.unit.test.': {
- 'ip': '10.0.0.1'
- }
-}
+MASTERS = {"ns1.unit.test.": {"ip": "10.0.0.1"}}
+
class Handler(pdns.remotebackend.Handler):
def get_domain(self, domain):
p = domain.find(".")
if p == -1:
break
- domain = domain[p+1:]
+ domain = domain[p + 1 :]
return None
- def do_lookup(self, qname='', qtype='', **kwargs):
+ def do_lookup(self, qname="", qtype="", **kwargs):
domain = self.get_domain(qname)
if domain:
self.result = []
- rrset = domain['rr'].get(qname, {'qtype': []})
+ rrset = domain["rr"].get(qname, {"qtype": []})
rr = rrset.get(qtype, [])
for r in rr:
- self.result.append(self.record(qname=qname, qtype=qtype, content=r, ttl=domain['ttl']))
+ self.result.append(self.record(qname=qname, qtype=qtype, content=r, ttl=domain["ttl"]))
def do_list(self, zonename="", **kwargs):
domain = self.get_domain(zonename)
if domain:
self.result = []
- for qname, rrset in domain['rr'].items():
+ for qname, rrset in domain["rr"].items():
for qtype, rr in rrset.items():
for r in rr:
- self.result.append(self.record(qname=qname, qtype=qtype, content=r, ttl=domain['ttl']))
+ self.result.append(self.record(qname=qname, qtype=qtype, content=r, ttl=domain["ttl"]))
- def do_getalldomainmetadata(self, name='', **kwargs):
+ def do_getalldomainmetadata(self, name="", **kwargs):
domain = self.get_domain(name)
if domain:
- self.result = domain['meta']
+ self.result = domain["meta"]
- def do_getdomainmetadata(self, name='', kind='', **kwargs):
+ def do_getdomainmetadata(self, name="", kind="", **kwargs):
self.do_getalldomainmetadata(name=name)
if self.result:
self.result = self.result[kind]
- def do_setdomainmetadata(self, name='', kind='', value=None, **kwargs):
+ def do_setdomainmetadata(self, name="", kind="", value=None, **kwargs):
domain = self.get_domain(name)
if domain:
if value is None:
- del domain['meta'][kind]
+ del domain["meta"][kind]
else:
- domain['meta'][kind] = value
+ domain["meta"][kind] = value
self.result = True
- def do_adddomainkey(self, name='', key=None, **kwargs):
+ def do_adddomainkey(self, name="", key=None, **kwargs):
if key is None:
key = {}
domain = self.get_domain(name)
if domain:
- k_id = len(domain['keys']) + 1
- key['id'] = k_id
- domain['keys'][k_id] = key
+ k_id = len(domain["keys"]) + 1
+ key["id"] = k_id
+ domain["keys"][k_id] = key
self.result = k_id
- def do_getdomainkeys(self, name='', **kwargs):
+ def do_getdomainkeys(self, name="", **kwargs):
domain = self.get_domain(name)
if domain:
self.result = []
- for k_id, k in domain['keys'].items():
+ for k_id, k in domain["keys"].items():
self.result.append(k)
- def do_activatedomainkey(self, name='', **kwargs):
+ def do_activatedomainkey(self, name="", **kwargs):
domain = self.get_domain(name)
if domain:
- key = domain['keys'].get(int(kwargs['id']))
+ key = domain["keys"].get(int(kwargs["id"]))
if key:
- key['active'] = True
+ key["active"] = True
self.result = True
- def do_deactivatedomainkey(self, name='', **kwargs):
+ def do_deactivatedomainkey(self, name="", **kwargs):
domain = self.get_domain(name)
if domain:
- key = domain['keys'].get(int(kwargs['id']))
+ key = domain["keys"].get(int(kwargs["id"]))
if key:
- key['active'] = False
+ key["active"] = False
self.result = True
- def do_publishdomainkey(self, name='', **kwargs):
+ def do_publishdomainkey(self, name="", **kwargs):
domain = self.get_domain(name)
if domain:
- key = domain['keys'].get(int(kwargs['id']))
+ key = domain["keys"].get(int(kwargs["id"]))
if key:
- key['published'] = True
+ key["published"] = True
self.result = True
- def do_unpublishdomainkey(self, name='', **kwargs):
+ def do_unpublishdomainkey(self, name="", **kwargs):
domain = self.get_domain(name)
if domain:
- key = domain['keys'].get(int(kwargs['id']))
+ key = domain["keys"].get(int(kwargs["id"]))
if key:
- key['published'] = False
+ key["published"] = False
self.result = True
- def do_removedomainkey(self, name='', **kwargs):
+ def do_removedomainkey(self, name="", **kwargs):
domain = self.get_domain(name)
if domain:
- k_id = int(kwargs['id'])
- if k_id in domain['keys']:
- del domain['keys'][k_id]
+ k_id = int(kwargs["id"])
+ if k_id in domain["keys"]:
+ del domain["keys"][k_id]
self.result = True
- def do_getbeforeandafternamesabsolute(self, qname='', **kwargs):
- if qname == 'middle.unit.test.':
+ def do_getbeforeandafternamesabsolute(self, qname="", **kwargs):
+ if qname == "middle.unit.test.":
self.result = {
- 'unhashed': 'middle.',
- 'before': 'begin.',
- 'after': 'stop.',
+ "unhashed": "middle.",
+ "before": "begin.",
+ "after": "stop.",
}
def do_setnotified(self, **kwargs):
- d_id = int(kwargs['id'])
+ d_id = int(kwargs["id"])
domain_name = ID_DOMAIN.get(d_id)
domain = DOMAINS.get(domain_name)
if domain:
- domain['notified_serial'] = kwargs['serial']
+ domain["notified_serial"] = kwargs["serial"]
self.result = True
def fill_domaininfo(self, name=""):
domain = self.get_domain(name)
if domain:
- self.result.append({
- 'id': domain['id'],
- 'zone': domain['name'],
- 'masters': MASTERS,
- 'notified_serial': domain['notified_serial'],
- 'serial': domain['notified_serial'],
- 'last_check': int(time.time()),
- 'kind': domain['kind']
- })
+ self.result.append(
+ {
+ "id": domain["id"],
+ "zone": domain["name"],
+ "masters": MASTERS,
+ "notified_serial": domain["notified_serial"],
+ "serial": domain["notified_serial"],
+ "last_check": int(time.time()),
+ "kind": domain["kind"],
+ }
+ )
def do_getdomaininfo(self, name="", **kwargs):
self.result = []
if self.result:
self.result = self.result[0]
- def do_ismaster(self, name='', ip='', **kwargs):
+ def do_ismaster(self, name="", ip="", **kwargs):
ips = MASTERS.get(name, [])
if ip in ips:
self.result = True
- def do_supermasterbackend(self, domain='', nsset=[], **kwargs):
+ def do_supermasterbackend(self, domain="", nsset=[], **kwargs):
d_id = len(DOMAINS) + 1
dom = domain.lower()
domainObject = {
- 'id': d_id,
- 'name': dom,
- 'kind': 'slave',
- 'notified_serial': 0,
- 'meta': {},
- 'keys': {},
- 'rr': {
+ "id": d_id,
+ "name": dom,
+ "kind": "slave",
+ "notified_serial": 0,
+ "meta": {},
+ "keys": {},
+ "rr": {
dom: {
- 'SOA': ["ns.%s hostmaster.%s 1 2 3 4 5" % (dom, dom)],
+ "SOA": ["ns.%s hostmaster.%s 1 2 3 4 5" % (dom, dom)],
}
},
- 'ttl': 300,
+ "ttl": 300,
}
nsset = []
for rr in nsset:
- nsset.append(self.record(qname=rr['qname'], qtype=rr['qtype'], content=rr['content'], ttl=rr['ttl']))
+ nsset.append(self.record(qname=rr["qname"], qtype=rr["qtype"], content=rr["content"], ttl=rr["ttl"]))
- domainObject['rr'][dom]['NS'] = nsset
+ domainObject["rr"][dom]["NS"] = nsset
DOMAINS[dom] = domainObject
- self.result = [{
- 'nameserver': 'ns.%s' % dom,
- 'account': ''
- }]
+ self.result = [{"nameserver": "ns.%s" % dom, "account": ""}]
-
- def do_createslavedomain(self, domain='', **kwargs):
+ def do_createslavedomain(self, domain="", **kwargs):
d_id = len(DOMAINS) + 1
dom = domain.lower()
domainObject = {
- 'id': d_id,
- 'name': dom,
- 'kind': 'slave',
- 'notified_serial': 0,
- 'ttl': 300,
- 'meta': {},
- 'keys': {},
- 'rr': {
- }
+ "id": d_id,
+ "name": dom,
+ "kind": "slave",
+ "notified_serial": 0,
+ "ttl": 300,
+ "meta": {},
+ "keys": {},
+ "rr": {},
}
DOMAINS[dom] = domainObject
self.result = True
def do_feedrecord(self, rr={}, **kwargs):
- qname = rr['qname']
- qtype = rr['qtype']
+ qname = rr["qname"]
+ qtype = rr["qtype"]
domain = self.get_domain(qname)
if domain:
- if not qname in domain['rr']:
- domain['rr'][qname] = {qtype: []}
- elif not qtype in domain['rr'][qname]:
- domain['rr'][qname][qtype] = []
- domain['rr'][qname][qtype].append(self.record(
- qname=qname,
- qtype=qtype,
- content=rr['content'],
- ttl=rr.get('ttl', domain['ttl']))
+ if not qname in domain["rr"]:
+ domain["rr"][qname] = {qtype: []}
+ elif not qtype in domain["rr"][qname]:
+ domain["rr"][qname][qtype] = []
+ domain["rr"][qname][qtype].append(
+ self.record(qname=qname, qtype=qtype, content=rr["content"], ttl=rr.get("ttl", domain["ttl"]))
)
self.result = True
- def do_replacerrset(self, qname='', qtype='', rrset=[], **kwargs):
+ def do_replacerrset(self, qname="", qtype="", rrset=[], **kwargs):
domain = self.get_domain(qname)
- if domain and qname in domain['rr']:
- if qtype in domain['rr'][qname]:
- del domain['rr'][qname][qtype]
+ if domain and qname in domain["rr"]:
+ if qtype in domain["rr"][qname]:
+ del domain["rr"][qname][qtype]
for row in rrset:
self.do_feedrecord(rr=row)
def do_feedents3(self, **kwargs):
self.result = True
- def do_gettsigkey(self, name='', **kwargs):
+ def do_gettsigkey(self, name="", **kwargs):
if name in TSIG_KEYS:
self.result = TSIG_KEYS[name]
- def do_settsigkey(self, name='', algorithm='', content='', **kwargs):
+ def do_settsigkey(self, name="", algorithm="", content="", **kwargs):
TSIG_KEYS[name] = {
- 'name': name,
- 'algorithm': algorithm,
- 'content': content,
+ "name": name,
+ "algorithm": algorithm,
+ "content": content,
}
self.result = True
for name, key in TSIG_KEYS.items():
self.result.append(key)
- def do_deletetsigkey(self, name='', **kwargs):
+ def do_deletetsigkey(self, name="", **kwargs):
if name in TSIG_KEYS:
del TSIG_KEYS[name]
self.result = True
def do_aborttransaction(self, **kwargs):
self.result = True
- def do_directbackendcmd(self, query='', **kwargs):
+ def do_directbackendcmd(self, query="", **kwargs):
self.result = query
def do_getalldomains(self, **kwargs):
def do_getupdatedmasters(self, **kwargs):
self.result = []
for name in DOMAINS.keys():
- if DOMAINS[name]['kind'] == 'master':
+ if DOMAINS[name]["kind"] == "master":
self.fill_domaininfo(name=name)
class BackendHandler(Handler):
def __init__(self, options={}):
super().__init__(options=options)
- self.dbpath = options['dbpath']
+ self.dbpath = options["dbpath"]
self.db = sqlite3.connect(self.dbpath)
def get_domain_id(self, name):
raise KeyError
return int(row[0])
- def record(self, qname='', qtype='', content='', ttl=1, prio=0, auth=1, domain_id=-1):
+ def record(self, qname="", qtype="", content="", ttl=1, prio=0, auth=1, domain_id=-1):
"""Generate one record"""
if ttl == -1:
ttl = self.ttl
- if qtype in ('MX', 'SRV'):
+ if qtype in ("MX", "SRV"):
content = "%d %s" % (prio, content)
- return {'qtype': qtype, 'qname': qname, 'content': content,
- 'ttl': ttl, 'auth': auth, 'domain_id': domain_id}
+ return {"qtype": qtype, "qname": qname, "content": content, "ttl": ttl, "auth": auth, "domain_id": domain_id}
# ends up here as qname=qname, id=id
def getbeforename(self, **kwargs):
- cur = self.db.execute("SELECT ordername FROM records WHERE ordername < :qname AND domain_id = :id ORDER BY ordername DESC LIMIT 1", kwargs)
+ cur = self.db.execute(
+ "SELECT ordername FROM records WHERE ordername < :qname AND domain_id = :id ORDER BY ordername DESC LIMIT 1",
+ kwargs,
+ )
row = cur.fetchone()
if not row:
- cur = self.db.execute("SELECT ordername FROM records WHERE domain_id = :id ORDER by ordername DESC LIMIT 1", kwargs)
+ cur = self.db.execute(
+ "SELECT ordername FROM records WHERE domain_id = :id ORDER by ordername DESC LIMIT 1", kwargs
+ )
row = cur.fetchone()
result = row[0]
if row[0] is None:
- result = ''
+ result = ""
return result
def getaftername(self, **kwargs):
- cur = self.db.execute("SELECT ordername FROM records WHERE ordername > :qname AND domain_id = :id ORDER BY ordername LIMIT 1", kwargs)
+ cur = self.db.execute(
+ "SELECT ordername FROM records WHERE ordername > :qname AND domain_id = :id ORDER BY ordername LIMIT 1",
+ kwargs,
+ )
row = cur.fetchone()
if row is None:
- cur = self.db.execute("SELECT ordername FROM records WHERE domain_id = :id ORDER by ordername LIMIT 1", kwargs)
+ cur = self.db.execute(
+ "SELECT ordername FROM records WHERE domain_id = :id ORDER by ordername LIMIT 1", kwargs
+ )
row = cur.fetchone()
result = row[0]
if row[0] is None:
- result = ''
+ result = ""
return result
def do_getbeforeandafternamesabsolute(self, **kwargs):
self.result = {
- 'before': self.getbeforename(**kwargs),
- 'after': self.getaftername(**kwargs),
- 'unhashed': kwargs['qname']
+ "before": self.getbeforename(**kwargs),
+ "after": self.getaftername(**kwargs),
+ "unhashed": kwargs["qname"],
}
def do_getbeforeandafternames(self, **kwargs):
def do_getdomainkeys(self, name, **kwargs):
self.result = []
- cur = self.db.execute("SELECT cryptokeys.id, flags, active, published, content FROM domains JOIN cryptokeys ON domains.id = cryptokeys.domain_id WHERE domains.name = :name", {'name':name})
+ cur = self.db.execute(
+ "SELECT cryptokeys.id, flags, active, published, content FROM domains JOIN cryptokeys ON domains.id = cryptokeys.domain_id WHERE domains.name = :name",
+ {"name": name},
+ )
for row in cur.fetchall():
- self.result.append({
- 'id': row[0],
- 'flags': row[1],
- 'active': row[2] != 0,
- 'published': row[3],
- 'content': row[4]
- })
+ self.result.append(
+ {"id": row[0], "flags": row[1], "active": row[2] != 0, "published": row[3], "content": row[4]}
+ )
if len(self.result) == 0:
self.result = False
self.log.append(self.dbpath)
- def do_lookup(self, qname='', qtype='', domain_id=-1, **kwargs):
+ def do_lookup(self, qname="", qtype="", domain_id=-1, **kwargs):
self.result = []
- if kwargs.get('zone-id', -1) > 0:
- domain_id = kwargs['zone-id']
+ if kwargs.get("zone-id", -1) > 0:
+ domain_id = kwargs["zone-id"]
if domain_id > -1:
if qtype == "ANY":
sql = "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE name = :qname AND domain_id = :domain_id"
sql = "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE name = :qname"
else:
sql = "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE name = :qname AND type = :qtype"
- cur = self.db.execute(sql, {'qname': qname, 'qtype': qtype, 'domain_id': domain_id})
- for row in cur.fetchall():
- self.result.append(self.record(qname=row[1],qtype=row[2],content=row[3],ttl=row[4],prio=row[5],auth=row[6],domain_id=row[0]))
+ cur = self.db.execute(sql, {"qname": qname, "qtype": qtype, "domain_id": domain_id})
+ for row in cur.fetchall():
+ self.result.append(
+ self.record(
+ qname=row[1], qtype=row[2], content=row[3], ttl=row[4], prio=row[5], auth=row[6], domain_id=row[0]
+ )
+ )
- def do_getdomaininfo(self, name='', **kwargs):
+ def do_getdomaininfo(self, name="", **kwargs):
self.result = False
- cur = self.db.execute("SELECT domain_id,name,content FROM records WHERE name = :name AND type = 'SOA'", {'name': name})
+ cur = self.db.execute(
+ "SELECT domain_id,name,content FROM records WHERE name = :name AND type = 'SOA'", {"name": name}
+ )
for row in cur.fetchall():
self.result = {
- 'zone': row[1],
- 'serial': int(row[2].split(' ')[2]),
- 'kind': 'native',
- 'id': row[0],
+ "zone": row[1],
+ "serial": int(row[2].split(" ")[2]),
+ "kind": "native",
+ "id": row[0],
}
def do_getalldomains(self):
self.result = []
- cur = self.db.execute("SELECT domain_id,name,content FROM records WHERE name = :name AND type = 'SOA'", {'name': name})
+ cur = self.db.execute(
+ "SELECT domain_id,name,content FROM records WHERE name = :name AND type = 'SOA'", {"name": name}
+ )
for row in cur.fetchall():
- self.result.append({
- 'zone': row[1],
- 'serial': int(row[2].split(' ')[2]),
- 'kind': 'native',
- 'id': row[0],
- })
-
- def do_list(self, zonename='', domain_id=-1, **kwargs):
+ self.result.append(
+ {
+ "zone": row[1],
+ "serial": int(row[2].split(" ")[2]),
+ "kind": "native",
+ "id": row[0],
+ }
+ )
+
+ def do_list(self, zonename="", domain_id=-1, **kwargs):
if domain_id == -1:
try:
domain_id = self.get_domain_id(zonename)
return
if domain_id > -1:
self.result = []
- cur = self.db.execute("SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE domain_id = ?", (domain_id,))
+ cur = self.db.execute(
+ "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE domain_id = ?", (domain_id,)
+ )
for row in cur.fetchall():
- self.result.append(self.record(qname=row[1],qtype=row[2],content=row[3],ttl=row[4],prio=row[5],auth=row[6],domain_id=row[0]))
+ self.result.append(
+ self.record(
+ qname=row[1],
+ qtype=row[2],
+ content=row[3],
+ ttl=row[4],
+ prio=row[5],
+ auth=row[6],
+ domain_id=row[0],
+ )
+ )
def do_adddomainkey(self, name, key, **kwargs):
try:
domain_id = self.get_domain_id(name)
except KeyError:
return
- key['domain_id'] = domain_id
+ key["domain_id"] = domain_id
- cur = self.db.execute("INSERT INTO cryptokeys (domain_id, flags, active, published, content) VALUES(:domain_id, :flags, :active, :published, :content)", key)
+ cur = self.db.execute(
+ "INSERT INTO cryptokeys (domain_id, flags, active, published, content) VALUES(:domain_id, :flags, :active, :published, :content)",
+ key,
+ )
self.db.commit()
self.result = cur.lastrowid
def do_deactivatedomainkey(self, **kwargs):
try:
- domain_id = self.get_domain_id(kwargs['name'])
+ domain_id = self.get_domain_id(kwargs["name"])
except KeyError:
return
- kwargs['domain_id'] = domain_id
+ kwargs["domain_id"] = domain_id
self.db.execute("UPDATE cryptokeys SET active = 0 WHERE domain_id = :domain_id AND id = :id", kwargs)
self.db.commit()
def do_activatedomainkey(self, **kwargs):
try:
- domain_id = self.get_domain_id(kwargs['name'])
+ domain_id = self.get_domain_id(kwargs["name"])
except KeyError:
return
- kwargs['domain_id'] = domain_id
+ kwargs["domain_id"] = domain_id
self.db.execute("UPDATE cryptokeys SET active = 1 WHERE domain_id = :domain_id AND id = :id", kwargs)
self.db.commit()
def do_unpublishdomainkey(self, **kwargs):
try:
- domain_id = self.get_domain_id(kwargs['name'])
+ domain_id = self.get_domain_id(kwargs["name"])
except KeyError:
return
- kwargs['domain_id'] = domain_id
+ kwargs["domain_id"] = domain_id
self.db.execute("UPDATE cryptokeys SET published = 0 WHERE domain_id = :domain_id AND id = :id", kwargs)
self.db.commit()
def do_publishdomainkey(self, **kwargs):
try:
- domain_id = self.get_domain_id(kwargs['name'])
+ domain_id = self.get_domain_id(kwargs["name"])
except KeyError:
return
- kwargs['domain_id'] = domain_id
+ kwargs["domain_id"] = domain_id
self.db.execute("UPDATE cryptokeys SET published = 1 WHERE domain_id = :domain_id AND id = :id", kwargs)
self.db.commit()
self.result = True
def do_getalldomainmetadata(self, name, **kwargs):
- cur = self.db.execute("SELECT kind, content FROM domainmetadata JOIN domains WHERE name = :name", {'name': name})
+ cur = self.db.execute(
+ "SELECT kind, content FROM domainmetadata JOIN domains WHERE name = :name", {"name": name}
+ )
self.result = {}
for row in cur.fetchall():
if not row[0] in self.result:
self.result[row[0]].append(row[1])
def do_getdomainmetadata(self, name, kind, **kwargs):
- cur = self.db.execute("SELECT content FROM domainmetadata JOIN domains WHERE name = :name AND kind = :kind", {'name': name, 'kind': kind})
+ cur = self.db.execute(
+ "SELECT content FROM domainmetadata JOIN domains WHERE name = :name AND kind = :kind",
+ {"name": name, "kind": kind},
+ )
self.result = cur.fetchall()
def do_setdomainmetadata(self, name, kind, value, **kwargs):
except KeyError:
return
- self.db.execute("DELETE FROM domainmetadata WHERE domain_id = :domain_id AND kind = :kind", {
- 'domain_id': domain_id,
- 'kind': kind
- })
+ self.db.execute(
+ "DELETE FROM domainmetadata WHERE domain_id = :domain_id AND kind = :kind",
+ {"domain_id": domain_id, "kind": kind},
+ )
if value:
- self.db.execute("INSERT INTO domainmetadata (domain_id,kind,content) VALUES(:domain_id, :kind, :content)", {
- 'domain_id': domain_id,
- 'kind': kind,
- 'content': content
- })
+ self.db.execute(
+ "INSERT INTO domainmetadata (domain_id,kind,content) VALUES(:domain_id, :kind, :content)",
+ {"domain_id": domain_id, "kind": kind, "content": content},
+ )
self.db.commit()
def do_starttransaction(self, trxid, **kwargs):
from urllib.parse import parse_qsl, urlparse, unquote
+
class DNSBackendHandler(http.server.BaseHTTPRequestHandler):
def __init__(self, *args, **kwargs):
- self.handler = kwargs['handler']
+ self.handler = kwargs["handler"]
super().__init__(*args)
def url_to_args(self):
parts.pop(0)
self.method = None
- if parts.pop(0) != 'dns':
+ if parts.pop(0) != "dns":
return
self.method = parts.pop(0).lower()
self.args = {}
- if self.method == 'lookup':
- self.args['qname'] = parts.pop(0)
- self.args['qtype'] = parts.pop(0)
- elif self.method == 'list':
- self.args['id'] = int(parts.pop(0))
- self.args['zonename'] = parts.pop(0)
- elif self.method in ('getbeforeandafternamesabsolute', 'getbeforeandafternames'):
- self.args['id'] = int(parts.pop(0))
- self.args['qname'] = parts.pop(0)
- elif self.method in ('getdomainmetadata', 'setdomainmetadata'):
- self.args['name'] = parts.pop(0)
- self.args['kind'] = parts.pop(0)
- elif self.method == 'getdomainkeys':
- self.args['name'] = parts.pop(0)
- elif self.method in ('removedomainkey', 'activatedomainkey', 'deactivatedomainkey'):
- self.args['id'] = int(parts.pop(0))
- self.args['name'] = parts.pop(0)
- elif self.method in ('adddomainkey', 'gettsigkey', 'getdomaininfo', 'settsigkey', 'deletetsigkey', 'getalldomainmetadata'):
- self.args['name'] = parts.pop(0)
- elif self.method == 'setnotified':
- self.args['id'] = int(parts.pop(0))
- elif self.method == 'feedents':
- self.args['id'] = int(parts.pop(0))
- self.args['trxid'] = int(parts.pop(0))
- elif self.method == 'ismaster':
- self.args['name'] = parts.pop(0)
- self.args['ip'] = parts.pop(0)
- elif self.method in ('supermasterbackend', 'createslavedomain'):
- self.args['ip'] = parts.pop(0)
- self.args['domain'] = parts.pop(0)
- elif self.method in ('feedents3', 'starttransaction'):
- self.args['id'] = int(parts.pop(0))
- self.args['domain'] = parts.pop(0)
- self.args['trxid'] = int(parts.pop(0))
- elif self.method in ('feedrecord', 'committransaction', 'aborttransaction'):
- self.args['trxid'] = int(parts.pop(0))
- elif self.method == 'replacerrset':
- self.args['id'] = int(parts.pop(0))
- self.args['qname'] = parts.pop(0)
- self.args['qtype'] = parts.pop(0)
+ if self.method == "lookup":
+ self.args["qname"] = parts.pop(0)
+ self.args["qtype"] = parts.pop(0)
+ elif self.method == "list":
+ self.args["id"] = int(parts.pop(0))
+ self.args["zonename"] = parts.pop(0)
+ elif self.method in ("getbeforeandafternamesabsolute", "getbeforeandafternames"):
+ self.args["id"] = int(parts.pop(0))
+ self.args["qname"] = parts.pop(0)
+ elif self.method in ("getdomainmetadata", "setdomainmetadata"):
+ self.args["name"] = parts.pop(0)
+ self.args["kind"] = parts.pop(0)
+ elif self.method == "getdomainkeys":
+ self.args["name"] = parts.pop(0)
+ elif self.method in ("removedomainkey", "activatedomainkey", "deactivatedomainkey"):
+ self.args["id"] = int(parts.pop(0))
+ self.args["name"] = parts.pop(0)
+ elif self.method in (
+ "adddomainkey",
+ "gettsigkey",
+ "getdomaininfo",
+ "settsigkey",
+ "deletetsigkey",
+ "getalldomainmetadata",
+ ):
+ self.args["name"] = parts.pop(0)
+ elif self.method == "setnotified":
+ self.args["id"] = int(parts.pop(0))
+ elif self.method == "feedents":
+ self.args["id"] = int(parts.pop(0))
+ self.args["trxid"] = int(parts.pop(0))
+ elif self.method == "ismaster":
+ self.args["name"] = parts.pop(0)
+ self.args["ip"] = parts.pop(0)
+ elif self.method in ("supermasterbackend", "createslavedomain"):
+ self.args["ip"] = parts.pop(0)
+ self.args["domain"] = parts.pop(0)
+ elif self.method in ("feedents3", "starttransaction"):
+ self.args["id"] = int(parts.pop(0))
+ self.args["domain"] = parts.pop(0)
+ self.args["trxid"] = int(parts.pop(0))
+ elif self.method in ("feedrecord", "committransaction", "aborttransaction"):
+ self.args["trxid"] = int(parts.pop(0))
+ elif self.method == "replacerrset":
+ self.args["id"] = int(parts.pop(0))
+ self.args["qname"] = parts.pop(0)
+ self.args["qtype"] = parts.pop(0)
assert len(parts) == 0, parts
self.parse_qsl(url.query)
k1 = m.group(1)
k2 = m.group(2)
if k1 not in res:
- if k2 == '':
+ if k2 == "":
res[k1] = list()
else:
res[k1] = {}
- if k2 == '':
+ if k2 == "":
res[k1].append(value)
- else:
+ else:
res[k1][k2] = value
else:
res[key] = value
self.args = self.args | res
def do_GET(self):
- if self.path == '/ping':
+ if self.path == "/ping":
self.send_response(200)
self.end_headers()
self.wfile.write("pong".encode())
self.do_POST()
def do_POST(self):
- self.url_to_args()
+ self.url_to_args()
if not self.method:
self.send_error(404)
try:
length = 0
- if 'content-length' in self.headers:
- length = int(self.headers.get('content-length'))
+ if "content-length" in self.headers:
+ length = int(self.headers.get("content-length"))
if length > 0:
qs = self.rfile.read(length).decode()
self.parse_qsl(qs)
if self.method == "adddomainkey":
- self.args['key'] = {
- 'flags': self.args['flags'],
- 'active': self.args['active'],
- 'published': self.args['published'],
- 'content': self.args['content']
+ self.args["key"] = {
+ "flags": self.args["flags"],
+ "active": self.args["active"],
+ "published": self.args["published"],
+ "content": self.args["content"],
}
- del self.args['flags']
- del self.args['active']
- del self.args['published']
- del self.args['content']
+ del self.args["flags"]
+ del self.args["active"]
+ del self.args["published"]
+ del self.args["content"]
- if 'serial' in self.args:
- self.args['serial'] = int(self.args['serial'])
+ if "serial" in self.args:
+ self.args["serial"] = int(self.args["serial"])
method = "do_%s" % self.method
if callable(getattr(self.handler, method, None)):
getattr(self.handler, method)(**self.args)
- result = json.dumps({'result':self.handler.result,'log':self.handler.log}).encode()
+ result = json.dumps({"result": self.handler.result, "log": self.handler.log}).encode()
self.send_response(200)
- self.send_header("content-type", "text/javascript");
+ self.send_header("content-type", "text/javascript")
self.send_header("content-length", len(result))
self.end_headers()
self.wfile.write(result)
else:
- self.send_error(404, message=json.dumps({'error': 'No such method'}))
+ self.send_error(404, message=json.dumps({"error": "No such method"}))
except BrokenPipeError as e2:
raise e2
except Exception as e:
from dnsbackend import DNSBackendHandler
import os
+
class DNSBackendServer(http.server.HTTPServer):
def __init__(self, *args, **kwargs):
path = os.path.dirname(os.path.realpath(__file__))
- self.handler = BackendHandler(options={'dbpath': os.path.join(path, 'remote.sqlite3')})
+ self.handler = BackendHandler(options={"dbpath": os.path.join(path, "remote.sqlite3")})
super().__init__(*args, **kwargs)
def finish_request(self, request, client_address):
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self, handler=self.handler)
+
def main():
- server = DNSBackendServer(('', 62434), DNSBackendHandler)
+ server = DNSBackendServer(("", 62434), DNSBackendHandler)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
+
main()
from backend import BackendHandler
import os
+
def main():
path = os.path.dirname(os.path.realpath(__file__))
- connector = PipeConnector(BackendHandler, options={'dbpath': os.path.join(path, 'remote.sqlite3'), 'rawlog':'/tmp/raw.json'})
+ connector = PipeConnector(
+ BackendHandler, options={"dbpath": os.path.join(path, "remote.sqlite3"), "rawlog": "/tmp/raw.json"}
+ )
connector.run()
+
main()
from backend import BackendHandler
import os
+
def main():
path = os.path.dirname(os.path.realpath(__file__))
- connector = PipeConnector(BackendHandler, options={'dbpath': os.path.join(path, 'remote.sqlite3'), 'rawlog':'/tmp/raw.json'})
+ connector = PipeConnector(
+ BackendHandler, options={"dbpath": os.path.join(path, "remote.sqlite3"), "rawlog": "/tmp/raw.json"}
+ )
connector.run()
+
main()
import os
from backend import BackendHandler
+
def run(socket, handler):
while True:
message = socket.recv()
try:
message = json.loads(message.decode().strip())
- method = "do_%s" % message['method'].lower()
- args = message['parameters']
+ method = "do_%s" % message["method"].lower()
+ args = message["parameters"]
handler.result = False
handler.log = []
if callable(getattr(handler, method, None)):
getattr(handler, method)(**args)
- result = json.dumps({'result': handler.result,'log': handler.log})
+ result = json.dumps({"result": handler.result, "log": handler.log})
socket.send(result.encode())
except KeyboardInterrupt as e3:
return
raise e2
except Exception as e:
print(e)
- socket.send(json.dumps({'result':False}).encode())
+ socket.send(json.dumps({"result": False}).encode())
def main():
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("ipc:///tmp/pdns.0")
- handler = BackendHandler(options={'dbpath': os.path.join(path, 'remote.sqlite3')})
+ handler = BackendHandler(options={"dbpath": os.path.join(path, "remote.sqlite3")})
try:
run(socket, handler)
pass
os.unlink("/tmp/remotebackend.0")
-
+
+
main()
from pdns_unittest import Handler
from urllib.parse import parse_qsl, urlparse, unquote
+
class DNSBackendServer(http.server.HTTPServer):
def __init__(self, *args, **kwargs):
self.handler = Handler()
class DNSBackendHandler(http.server.BaseHTTPRequestHandler):
def __init__(self, *args, **kwargs):
- self.handler = kwargs['handler']
+ self.handler = kwargs["handler"]
super().__init__(*args)
def url_to_args(self):
parts.pop(0)
self.method = None
- if parts.pop(0) != 'dns':
+ if parts.pop(0) != "dns":
return
self.method = parts.pop(0).lower()
self.args = {}
- if self.method == 'lookup':
- self.args['qname'] = parts.pop(0)
- self.args['qtype'] = parts.pop(0)
- elif self.method == 'list':
- self.args['id'] = int(parts.pop(0))
- self.args['zonename'] = parts.pop(0)
- elif self.method in ('getbeforeandafternamesabsolute', 'getbeforeandafternames'):
- self.args['id'] = int(parts.pop(0))
- self.args['qname'] = parts.pop(0)
- elif self.method in ('getdomainmetadata', 'setdomainmetadata'):
- self.args['name'] = parts.pop(0)
- self.args['kind'] = parts.pop(0)
- elif self.method == 'getdomainkeys':
- self.args['name'] = parts.pop(0)
- elif self.method in ('removedomainkey', 'activatedomainkey', 'deactivatedomainkey'):
- self.args['id'] = int(parts.pop(0))
- self.args['name'] = parts.pop(0)
- elif self.method in ('adddomainkey', 'gettsigkey', 'getdomaininfo', 'settsigkey', 'deletetsigkey', 'getalldomainmetadata'):
- self.args['name'] = parts.pop(0)
- elif self.method == 'setnotified':
- self.args['id'] = int(parts.pop(0))
- elif self.method == 'feedents':
- self.args['id'] = int(parts.pop(0))
- self.args['trxid'] = int(parts.pop(0))
- elif self.method == 'ismaster':
- self.args['name'] = parts.pop(0)
- self.args['ip'] = parts.pop(0)
- elif self.method in ('supermasterbackend', 'createslavedomain'):
- self.args['ip'] = parts.pop(0)
- self.args['domain'] = parts.pop(0)
- elif self.method in ('feedents3', 'starttransaction'):
- self.args['id'] = int(parts.pop(0))
- self.args['domain'] = parts.pop(0)
- self.args['trxid'] = int(parts.pop(0))
- elif self.method in ('feedrecord', 'committransaction', 'aborttransaction'):
- self.args['trxid'] = int(parts.pop(0))
- elif self.method == 'replacerrset':
- self.args['id'] = int(parts.pop(0))
- self.args['qname'] = parts.pop(0)
- self.args['qtype'] = parts.pop(0)
+ if self.method == "lookup":
+ self.args["qname"] = parts.pop(0)
+ self.args["qtype"] = parts.pop(0)
+ elif self.method == "list":
+ self.args["id"] = int(parts.pop(0))
+ self.args["zonename"] = parts.pop(0)
+ elif self.method in ("getbeforeandafternamesabsolute", "getbeforeandafternames"):
+ self.args["id"] = int(parts.pop(0))
+ self.args["qname"] = parts.pop(0)
+ elif self.method in ("getdomainmetadata", "setdomainmetadata"):
+ self.args["name"] = parts.pop(0)
+ self.args["kind"] = parts.pop(0)
+ elif self.method == "getdomainkeys":
+ self.args["name"] = parts.pop(0)
+ elif self.method in ("removedomainkey", "activatedomainkey", "deactivatedomainkey"):
+ self.args["id"] = int(parts.pop(0))
+ self.args["name"] = parts.pop(0)
+ elif self.method in (
+ "adddomainkey",
+ "gettsigkey",
+ "getdomaininfo",
+ "settsigkey",
+ "deletetsigkey",
+ "getalldomainmetadata",
+ ):
+ self.args["name"] = parts.pop(0)
+ elif self.method == "setnotified":
+ self.args["id"] = int(parts.pop(0))
+ elif self.method == "feedents":
+ self.args["id"] = int(parts.pop(0))
+ self.args["trxid"] = int(parts.pop(0))
+ elif self.method == "ismaster":
+ self.args["name"] = parts.pop(0)
+ self.args["ip"] = parts.pop(0)
+ elif self.method in ("supermasterbackend", "createslavedomain"):
+ self.args["ip"] = parts.pop(0)
+ self.args["domain"] = parts.pop(0)
+ elif self.method in ("feedents3", "starttransaction"):
+ self.args["id"] = int(parts.pop(0))
+ self.args["domain"] = parts.pop(0)
+ self.args["trxid"] = int(parts.pop(0))
+ elif self.method in ("feedrecord", "committransaction", "aborttransaction"):
+ self.args["trxid"] = int(parts.pop(0))
+ elif self.method == "replacerrset":
+ self.args["id"] = int(parts.pop(0))
+ self.args["qname"] = parts.pop(0)
+ self.args["qtype"] = parts.pop(0)
assert len(parts) == 0, parts
self.parse_qsl(url.query)
k1 = m.group(1)
k2 = m.group(2)
if k1 not in res:
- if k2 == '':
+ if k2 == "":
res[k1] = list()
else:
res[k1] = {}
- if k2 == '':
+ if k2 == "":
res[k1].append(value)
- else:
+ else:
res[k1][k2] = value
else:
res[key] = value
self.args = self.args | res
def do_GET(self):
- if self.path == '/ping':
+ if self.path == "/ping":
self.send_response(200)
self.end_headers()
self.wfile.write("pong".encode())
self.do_POST()
def do_POST(self):
- self.url_to_args()
+ self.url_to_args()
if not self.method:
self.send_error(404)
try:
length = 0
- if 'content-length' in self.headers:
- length = int(self.headers.get('content-length'))
+ if "content-length" in self.headers:
+ length = int(self.headers.get("content-length"))
if length > 0:
qs = self.rfile.read(length).decode()
self.parse_qsl(qs)
if self.method == "adddomainkey":
- self.args['key'] = {
- 'flags': self.args['flags'],
- 'active': self.args['active'],
- 'published': self.args['published'],
- 'content': self.args['content']
+ self.args["key"] = {
+ "flags": self.args["flags"],
+ "active": self.args["active"],
+ "published": self.args["published"],
+ "content": self.args["content"],
}
- del self.args['flags']
- del self.args['active']
- del self.args['published']
- del self.args['content']
+ del self.args["flags"]
+ del self.args["active"]
+ del self.args["published"]
+ del self.args["content"]
- if 'serial' in self.args:
- self.args['serial'] = int(self.args['serial'])
+ if "serial" in self.args:
+ self.args["serial"] = int(self.args["serial"])
self.log_error("%r", self.args)
method = "do_%s" % self.method
if callable(getattr(self.handler, method, None)):
getattr(self.handler, method)(**self.args)
- result = json.dumps({'result':self.handler.result,'log':self.handler.log}).encode()
+ result = json.dumps({"result": self.handler.result, "log": self.handler.log}).encode()
self.log_error("%r", self.handler.result)
self.send_response(200)
- self.send_header("content-type", "text/javascript");
+ self.send_header("content-type", "text/javascript")
self.send_header("content-length", len(result))
self.end_headers()
self.wfile.write(result)
else:
- self.send_error(404, message=json.dumps({'error': 'No such method'}))
+ self.send_error(404, message=json.dumps({"error": "No such method"}))
except BrokenPipeError as e2:
raise e2
except Exception as e:
def main():
- server = DNSBackendServer(('', 62434), DNSBackendHandler)
+ server = DNSBackendServer(("", 62434), DNSBackendHandler)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
+
main()
class DNSBackendHandler(http.server.BaseHTTPRequestHandler):
def __init__(self, *args, **kwargs):
- self.handler = kwargs['handler']
+ self.handler = kwargs["handler"]
super().__init__(*args)
def do_GET(self):
- if self.path == '/ping':
+ if self.path == "/ping":
self.send_response(200)
self.end_headers()
self.wfile.write("pong".encode())
return
try:
- length = int(self.headers.get('content-length'))
+ length = int(self.headers.get("content-length"))
message = json.loads(self.rfile.read(length).decode())
- method = "do_" + message['method'].lower()
- args = message['parameters']
+ method = "do_" + message["method"].lower()
+ args = message["parameters"]
self.handler.result = False
self.handler.log = []
if callable(getattr(self.handler, method, None)):
getattr(self.handler, method)(**args)
- result = json.dumps({'result':self.handler.result,'log':self.handler.log}).encode()
+ result = json.dumps({"result": self.handler.result, "log": self.handler.log}).encode()
self.send_response(200)
- self.send_header("content-type", "text/javascript");
+ self.send_header("content-type", "text/javascript")
self.send_header("content-length", len(result))
self.end_headers()
self.wfile.write(result)
else:
- self.send_error(404, message=json.dumps({'error': 'No such method'}))
+ self.send_error(404, message=json.dumps({"error": "No such method"}))
except BrokenPipeError as e2:
raise e2
except Exception as e:
def main():
- server = DNSBackendServer(('', 62434), DNSBackendHandler)
+ server = DNSBackendServer(("", 62434), DNSBackendHandler)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
+
main()
class DNSBackendHandler(http.server.BaseHTTPRequestHandler):
def __init__(self, *args, **kwargs):
- self.handler = kwargs['handler']
+ self.handler = kwargs["handler"]
super().__init__(*args)
def do_GET(self):
- if self.path == '/ping':
+ if self.path == "/ping":
self.send_response(200)
self.end_headers()
self.wfile.write("pong".encode())
def do_POST(self):
path = urlparse(self.path).path
- if not path.startswith('/dns/'):
+ if not path.startswith("/dns/"):
self.send_error(404)
return
try:
- length = int(self.headers.get('content-length'))
- args = json.loads(parse_qs(self.rfile.read(length).decode())['parameters'][0])
+ length = int(self.headers.get("content-length"))
+ args = json.loads(parse_qs(self.rfile.read(length).decode())["parameters"][0])
method = "do_%s" % path[5:].lower()
self.log_error("%r", args)
if callable(getattr(self.handler, method, None)):
getattr(self.handler, method)(**args)
- result = json.dumps({'result':self.handler.result,'log':self.handler.log}).encode()
+ result = json.dumps({"result": self.handler.result, "log": self.handler.log}).encode()
self.send_response(200)
- self.send_header("content-type", "text/javascript");
+ self.send_header("content-type", "text/javascript")
self.send_header("content-length", len(result))
self.end_headers()
self.wfile.write(result)
else:
- self.send_error(404, message=json.dumps({'error': 'No such method'}))
+ self.send_error(404, message=json.dumps({"error": "No such method"}))
except BrokenPipeError as e2:
raise e2
except Exception as e:
def main():
- server = DNSBackendServer(('', 62434), DNSBackendHandler)
+ server = DNSBackendServer(("", 62434), DNSBackendHandler)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
+
main()
import os
from pdns_unittest import Handler
+
def run(socket, handler):
while True:
message = socket.recv()
try:
message = json.loads(message.decode().strip())
- method = "do_%s" % message['method'].lower()
- args = message['parameters']
+ method = "do_%s" % message["method"].lower()
+ args = message["parameters"]
handler.result = False
handler.log = []
if callable(getattr(handler, method, None)):
getattr(handler, method)(**args)
- result = json.dumps({'result': handler.result,'log': handler.log})
+ result = json.dumps({"result": handler.result, "log": handler.log})
socket.send(result.encode())
except KeyboardInterrupt as e3:
return
raise e2
except Exception as e:
print(e)
- socket.send(json.dumps({'result':False}).encode())
+ socket.send(json.dumps({"result": False}).encode())
def main():
pass
os.unlink("/tmp/remotebackend.0")
-
+
+
main()
def get_temporary_file_for_generated_code(dest_dir):
- generated_fp = tempfile.NamedTemporaryFile(
- mode="w+t", encoding="utf-8", dir=dest_dir, delete=False
- )
- generated_fp.write(
- "// !! This file has been generated by dnsdist-rules-generator.py, do not edit by hand!!\n"
- )
+ generated_fp = tempfile.NamedTemporaryFile(mode="w+t", encoding="utf-8", dir=dest_dir, delete=False)
+ generated_fp.write("// !! This file has been generated by dnsdist-rules-generator.py, do not edit by hand!!\n")
return generated_fp
generated_fp.write(output)
output_file_name = (
- "dnsdist-response-actions-factory-generated.hh"
- if response
- else "dnsdist-actions-factory-generated.hh"
+ "dnsdist-response-actions-factory-generated.hh" if response else "dnsdist-actions-factory-generated.hh"
)
handle_generated_file(generated_fp.name, output_file_name, build_dir)
generated_fp.write(output)
output_file_name = (
- "dnsdist-lua-response-actions-generated-body.hh"
- if response
- else "dnsdist-lua-actions-generated-body.hh"
+ "dnsdist-lua-response-actions-generated-body.hh" if response else "dnsdist-lua-actions-generated-body.hh"
)
handle_generated_file(generated_fp.name, output_file_name, build_dir)
if "parameters" in selector:
output += get_cpp_parameters_definition(selector["parameters"], True)
output += ") {\n"
- output += (
- f" return std::shared_ptr<DNSRule>(dnsdist::selectors::get{name}Selector("
- )
+ output += f" return std::shared_ptr<DNSRule>(dnsdist::selectors::get{name}Selector("
if "parameters" in selector:
output += get_cpp_parameters(selector["parameters"], True)
output += "));\n"
source_dir = sys.argv[1]
build_dir = sys.argv[2]
- definitions = get_definitions_from_file(
- f"{source_dir}/dnsdist-actions-definitions.yml"
- )
+ definitions = get_definitions_from_file(f"{source_dir}/dnsdist-actions-definitions.yml")
generate_actions_factory_header(definitions, build_dir)
generate_actions_factory(definitions, build_dir)
generate_lua_actions_bindings(definitions, build_dir)
- definitions = get_definitions_from_file(
- f"{source_dir}/dnsdist-response-actions-definitions.yml"
- )
+ definitions = get_definitions_from_file(f"{source_dir}/dnsdist-response-actions-definitions.yml")
generate_actions_factory_header(definitions, build_dir, response=True)
generate_actions_factory(definitions, build_dir, response=True)
generate_lua_actions_bindings(definitions, build_dir, response=True)
- definitions = get_definitions_from_file(
- f"{source_dir}/dnsdist-selectors-definitions.yml"
- )
+ definitions = get_definitions_from_file(f"{source_dir}/dnsdist-selectors-definitions.yml")
generate_selectors_factory_header(definitions, build_dir)
generate_selectors_factory(definitions, build_dir)
generate_lua_selectors_bindings(definitions, build_dir)
return result
-def get_rust_serde_annotations(
- rust_type, default, rename, obj, field, default_functions
-):
+def get_rust_serde_annotations(rust_type, default, rename, obj, field, default_functions):
rename_value = f'rename = "{rename}", ' if rename else ""
if default is None:
if not rename_value:
return f"""#[serde({rename_value}default = "crate::{type_upper}::<{default}>::value", skip_serializing_if = "crate::if_true")]"""
if rust_type in ["String", "Vec<String>"]:
basename = obj + "_" + field
- default_functions.append(
- gen_rust_default_functions(rust_type, default, basename)
- )
+ default_functions.append(gen_rust_default_functions(rust_type, default, basename))
return f"""#[serde({rename_value}default = "crate::default_value_{basename}", skip_serializing_if = "crate::default_value_equal_{basename}")]"""
return f"""#[serde({rename_value}default = "crate::{type_upper}::<{default}>::value", skip_serializing_if = "crate::{type_upper}::<{default}>::is_equal")]"""
return f"dnsdistsettings::{rust_type}"
-def get_rust_struct_fields_from_definition(
- name, keys, default_functions, indent, special_serde_object=False
-):
+def get_rust_struct_fields_from_definition(name, keys, default_functions, indent, special_serde_object=False):
if not "parameters" in keys:
return ""
output = ""
for parameter in keys["parameters"]:
- parameter_name = (
- get_rust_field_name(parameter["name"])
- if not "rename" in parameter
- else parameter["rename"]
- )
+ parameter_name = get_rust_field_name(parameter["name"]) if not "rename" in parameter else parameter["rename"]
rust_type = parameter["type"]
if "rust-type" in parameter:
rust_type = parameter["rust-type"]
return output
-def get_rust_struct_from_definition(
- name, keys, default_functions, indent_spaces=4, special_serde_object=False
-):
+def get_rust_struct_from_definition(name, keys, default_functions, indent_spaces=4, special_serde_object=False):
if not "parameters" in keys:
return ""
obj_name = get_rust_object_name(name)
output += " }\n"
if special_serde_object or not "skip-serde" in keys or not keys["skip-serde"]:
default_functions.append(
- write_rust_default_trait_impl(
- f"{obj_name}Configuration{name_suffix}", special_serde_object
- )
+ write_rust_default_trait_impl(f"{obj_name}Configuration{name_suffix}", special_serde_object)
)
return output
"""
-def get_struct_validation_function_from_definition(
- name, parameters, special_serde_object=False
-):
+def get_struct_validation_function_from_definition(name, parameters, special_serde_object=False):
if len(parameters) == 0:
return ""
namespace = "dnsdistsettings::" if not special_serde_object else ""
fn validate(&self) -> Result<(), ValidationError> {{
"""
for parameter in parameters:
- field_name = (
- get_rust_field_name(parameter["name"])
- if parameter["name"] != "namespace"
- else "name_space"
- )
+ field_name = get_rust_field_name(parameter["name"]) if parameter["name"] != "namespace" else "name_space"
rust_type = parameter["type"]
output += get_validation_for_field(field_name, rust_type)
output += """ Ok(())
def generate_flat_settings_for_cxx(definitions, src_dir, out_file_path):
cxx_flat_settings_fp = get_temporary_file_for_generated_code(out_file_path)
- include_file(
- cxx_flat_settings_fp, src_dir + "/dnsdist-configuration-yaml-items-pre-in.cc"
- )
+ include_file(cxx_flat_settings_fp, src_dir + "/dnsdist-configuration-yaml-items-pre-in.cc")
# first we do runtime-settable settings
cxx_flat_settings_fp.write("""#if defined(HAVE_YAML_CONFIGURATION)
continue
internal_field_name = parameter["internal-field-name"]
rust_field_name = (
- get_rust_field_name(parameter["name"])
- if not "rename" in parameter
- else parameter["rename"]
- )
- default = (
- parameter["default"]
- if parameter["type"] != "String"
- else '"' + parameter["default"] + '"'
- )
- cxx_flat_settings_fp.write(
- f" if (config.{internal_field_name} == {default}) {{\n"
+ get_rust_field_name(parameter["name"]) if not "rename" in parameter else parameter["rename"]
)
+ default = parameter["default"] if parameter["type"] != "String" else '"' + parameter["default"] + '"'
+ cxx_flat_settings_fp.write(f" if (config.{internal_field_name} == {default}) {{\n")
if parameter["type"] != "String":
cxx_flat_settings_fp.write(
f" config.{internal_field_name} = yamlConfig.{category_name}.{rust_field_name};\n"
continue
internal_field_name = parameter["internal-field-name"]
rust_field_name = (
- get_rust_field_name(parameter["name"])
- if not "rename" in parameter
- else parameter["rename"]
- )
- default = (
- parameter["default"]
- if parameter["type"] != "String"
- else '"' + parameter["default"] + '"'
- )
- cxx_flat_settings_fp.write(
- f" if (config.{internal_field_name} == {default}) {{\n"
+ get_rust_field_name(parameter["name"]) if not "rename" in parameter else parameter["rename"]
)
+ default = parameter["default"] if parameter["type"] != "String" else '"' + parameter["default"] + '"'
+ cxx_flat_settings_fp.write(f" if (config.{internal_field_name} == {default}) {{\n")
if parameter["type"] != "String":
cxx_flat_settings_fp.write(
f" config.{internal_field_name} = yamlConfig.{category_name}.{rust_field_name};\n"
action_buffer += f'{indent}#[serde(default, skip_serializing_if = "crate::is_default")]\n'
action_buffer += f"{indent}name: String,\n"
- action_buffer += get_rust_struct_fields_from_definition(
- struct_name, action, default_functions, indent
- )
+ action_buffer += get_rust_struct_fields_from_definition(struct_name, action, default_functions, indent)
action_buffer += " }\n\n"
selector_buffer += f'{indent}#[serde(default, skip_serializing_if = "crate::is_default")]\n'
selector_buffer += f"{indent}name: String,\n"
- selector_buffer += get_rust_struct_fields_from_definition(
- struct_name, selector, default_functions, indent
- )
+ selector_buffer += get_rust_struct_fields_from_definition(struct_name, selector, default_functions, indent)
selector_buffer += " }\n\n"
continue
name = get_rust_object_name(action["name"])
struct_name = f"{name}{suffix}Configuration"
- parameters = (
- get_cpp_parameters("config", action["parameters"], True)
- if "parameters" in action
- else ""
- )
+ parameters = get_cpp_parameters("config", action["parameters"], True) if "parameters" in action else ""
wrappers_buffer += f"""std::shared_ptr<DNS{suffix}Wrapper> get{name}{suffix}(const {struct_name}& config)
{{
auto action = dnsdist::actions::get{name}{suffix}({parameters});
continue
name = get_rust_object_name(action["name"])
struct_name = f"{name}{suffix}Configuration"
- parameters = (
- get_cpp_parameters("config", action["parameters"], True)
- if "parameters" in action
- else ""
- )
+ parameters = get_cpp_parameters("config", action["parameters"], True) if "parameters" in action else ""
wrappers_buffer += f"""std::shared_ptr<DNS{suffix}Wrapper> get{name}{suffix}(const {struct_name}& config)
{{
auto action = dnsdist::actions::get{name}{suffix}({parameters});
continue
name = get_rust_object_name(selector["name"])
struct_name = f"{name}{suffix}Configuration"
- parameters = (
- get_cpp_parameters("config", selector["parameters"], True)
- if "parameters" in selector
- else ""
- )
+ parameters = get_cpp_parameters("config", selector["parameters"], True) if "parameters" in selector else ""
wrappers_buffer += f"""std::shared_ptr<DNS{suffix}> get{name}{suffix}(const {struct_name}& config)
{{
auto selector = dnsdist::selectors::get{name}{suffix}({parameters});
suffix = "Selector"
for selector in selectors_definitions:
name = get_rust_object_name(selector["name"])
- output_buffer += f" fn get{name}{suffix}(config: &{name}{suffix}Configuration) -> Result<SharedPtr<DNS{suffix}>>;\n"
+ output_buffer += (
+ f" fn get{name}{suffix}(config: &{name}{suffix}Configuration) -> Result<SharedPtr<DNS{suffix}>>;\n"
+ )
output_buffer += " }\n"
output.write(output_buffer)
def generate_rust_action_to_config(output, def_dir, response):
suffix = "ResponseAction" if response else "Action"
actions_definitions = get_actions_definitions(def_dir, response)
- function_name = (
- "get_one_action_from_serde"
- if not response
- else "get_one_response_action_from_serde"
- )
+ function_name = "get_one_action_from_serde" if not response else "get_one_response_action_from_serde"
enum_buffer = f"""fn {function_name}(action: &{suffix}) -> Result<dnsdistsettings::SharedDNS{suffix}, cxx::Exception> {{
match action {{
{suffix}::Default => {{}}
output.write(enum_buffer)
-def handle_structures(
- generated_fp, definitions, default_functions, validation_functions
-):
+def handle_structures(generated_fp, definitions, default_functions, validation_functions):
for definition_name, keys in definitions.items():
- generated_fp.write(
- get_rust_struct_from_definition(definition_name, keys, default_functions)
- + "\n"
- )
+ generated_fp.write(get_rust_struct_from_definition(definition_name, keys, default_functions) + "\n")
if definition_name not in [
"global",
"proto_buf_meta",
def get_temporary_file_for_generated_code(directory):
- generated_fp = tempfile.NamedTemporaryFile(
- mode="w+t", encoding="utf-8", dir=directory, delete=False
- )
- generated_fp.write(
- "// !! This file has been generated by dnsdist-settings-generator.py, do not edit by hand!!\n"
- )
+ generated_fp = tempfile.NamedTemporaryFile(mode="w+t", encoding="utf-8", dir=directory, delete=False)
+ generated_fp.write("// !! This file has been generated by dnsdist-settings-generator.py, do not edit by hand!!\n")
return generated_fp
def main():
if len(sys.argv) != 4:
- print(
- f"Usage: {sys.argv[0]} <path/to/definitions/files> <rust/output/dir> <cxx/build/root/dir>"
- )
+ print(f"Usage: {sys.argv[0]} <path/to/definitions/files> <rust/output/dir> <cxx/build/root/dir>")
sys.exit(1)
definitions_dir = sys.argv[1]
rust_dir = sys.argv[2]
cxx_build_dir = sys.argv[3]
- definitions = get_definitions_from_file(
- definitions_dir + "/dnsdist-settings-definitions.yml"
- )
+ definitions = get_definitions_from_file(definitions_dir + "/dnsdist-settings-definitions.yml")
default_functions = []
validation_functions = []
generate_actions_config(generated_fp, definitions_dir, True, default_functions)
generate_selectors_config(generated_fp, definitions_dir, default_functions)
- handle_structures(
- generated_fp, definitions, default_functions, validation_functions
- )
+ handle_structures(generated_fp, definitions, default_functions, validation_functions)
- generate_cpp_action_selector_functions_callable_from_rust(
- generated_fp, definitions_dir
- )
+ generate_cpp_action_selector_functions_callable_from_rust(generated_fp, definitions_dir)
include_file(generated_fp, f"{rust_dir}/rust-middle-in.rs")
# we are now outside of the dnsdistsettings namespace
for definition_name, keys in definitions.items():
if definition_name == "global":
generated_fp.write(
- get_rust_struct_from_definition(
- definition_name, keys, default_functions, special_serde_object=True
- )
+ get_rust_struct_from_definition(definition_name, keys, default_functions, special_serde_object=True)
+ "\n"
)
validation_functions.append(
def rust_type_to_human_str(rust_type, entry_type, generate_ref=True):
if is_vector_of(rust_type):
- return "Sequence of " + rust_type_to_human_str(
- get_vector_sub_type(rust_type), entry_type, generate_ref
- )
+ return "Sequence of " + rust_type_to_human_str(get_vector_sub_type(rust_type), entry_type, generate_ref)
if rust_type in ["u8", "u16", "u32", "u64"]:
return "Unsigned integer"
if rust_type == "f64":
return output
-def process_object(
- object_name, entries, entry_type, is_setting_struct=False, lua_equivalent=None
-):
+def process_object(object_name, entries, entry_type, is_setting_struct=False, lua_equivalent=None):
output = f".. _yaml-{entry_type}-{object_name}:\n\n"
output += f"{object_name}\n"
def get_temporary_file_for_generated_content(directory):
- generated_fp = tempfile.NamedTemporaryFile(
- mode="w+t", encoding="utf-8", dir=directory, delete=False
- )
- generated_fp.write(
- ".. THIS IS A GENERATED FILE. DO NOT EDIT. See dnsdist-settings-documentation-generator.py\n\n"
- )
+ generated_fp = tempfile.NamedTemporaryFile(mode="w+t", encoding="utf-8", dir=directory, delete=False)
+ generated_fp.write(".. THIS IS A GENERATED FILE. DO NOT EDIT. See dnsdist-settings-documentation-generator.py\n\n")
return generated_fp
lua_equivalent = object_name + ("Rule" if entry_type == "selector" else suffix)
if "no-lua-equivalent" in entry:
lua_equivalent = None
- output += process_object(
- object_name + suffix, entry, "settings", lua_equivalent=lua_equivalent
- )
+ output += process_object(object_name + suffix, entry, "settings", lua_equivalent=lua_equivalent)
return output
source_dir = sys.argv[1]
docs_folder = f"{source_dir}/docs/"
if not os.path.isdir(docs_folder):
- print(
- "Skipping settings documentation generation because the docs/ folder does not exist"
- )
+ print("Skipping settings documentation generation because the docs/ folder does not exist")
return
generated_fp = get_temporary_file_for_generated_content(docs_folder)
os.rename(generated_fp.name, f"{docs_folder}/reference/yaml-settings.rst")
generated_fp = get_temporary_file_for_generated_content(docs_folder)
- output = process_selectors_or_actions(
- f"{source_dir}/dnsdist-actions-definitions.yml", "action"
- )
+ output = process_selectors_or_actions(f"{source_dir}/dnsdist-actions-definitions.yml", "action")
generated_fp.write(output)
os.rename(generated_fp.name, f"{docs_folder}/reference/yaml-actions.rst")
generated_fp = get_temporary_file_for_generated_content(docs_folder)
- output = process_selectors_or_actions(
- f"{source_dir}/dnsdist-response-actions-definitions.yml", "response-action"
- )
+ output = process_selectors_or_actions(f"{source_dir}/dnsdist-response-actions-definitions.yml", "response-action")
generated_fp.write(output)
os.rename(generated_fp.name, f"{docs_folder}/reference/yaml-response-actions.rst")
generated_fp = get_temporary_file_for_generated_content(docs_folder)
- output = process_selectors_or_actions(
- f"{source_dir}/dnsdist-selectors-definitions.yml", "selector"
- )
+ output = process_selectors_or_actions(f"{source_dir}/dnsdist-selectors-definitions.yml", "selector")
generated_fp.write(output)
os.rename(generated_fp.name, f"{docs_folder}/reference/yaml-selectors.rst")
html_favicon = "_static/favicon.ico"
-html_sidebars = {
- "**": ["searchbox.html", "relations.html", "localtoc.html", "sourcelink.html"]
-}
+html_sidebars = {"**": ["searchbox.html", "relations.html", "localtoc.html", "sourcelink.html"]}
# -- Options for HTMLHelp output ------------------------------------------
from datetime import datetime, timedelta
import random
-logger = logging.getLogger('pdns-keyroller')
+logger = logging.getLogger("pdns-keyroller")
+
def display_keyrollerdomain_infos(zone, api):
zoneconf = keyrollerdomain.KeyrollerDomain(zone, api)
- if zoneconf.state :
+ if zoneconf.state:
if zoneconf.state.is_rolling:
timeleft = zoneconf.state.current_roll.current_step_datetime - datetime.now()
logger.info(
- '{} is rolling its {} using the {} method. It is in the step {}, which was made {}. Next step scheduled {}'.format(
- zone, zoneconf.state.current_roll.keytype.upper(),
- zoneconf.state.current_roll.rolltype, zoneconf.state.current_roll.current_step_name,
+ "{} is rolling its {} using the {} method. It is in the step {}, which was made {}. Next step scheduled {}".format(
+ zone,
+ zoneconf.state.current_roll.keytype.upper(),
+ zoneconf.state.current_roll.rolltype,
+ zoneconf.state.current_roll.current_step_name,
zoneconf.state.current_roll.step_datetimes[-1],
- "in {}".format(timeleft) if timeleft > timedelta(0) else "ASAP"
+ "in {}".format(timeleft) if timeleft > timedelta(0) else "ASAP",
)
)
else:
- logger.info('{} is not rolling. Last KSK roll was {} and the last ZSK roll was {}'.format(
- zone, zoneconf.state.last_ksk_roll_str, zoneconf.state.last_zsk_roll_str))
- else :
- logger.info('{} is not rolling'.format(zone))
+ logger.info(
+ "{} is not rolling. Last KSK roll was {} and the last ZSK roll was {}".format(
+ zone, zoneconf.state.last_ksk_roll_str, zoneconf.state.last_zsk_roll_str
+ )
+ )
+ else:
+ logger.info("{} is not rolling".format(zone))
+
-if __name__ == '__main__':
+if __name__ == "__main__":
argp = argparse.ArgumentParser(
- prog='pdns-keyroller-ctl', formatter_class=argparse.ArgumentDefaultsHelpFormatter,
- description='PowerDNS DNSSEC key-roller')
- argp.add_argument('--config', '-c', metavar='PATH', type=str, default='/etc/powerdns/pdns-keyroller.conf',
- help='Load this configuration file')
- argp.add_argument('--baseurl', '-b', required=False, metavar='BASEURL', help='The base-URL for the authoritative webserver'
- 'Overrides the one set in the config-file')
- argp.add_argument('--apikey', '-k', required=False, metavar='API-KEY', help='The key needed to access the API')
- argp.add_argument('--verbose', '-v', action='count', help='Be more verbose')
- argp.set_defaults(command='none')
+ prog="pdns-keyroller-ctl",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ description="PowerDNS DNSSEC key-roller",
+ )
+ argp.add_argument(
+ "--config",
+ "-c",
+ metavar="PATH",
+ type=str,
+ default="/etc/powerdns/pdns-keyroller.conf",
+ help="Load this configuration file",
+ )
+ argp.add_argument(
+ "--baseurl",
+ "-b",
+ required=False,
+ metavar="BASEURL",
+ help="The base-URL for the authoritative webserverOverrides the one set in the config-file",
+ )
+ argp.add_argument("--apikey", "-k", required=False, metavar="API-KEY", help="The key needed to access the API")
+ argp.add_argument("--verbose", "-v", action="count", help="Be more verbose")
+ argp.set_defaults(command="none")
sub_parsers = argp.add_subparsers()
- configs_parser = sub_parsers.add_parser('configs', help='Lists configured domains')
- configs_parser.set_defaults(command='configs', action='list')
+ configs_parser = sub_parsers.add_parser("configs", help="Lists configured domains")
+ configs_parser.set_defaults(command="configs", action="list")
configs_subparsers = configs_parser.add_subparsers()
- configs_show_parser = configs_subparsers.add_parser('show', help='Show the roll configuration of the current domain')
- configs_show_parser.set_defaults(action='show')
- configs_show_parser.add_argument('domain', metavar='DOMAIN')
-
- configs_roll_parser = configs_subparsers.add_parser('roll', help='Setup the domain for autoroll')
- configs_roll_parser.set_defaults(action='roll')
- configs_roll_parser.add_argument('domain', metavar='DOMAIN')
-
- configs_roll_parser.add_argument('--force', '-f', required=False, default=False, action="store_true", help='Force creation even if a configuration already exists')
- configs_roll_parser.add_argument('--ksk-frequency', required=False)
- configs_roll_parser.add_argument('--ksk-algo', required=False)
- configs_roll_parser.add_argument('--zsk-algo', required=False)
- configs_roll_parser.add_argument('--zsk-frequency', required=False)
-
- configs_list_parser = configs_subparsers.add_parser('list', help='List all configured domains')
- configs_list_parser.set_defaults(action='list')
-
-
+ configs_show_parser = configs_subparsers.add_parser(
+ "show", help="Show the roll configuration of the current domain"
+ )
+ configs_show_parser.set_defaults(action="show")
+ configs_show_parser.add_argument("domain", metavar="DOMAIN")
+
+ configs_roll_parser = configs_subparsers.add_parser("roll", help="Setup the domain for autoroll")
+ configs_roll_parser.set_defaults(action="roll")
+ configs_roll_parser.add_argument("domain", metavar="DOMAIN")
+
+ configs_roll_parser.add_argument(
+ "--force",
+ "-f",
+ required=False,
+ default=False,
+ action="store_true",
+ help="Force creation even if a configuration already exists",
+ )
+ configs_roll_parser.add_argument("--ksk-frequency", required=False)
+ configs_roll_parser.add_argument("--ksk-algo", required=False)
+ configs_roll_parser.add_argument("--zsk-algo", required=False)
+ configs_roll_parser.add_argument("--zsk-frequency", required=False)
+
+ configs_list_parser = configs_subparsers.add_parser("list", help="List all configured domains")
+ configs_list_parser.set_defaults(action="list")
# roll
- roll_parser = sub_parsers.add_parser('roll', help='Manipulate current rolls')
- roll_parser.set_defaults(command='roll', action='waiting')
+ roll_parser = sub_parsers.add_parser("roll", help="Manipulate current rolls")
+ roll_parser.set_defaults(command="roll", action="waiting")
roll_subparsers = roll_parser.add_subparsers()
- roll_waiting_parser = roll_subparsers.add_parser('waiting', help='List waiting zones (KSK rolls waiting for DS change)')
- roll_waiting_parser.set_defaults(action='waiting')
+ roll_waiting_parser = roll_subparsers.add_parser(
+ "waiting", help="List waiting zones (KSK rolls waiting for DS change)"
+ )
+ roll_waiting_parser.set_defaults(action="waiting")
- roll_step_parser = roll_subparsers.add_parser('step', help='Step waiting roll')
- roll_step_parser.set_defaults(action='step')
+ roll_step_parser = roll_subparsers.add_parser("step", help="Step waiting roll")
+ roll_step_parser.set_defaults(action="step")
- roll_step_parser.add_argument('domain', metavar='DOMAIN')
- roll_step_parser.add_argument('ttl', metavar='TTL')
+ roll_step_parser.add_argument("domain", metavar="DOMAIN")
+ roll_step_parser.add_argument("ttl", metavar="TTL")
arguments = argp.parse_args()
api_config = config.api()
try:
if arguments.baseurl:
- api_config['baseurl'] = arguments.baseurl
+ api_config["baseurl"] = arguments.baseurl
if arguments.apikey:
- api_config['apikey'] = arguments.apikey
+ api_config["apikey"] = arguments.apikey
api = PDNSApi(**api_config)
except ConnectionError as e:
logger.error("Unable to connect to PowerDNS: {}".format(e))
sys.exit(1)
- if arguments.command == 'none':
+ if arguments.command == "none":
argp.print_help()
sys.exit(1)
- if arguments.command == 'configs':
- if arguments.action == 'list':
+ if arguments.command == "configs":
+ if arguments.action == "list":
for zone in api.get_zones():
try:
display_keyrollerdomain_infos(zone.id, api)
continue
except Exception as e:
logger.error("Unable to get config for domain {}: {}".format(zone.id, e))
- if arguments.action == 'show':
+ if arguments.action == "show":
try:
domaincfg = domainconfig.from_api(arguments.domain, api)
logger.info(
- '{} has the following roll configuration: KSK {}, ZSK {}'.format(
+ "{} has the following roll configuration: KSK {}, ZSK {}".format(
arguments.domain,
domaincfg.ksk_frequency,
domaincfg.zsk_frequency,
except FileNotFoundError:
logger.error("{} is not under automatic keyroll".format(arguments.domain))
except ConnectionError:
- logger.error(
- 'No such domain {}'.format(
- arguments.domain
- )
- )
+ logger.error("No such domain {}".format(arguments.domain))
except Exception as e:
logger.error("Unable to get config for domain {}: {}".format(zone.id, e))
-
- if arguments.action == 'roll':
+ if arguments.action == "roll":
docreate = False
try:
domaincfg = domainconfig.from_api(arguments.domain, api)
if not arguments.force:
- logger.error(
- '{} already has an autoroll setup'.format(
- arguments.domain
- )
- )
+ logger.error("{} already has an autoroll setup".format(arguments.domain))
else:
docreate = True
except FileNotFoundError:
docreate = True
except ConnectionError:
- logger.error(
- 'No such domain {}'.format(
- arguments.domain
- )
- )
+ logger.error("No such domain {}".format(arguments.domain))
if docreate:
domaincfg = domainconfig.DomainConfig(**config.defaults())
domaincfg.zsk_algo = arguments.zsk_algo
domainconfig.to_api(arguments.domain, api, domaincfg)
logger.info(
- 'Successfully created configuration for {}: KSK {}, ZSK {}'.format(
+ "Successfully created configuration for {}: KSK {}, ZSK {}".format(
arguments.domain,
domaincfg.ksk_frequency,
domaincfg.zsk_frequency,
)
)
except SyntaxError as e:
- logger.error(
- 'Unable to setup given frequency {}: {}'.format(
- arguments.domain, e
- )
- )
- if arguments.command == 'roll':
- if arguments.action == 'waiting':
+ logger.error("Unable to setup given frequency {}: {}".format(arguments.domain, e))
+ if arguments.command == "roll":
+ if arguments.action == "waiting":
for zone in api.get_zones():
try:
zoneconf = keyrollerdomain.KeyrollerDomain(zone.id, api)
if zoneconf.state and zoneconf.state.is_rolling and zoneconf.state.current_roll.is_waiting_ds():
- logger.info('{} is waiting for DS replacement'.format(zone.id))
+ logger.info("{} is waiting for DS replacement".format(zone.id))
except FileNotFoundError:
continue
- elif arguments.action == 'step':
+ elif arguments.action == "step":
try:
zoneconf = keyrollerdomain.KeyrollerDomain(arguments.domain, api)
if zoneconf.state and zoneconf.state.current_roll.is_waiting_ds():
zoneconf.step(force=True, customttl=int(arguments.ttl))
logger.info(
- 'Successfully stepped {}, now waiting {} before deleting the keys'.format(
+ "Successfully stepped {}, now waiting {} before deleting the keys".format(
arguments.domain,
arguments.ttl,
)
)
except FileNotFoundError:
- logger.error(
- 'No such zone to step {}'.format(
- arguments.domain
- )
- )
+ logger.error("No such zone to step {}".format(arguments.domain))
import sys
import traceback
-logger = logging.getLogger('pdns-keyroller')
+logger = logging.getLogger("pdns-keyroller")
-if __name__ == '__main__':
+if __name__ == "__main__":
argp = argparse.ArgumentParser(
- prog='pdns-keyroller', formatter_class=argparse.ArgumentDefaultsHelpFormatter,
- description='PowerDNS DNSSEC key-roller daemon')
- argp.add_argument('--verbose', '-v', action='count', help='Be more verbose')
- argp.add_argument('--config', '-c', metavar='PATH', type=str, default='/etc/powerdns/pdns-keyroller.conf',
- help='Load this configuration file')
+ prog="pdns-keyroller",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ description="PowerDNS DNSSEC key-roller daemon",
+ )
+ argp.add_argument("--verbose", "-v", action="count", help="Be more verbose")
+ argp.add_argument(
+ "--config",
+ "-c",
+ metavar="PATH",
+ type=str,
+ default="/etc/powerdns/pdns-keyroller.conf",
+ help="Load this configuration file",
+ )
arguments = argp.parse_args()
try:
d = pdnskeyroller.daemon.Daemon(arguments.config)
except ConnectionError as e:
- logger.fatal('Unable to start: {}'.format(e))
+ logger.fatal("Unable to start: {}".format(e))
sys.exit(1)
try:
:return: A DNS Name that has a trailing dot
:rtype: str
"""
- if name == '.' or name == '=2E':
+ if name == "." or name == "=2E":
# lol
- return '=2E'
- if name[-1] != '.':
- return name + '.'
+ return "=2E"
+ if name[-1] != ".":
+ return name + "."
return name
TODO: We should probably try to do some caching
"""
- def __init__(self, apikey, version=1, baseurl='http://localhost:8081', server='localhost', timeout=2):
+ def __init__(self, apikey, version=1, baseurl="http://localhost:8081", server="localhost", timeout=2):
"""
:param apikey: The API Key needed to access the API (`api-key` setting)
:param version: The version of the API used, only 1 is supported at the moment
:raises: ConnectionError when the API is not reachable
"""
api_suffix = {
- 0: '',
- 1: '/api/v1',
+ 0: "",
+ 1: "/api/v1",
}[int(version)]
- url = baseurl + '{}/servers/{}'.format(api_suffix, server)
+ url = baseurl + "{}/servers/{}".format(api_suffix, server)
# Strip double (or more) slashes
- self.url = urllib.parse.urljoin(url, re.sub(r'/{2,}', '/', urllib.parse.urlparse(url).path))
+ self.url = urllib.parse.urljoin(url, re.sub(r"/{2,}", "/", urllib.parse.urlparse(url).path))
if apikey is None:
- raise Exception('apikey may not be None!')
+ raise Exception("apikey may not be None!")
self.apikey = apikey
self.timeout = timeout
self._server = server
# Test the API, raises in _do_request
- self._do_request('', 'GET')
+ self._do_request("", "GET")
def __repr__(self):
return '{}.PDNSApi(apikey="{}", version={}, baseurl="{}", server="{}", timeout={})'.format(
- __name__,
- self.apikey,
- self._version,
- self._baseurl,
- self._server,
- self.timeout
+ __name__, self.apikey, self._version, self._baseurl, self._server, self.timeout
)
def _do_request(self, uri, method, data=None):
:rtype: tuple(int, str)
"""
headers = {
- 'Accept': 'application/json',
- 'X-API-Key': self.apikey,
+ "Accept": "application/json",
+ "X-API-Key": self.apikey,
}
full_url = self.url + uri
if data is not None:
if not (isinstance(data, dict) or isinstance(data, list)):
- raise ValueError('data was passed as a {}, needs to be dict or list!'.format(type(data)))
- if method.upper() != 'GET':
- headers.update({'Content-Type': 'application/json'})
+ raise ValueError("data was passed as a {}, needs to be dict or list!".format(type(data)))
+ if method.upper() != "GET":
+ headers.update({"Content-Type": "application/json"})
- logger.debug('Attempting {} request to {} with data: {}'.format(method, full_url, data))
+ logger.debug("Attempting {} request to {} with data: {}".format(method, full_url, data))
ret = None
try:
raise ConnectionError("Unable to connect to {}: {}".format(full_url, e))
except requests.HTTPError as e:
logger.debug("Got an HTTP {} Error: {}".format(e.response.status_code, ret))
- raise ConnectionError("HTTP error code {} received for {}: {}".format(
- e.response.status_code, e.request.url, ret.get('error', ret)))
+ raise ConnectionError(
+ "HTTP error code {} received for {}: {}".format(
+ e.response.status_code, e.request.url, ret.get("error", ret)
+ )
+ )
except Exception as e:
msg = "Error doing {} request to {}: {}".format(method, full_url, e)
logger.debug(msg)
:return: All the cryptokeys for the zone
:rtype: list(CryptoKey)
"""
- code, resp = self._do_request('/zones/{}/cryptokeys'.format(_sanitize_dnsname(zone)),
- 'GET')
+ code, resp = self._do_request("/zones/{}/cryptokeys".format(_sanitize_dnsname(zone)), "GET")
if code == 200:
cryptokeys = []
for k in resp:
- k.pop('type')
+ k.pop("type")
cryptokeys.append(CryptoKey(**k))
return cryptokeys
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
def get_cryptokey(self, zone, cryptokey):
"""
if keyid == -1:
raise Exception("cryptokey is not a CryptoKey, nor a str or int")
- code, resp = self._do_request('/zones/{}/cryptokeys/{}'.format(_sanitize_dnsname(zone), keyid),
- 'GET')
+ code, resp = self._do_request("/zones/{}/cryptokeys/{}".format(_sanitize_dnsname(zone), keyid), "GET")
if code == 200:
- resp.pop('type')
+ resp.pop("type")
return CryptoKey(**resp)
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
def set_cryptokey_active(self, zone, cryptokey, active=True):
"""
if keyid == -1:
raise Exception("cryptokey is not a CryptoKey, nor a str or int")
- code, resp = self._do_request('/zones/{}/cryptokeys/{}'.format(_sanitize_dnsname(zone), keyid),
- 'PUT',
- {'active': active})
+ code, resp = self._do_request(
+ "/zones/{}/cryptokeys/{}".format(_sanitize_dnsname(zone), keyid), "PUT", {"active": active}
+ )
if code == 422:
- raise Exception('Failed to set cryptokey {} in zone {} to {}: {}'.format(
- keyid, zone, 'active' if active else 'inactive', resp))
+ raise Exception(
+ "Failed to set cryptokey {} in zone {} to {}: {}".format(
+ keyid, zone, "active" if active else "inactive", resp
+ )
+ )
if code == 204:
return self.get_cryptokey(zone, cryptokey)
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
def set_cryptokey_published(self, zone, cryptokey, published=True):
"""
if keyid == -1:
raise Exception("cryptokey is not a CryptoKey, nor a str or int")
- code, resp = self._do_request('/zones/{}/cryptokeys/{}'.format(_sanitize_dnsname(zone), keyid),
- 'PUT',
- {'published': published,
- 'active': True})
+ code, resp = self._do_request(
+ "/zones/{}/cryptokeys/{}".format(_sanitize_dnsname(zone), keyid),
+ "PUT",
+ {"published": published, "active": True},
+ )
if code == 422:
- raise Exception('Failed to set cryptokey {} in zone {} to {}: {}'.format(
- keyid, zone, 'published' if published else 'unpublished', resp))
+ raise Exception(
+ "Failed to set cryptokey {} in zone {} to {}: {}".format(
+ keyid, zone, "published" if published else "unpublished", resp
+ )
+ )
if code == 204:
return self.get_cryptokey(zone, cryptokey)
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
def publish_cryptokey(self, zone, cryptokey):
- return self.set_cryptokey_published(zone, cryptokey, published=True)
+ return self.set_cryptokey_published(zone, cryptokey, published=True)
def unpublish_cryptokey(self, zone, cryptokey):
- return self.set_cryptokey_published(zone, cryptokey, published=False)
+ return self.set_cryptokey_published(zone, cryptokey, published=False)
def delete_cryptokey(self, zone, cryptokey):
"""
keyid = cryptokey
if keyid == -1:
raise Exception("cryptokey is not a CryptoKey, nor a str or int")
- code, resp = self._do_request('/zones/{}/cryptokeys/{}'.format(_sanitize_dnsname(zone), keyid),
- 'DELETE')
+ code, resp = self._do_request("/zones/{}/cryptokeys/{}".format(_sanitize_dnsname(zone), keyid), "DELETE")
if code == 422:
- raise Exception('Failed to remove cryptokey {} in zone {}: {}'.format(
- keyid, zone, resp))
+ raise Exception("Failed to remove cryptokey {} in zone {}: {}".format(keyid, zone, resp))
if code == 204:
return
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
- def add_cryptokey(self, zone, keytype='zsk', active=False, content=None, algo=None, bits=None, published=True):
+ def add_cryptokey(self, zone, keytype="zsk", active=False, content=None, algo=None, bits=None, published=True):
"""
Adds a CryptoKey to zone. If content is None, a new key is generated by the server, using algorithm from `algo`
and a size of `bits` (if applicable). If `content` and `algo` are both None, the server default is used (in
:raises: an Exception on failure
"""
- data = {'active': active,
- 'keytype': keytype,
- 'published': published}
+ data = {"active": active, "keytype": keytype, "published": published}
if content is not None:
- data.update({'content': content})
+ data.update({"content": content})
if algo is not None:
algo = pdnsapi.cryptokey.shorthand_to_algo.get(algo, algo)
- data.update({'algorithm': algo})
+ data.update({"algorithm": algo})
if bits is not None:
- data.update({'bits': bits})
+ data.update({"bits": bits})
- code, resp = self._do_request('/zones/{}/cryptokeys'.format(_sanitize_dnsname(zone)),
- 'POST',
- data)
+ code, resp = self._do_request("/zones/{}/cryptokeys".format(_sanitize_dnsname(zone)), "POST", data)
if code == 422:
- raise Exception('Unable to create CryptoKey in zone {}: {}'.format(zone, resp))
+ raise Exception("Unable to create CryptoKey in zone {}: {}".format(zone, resp))
if code == 201:
- resp.pop('type')
+ resp.pop("type")
return CryptoKey(**resp)
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
def get_zones(self):
"""
:return: All zones ons the server
:rtype: list(:class:`pdnsapi.zone.Zone`)
"""
- code, resp = self._do_request('/zones',
- 'GET')
+ code, resp = self._do_request("/zones", "GET")
if code == 200:
return [Zone(**zone) for zone in resp]
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
def get_zone(self, zone):
"""
:param str zone: The zone we want the full contents for
:return: a :class:`pdnsapi.zone.Zone`
"""
- code, resp = self._do_request('/zones/{}'.format(_sanitize_dnsname(zone)),
- 'GET')
+ code, resp = self._do_request("/zones/{}".format(_sanitize_dnsname(zone)), "GET")
if code == 200:
return Zone(**resp)
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
def bump_soa(self, zone, serial=None):
"""
soa = None
content = self.get_zone(zone)
for rrset in content.rrsets:
- if rrset.rtype == "SOA" :
+ if rrset.rtype == "SOA":
soa = rrset
break
if soa is None:
- raise Exception('No such SOA record')
+ raise Exception("No such SOA record")
-
newcontent = soa.records[0].content.split(" ")
if serial != None:
newcontent[2] = serial
else:
newcontent[2] = str(int(newcontent[2]) + 1)
- code, resp = self._do_request('/zones/{}'.format(_sanitize_dnsname(zone)),
- 'PATCH',
- {
- "rrsets": [{
- "name": soa.name,
- "type": soa.rtype,
- "ttl": soa.ttl,
- "changetype": "REPLACE",
- "records": [
- {
- "content": " ".join(newcontent),
- "disabled": soa.records[0].disabled
- }
- ]
- }]
- })
+ code, resp = self._do_request(
+ "/zones/{}".format(_sanitize_dnsname(zone)),
+ "PATCH",
+ {
+ "rrsets": [
+ {
+ "name": soa.name,
+ "type": soa.rtype,
+ "ttl": soa.ttl,
+ "changetype": "REPLACE",
+ "records": [{"content": " ".join(newcontent), "disabled": soa.records[0].disabled}],
+ }
+ ]
+ },
+ )
if code == 204:
return self.get_zone(zone)
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
def set_zone_param(self, zone, param, value):
"""
:return:
"""
zonename = _sanitize_dnsname(zone)
- code, resp = self._do_request('/zones/{}'.format(zonename),
- 'PUT', {param: value})
+ code, resp = self._do_request("/zones/{}".format(zonename), "PUT", {param: value})
if code == 204:
return self.get_zone(zonename)
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
- def get_zone_metadata(self, zone, kind=''):
+ def get_zone_metadata(self, zone, kind=""):
"""
Gets zone metadata
:param kind: The zone metadata kind to retrieve. If this is an empty string, all zone metadata is retrieved
:return: A list of :class:`pdnsapi.metadata.ZoneMetadata` objects
"""
- code, resp = self._do_request('/zones/{}/metadata{}'.format(_sanitize_dnsname(zone), '/' + kind if len(kind) else ''),
- 'GET')
+ code, resp = self._do_request(
+ "/zones/{}/metadata{}".format(_sanitize_dnsname(zone), "/" + kind if len(kind) else ""), "GET"
+ )
if code == 200:
- if kind == '':
- return [ZoneMetadata(r['kind'], r['metadata']) for r in resp]
+ if kind == "":
+ return [ZoneMetadata(r["kind"], r["metadata"]) for r in resp]
else:
- return ZoneMetadata(resp['kind'], resp['metadata'])
+ return ZoneMetadata(resp["kind"], resp["metadata"])
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
def set_zone_metadata(self, zone, kind, metadata):
if not isinstance(metadata, list):
metadata = [metadata]
- obj = {'metadata': metadata}
- code, resp = self._do_request('/zones/{}/metadata/{}'.format(_sanitize_dnsname(zone), kind),
- 'PUT',
- obj)
+ obj = {"metadata": metadata}
+ code, resp = self._do_request("/zones/{}/metadata/{}".format(_sanitize_dnsname(zone), kind), "PUT", obj)
if code == 422:
- raise Exception('Failed to set metadata {} in zone {} to {}: {}'.format(kind, zone, metadata, resp))
+ raise Exception("Failed to set metadata {} in zone {} to {}: {}".format(kind, zone, metadata, resp))
if code == 200:
- return ZoneMetadata(resp['kind'], resp['metadata'])
+ return ZoneMetadata(resp["kind"], resp["metadata"])
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
def delete_zone_metadata(self, zone, kind):
- code, resp = self._do_request('/zones/{}/metadata/{}'.format(_sanitize_dnsname(zone), kind),
- 'DELETE')
+ code, resp = self._do_request("/zones/{}/metadata/{}".format(_sanitize_dnsname(zone), kind), "DELETE")
if code == 422:
- raise Exception('Failed to remove metadata {} in zone {}: {}'.format(kind, zone, resp))
+ raise Exception("Failed to remove metadata {} in zone {}: {}".format(kind, zone, resp))
if code == 200:
return
- raise Exception('Unexpected response: {}: {}'.format(code, resp))
+ raise Exception("Unexpected response: {}: {}".format(code, resp))
shorthand_to_algo = {v: k for k, v in algo_to_shorthand.items()}
-algo_to_bits = {
- 13: 256,
- 14: 512,
- 15: 32,
- 16: 57.
-}
+algo_to_bits = {13: 256, 14: 512, 15: 32, 16: 57.0}
class CryptoKey:
"""
Represents a CryptoKey from the API
"""
+
_algo = None
def __init__(self, id, active, keytype, flags=None, algo=None, dnskey=None, ds=None, privatekey=None, **kwargs):
self.dnskey = dnskey
self.ds = ds
self.privatekey = privatekey
- self.algo = algo or dnskey.split(' ')[2]
+ self.algo = algo or dnskey.split(" ")[2]
def __repr__(self):
return 'CryptoKey({id}, {active}, {keytype}, {flags}, {algo}, {dnskey}, {ds}, "{privatekey})'.format(
- id=self.id, active=self.active, keytype=self.keytype, flags=self.flags, algo=self.algo, dnskey=self.dnskey,
- ds=self.ds, privatekey=self.privatekey)
+ id=self.id,
+ active=self.active,
+ keytype=self.keytype,
+ flags=self.flags,
+ algo=self.algo,
+ dnskey=self.dnskey,
+ ds=self.ds,
+ privatekey=self.privatekey,
+ )
def __str__(self):
- return str({
- 'id': self.id,
- 'active': self.active,
- 'keytype': self.keytype,
- 'flags': self.flags,
- 'dnskey': self.dnskey,
- 'ds': self.ds,
- 'privatekey': self.privatekey,
- 'algo': self.algo,
- })
+ return str(
+ {
+ "id": self.id,
+ "active": self.active,
+ "keytype": self.keytype,
+ "flags": self.flags,
+ "dnskey": self.dnskey,
+ "ds": self.ds,
+ "privatekey": self.privatekey,
+ "algo": self.algo,
+ }
+ )
@property
def algo(self):
self.algo = shorthand_to_algo.get(val, val)
return
raise ValueError("Value is not a str or int, but a {}".format(type(val)))
-
def __init__(self, kind, metadata):
self.kind = kind
if not isinstance(metadata, list):
- raise Exception('metadata must be a list, not a {}'.format(type(metadata)))
+ raise Exception("metadata must be a list, not a {}".format(type(metadata)))
self.metadata = metadata
def empty(self):
return not self.metadata
def __repr__(self):
- return 'ZoneMetadata({}, {})'.format(self.kind, self.metadata)
+ return "ZoneMetadata({}, {})".format(self.kind, self.metadata)
def __str__(self):
- return str({
- 'kind': self.kind,
- 'metadata': self.metadata
- })
+ return str({"kind": self.kind, "metadata": self.metadata})
return 'RRSet("{}", "{}", {}, {}, {})'.format(self.name, self.rtype, self.ttl, self.records, self.comments)
def __str__(self):
- ret = '\n'.join(['; {}'.format(c) for c in self.comments])
- ret += '\n'.join(['{}{}\tIN\t{}\t{}'.format(';' if rec.disabled else '', self.name, self.rtype, rec.content)
- for rec in self.records])
+ ret = "\n".join(["; {}".format(c) for c in self.comments])
+ ret += "\n".join(
+ [
+ "{}{}\tIN\t{}\t{}".format(";" if rec.disabled else "", self.name, self.rtype, rec.content)
+ for rec in self.records
+ ]
+ )
return ret
@property
@records.setter
def records(self, val):
if not isinstance(val, list):
- raise Exception('TODO')
+ raise Exception("TODO")
if all(isinstance(v, dict) for v in val):
self._records = []
for v in val:
self._records.append(Record(**v))
return
if not all(isinstance(v, Record) for v in val):
- raise Exception('Not all records are of type Record')
+ raise Exception("Not all records are of type Record")
self._records = val
@property
@comments.setter
def comments(self, val):
if not isinstance(val, list):
- raise Exception('TODO')
+ raise Exception("TODO")
if all(isinstance(v, dict) for v in val):
self._comments = []
for v in val:
self._comments.append(Comment(**v))
return
if not all(isinstance(v, Comment) for v in val):
- raise Exception('Not all comments are of type Comment')
+ raise Exception("Not all comments are of type Comment")
self._comments = val
return 'Comment("{}", "{}", "{})'.format(self.content, self.modified_at, self.account)
def __str__(self):
- return '{} by {} on {}'.format(self.content, self.account, self.modified_at)
+ return "{} by {} on {}".format(self.content, self.account, self.modified_at)
class Zone:
"""
This represents a Zone-object
"""
- _keys = ["id", "name", "url", "kind", "serial", "notified_serial", "masters", "dnssec", "nsec3param",
- "nsec3narrow", "presigned", "soa_edit", "soa_edit_api", "account", "nameservers", "servers",
- "recursion_desired", "rrsets", "last_check"]
+
+ _keys = [
+ "id",
+ "name",
+ "url",
+ "kind",
+ "serial",
+ "notified_serial",
+ "masters",
+ "dnssec",
+ "nsec3param",
+ "nsec3narrow",
+ "presigned",
+ "soa_edit",
+ "soa_edit_api",
+ "account",
+ "nameservers",
+ "servers",
+ "recursion_desired",
+ "rrsets",
+ "last_check",
+ ]
_rrsets = []
- _kind = ''
+ _kind = ""
def __init__(self, **kwargs):
"""
setattr(self, k, v)
def __str__(self):
- ret = "{}".format('\n'.join(['; {} = {}'.format(
- k, str(getattr(self, k))) for k in Zone._keys if getattr(self, k, None) and k != 'rrsets']))
- ret += "\n{}".format('\n'.join([str(v) for v in self.rrsets]))
+ ret = "{}".format(
+ "\n".join(
+ [
+ "; {} = {}".format(k, str(getattr(self, k)))
+ for k in Zone._keys
+ if getattr(self, k, None) and k != "rrsets"
+ ]
+ )
+ )
+ ret += "\n{}".format("\n".join([str(v) for v in self.rrsets]))
return ret
def __repr__(self):
- return 'Zone({})'.format(
- ', '.join(['{}="{}"'.format(k, getattr(self, k)) for k in Zone._keys if getattr(self, k, None)]))
+ return "Zone({})".format(
+ ", ".join(['{}="{}"'.format(k, getattr(self, k)) for k in Zone._keys if getattr(self, k, None)])
+ )
@property
def kind(self):
@kind.setter
def kind(self, val):
- if val not in ['Native', 'Master', 'Slave']:
+ if val not in ["Native", "Master", "Slave"]:
raise Exception("TODO")
self._kind = val
:return:
"""
if not isinstance(val, list):
- raise Exception('Please pass a list of RRSets')
+ raise Exception("Please pass a list of RRSets")
if all(isinstance(v, dict) for v in val):
self._rrsets = []
for v in val:
self._rrsets.append(RRSet(**v))
return
if not all(isinstance(v, RRSet) for v in val):
- raise Exception('Not all rrsets are actually RRSets')
+ raise Exception("Not all rrsets are actually RRSets")
self._rrsets = val
-__author__ = 'PowerDNS.COM BV'
+__author__ = "PowerDNS.COM BV"
-PDNSKEYROLLER_CONFIG_metadata_kind = 'X-PDNSKEYROLLER-CONFIG'
-PDNSKEYROLLER_STATE_metadata_kind = 'X-PDNSKEYROLLER-STATE'
+PDNSKEYROLLER_CONFIG_metadata_kind = "X-PDNSKEYROLLER-CONFIG"
+PDNSKEYROLLER_STATE_metadata_kind = "X-PDNSKEYROLLER-STATE"
def _load_config(self):
# These are all the Defaults
tmp_conf = {
- 'keyroller': {
- 'loglevel': 'info',
+ "keyroller": {
+ "loglevel": "info",
},
- 'API': {
- 'version': 1,
- 'baseurl': 'http://localhost:8081',
- 'server': 'localhost',
- 'apikey': '',
- 'timeout': '2',
+ "API": {
+ "version": 1,
+ "baseurl": "http://localhost:8081",
+ "server": "localhost",
+ "apikey": "",
+ "timeout": "2",
},
- 'domain_defaults': {
- 'ksk_frequency': 0,
- 'ksk_algo': 13,
- 'ksk_method': 'prepublish',
- 'zsk_frequency': '6w',
- 'zsk_algo': 13,
- 'zsk_method': 'prepublish',
- 'key_style': 'single',
- 'ksk_keysize': 3069,
- 'zsk_keysize': 3069,
+ "domain_defaults": {
+ "ksk_frequency": 0,
+ "ksk_algo": 13,
+ "ksk_method": "prepublish",
+ "zsk_frequency": "6w",
+ "zsk_algo": 13,
+ "zsk_method": "prepublish",
+ "key_style": "single",
+ "ksk_keysize": 3069,
+ "zsk_keysize": 3069,
},
}
logger.debug("Loading configuration from {}".format(self._configfile))
try:
- with open(self._configfile, 'r') as f:
+ with open(self._configfile, "r") as f:
a = yaml.safe_load(f)
if a:
for k, v in tmp_conf.items():
if isinstance(v, list) and isinstance(a.get(k), list):
tmp_conf[k] = a.get(k)
- loglevel = getattr(logging, tmp_conf['keyroller']['loglevel'].upper())
+ loglevel = getattr(logging, tmp_conf["keyroller"]["loglevel"].upper())
if not isinstance(loglevel, int):
loglevel = logging.INFO
logger.info("Setting loglevel to {}".format(loglevel))
logging.basicConfig(level=loglevel)
except FileNotFoundError as e:
- logger.error('Unable to load configuration file: {}'.format(e))
+ logger.error("Unable to load configuration file: {}".format(e))
return tmp_conf
def api(self):
- return self._config['API']
+ return self._config["API"]
def defaults(self):
- return self._config['domain_defaults']
+ return self._config["domain_defaults"]
# Initialize all domains
self._domains = {}
- api = PDNSApi(**self._config['API'])
+ api = PDNSApi(**self._config["API"])
for zone in api.get_zones():
try:
zoneconf = pdnskeyroller.keyrollerdomain.KeyrollerDomain(zone.id, api)
def _load_config(self):
# These are all the Defaults
tmp_conf = {
- 'keyroller': {
- 'loglevel': 'info',
+ "keyroller": {
+ "loglevel": "info",
},
- 'API': {
- 'version': 1,
- 'baseurl': 'http://localhost:8081',
- 'server': 'localhost',
- 'apikey': '',
- 'timeout': '2',
+ "API": {
+ "version": 1,
+ "baseurl": "http://localhost:8081",
+ "server": "localhost",
+ "apikey": "",
+ "timeout": "2",
},
}
logger.debug("Loading configuration from {}".format(self._configfile))
try:
- with open(self._configfile, 'r') as f:
+ with open(self._configfile, "r") as f:
a = yaml.safe_load(f)
if a:
for k, v in tmp_conf.items():
if isinstance(v, list) and isinstance(a.get(k), list):
tmp_conf[k] = a.get(k)
- loglevel = getattr(logging, tmp_conf['keyroller']['loglevel'].upper())
+ loglevel = getattr(logging, tmp_conf["keyroller"]["loglevel"].upper())
if not isinstance(loglevel, int):
loglevel = logging.INFO
logger.info("Setting loglevel to {}".format(loglevel))
logging.basicConfig(level=loglevel)
except FileNotFoundError as e:
- logger.error('Unable to load configuration file: {}'.format(e))
+ logger.error("Unable to load configuration file: {}".format(e))
return tmp_conf
def _get_actionable_domains(self):
now = datetime.datetime.now()
- return [zone for zone, domainconf in self._domains.items() if
- domainconf.next_action_datetime and domainconf.next_action_datetime <= now]
+ return [
+ zone
+ for zone, domainconf in self._domains.items()
+ if domainconf.next_action_datetime and domainconf.next_action_datetime <= now
+ ]
def update_config(self):
"""
now = datetime.datetime.now()
logger.debug("Found {} domain(s) ({} actionable)".format(len(self._domains), len(actionable_domains)))
-
if len(actionable_domains) > 0:
for domain in actionable_domains:
keyrollerdomain = self._domains[domain]
if keyrollerdomain.state.is_rolling:
try:
- logger.info("Moving to step {} for {} roll".format(keyrollerdomain.current_step_name, keyrollerdomain.zone))
+ logger.info(
+ "Moving to step {} for {} roll".format(
+ keyrollerdomain.current_step_name, keyrollerdomain.zone
+ )
+ )
keyrollerdomain.step()
except Exception as e:
logger.error("Unable to advance keyroll: {}".format(e))
next_zsk_roll = keyrollerdomain.next_zsk_roll()
if next_zsk_roll is not None and next_zsk_roll <= now:
try:
- logger.info("Starting {} {} keyroll for {} ({} algo)".format("pre-publish", "ZSK", keyrollerdomain.zone, keyrollerdomain.config.zsk_algo))
+ logger.info(
+ "Starting {} {} keyroll for {} ({} algo)".format(
+ "pre-publish", "ZSK", keyrollerdomain.zone, keyrollerdomain.config.zsk_algo
+ )
+ )
roll = PrePublishKeyRoll()
- roll.initiate(keyrollerdomain.zone, keyrollerdomain.api, 'zsk', keyrollerdomain.config.zsk_algo)
+ roll.initiate(
+ keyrollerdomain.zone, keyrollerdomain.api, "zsk", keyrollerdomain.config.zsk_algo
+ )
keyrollerdomain.state.current_roll = roll
domainstate.to_api(keyrollerdomain.zone, keyrollerdomain.api, keyrollerdomain.state)
except Exception as e:
logger.error("Unable to start keyroll: {}".format(e))
elif next_ksk_roll is not None and next_ksk_roll <= now:
try:
- logger.info("Starting {} {} keyroll for {} ({} algo)".format("pre-publish", "KSK", keyrollerdomain.zone, keyrollerdomain.config.zsk_algo))
+ logger.info(
+ "Starting {} {} keyroll for {} ({} algo)".format(
+ "pre-publish", "KSK", keyrollerdomain.zone, keyrollerdomain.config.zsk_algo
+ )
+ )
roll = PrePublishKeyRoll()
- roll.initiate(keyrollerdomain.zone, keyrollerdomain.api, 'ksk', keyrollerdomain.config.ksk_algo)
+ roll.initiate(
+ keyrollerdomain.zone, keyrollerdomain.api, "ksk", keyrollerdomain.config.ksk_algo
+ )
keyrollerdomain.state.current_roll = roll
domainstate.to_api(keyrollerdomain.zone, keyrollerdomain.api, keyrollerdomain.state)
except Exception as e:
from pytimeparse.timeparse import timeparse
import pdnsapi.metadata
import json_tricks.nonp as json_tricks
-from pdnskeyroller.util import (parse_algo)
+from pdnskeyroller.util import parse_algo
DOMAINCONFIG_VERSION = 1
+
def from_api(zone, api):
"""
Retrieves a keyroller configuration for zone ``zone`` from the api ``api``
:raises: FileNotFoundError if ``zone`` does not have a roller config
"""
if not isinstance(api, pdnsapi.api.PDNSApi):
- raise Exception('api is not a PDNSApi')
+ raise Exception("api is not a PDNSApi")
metadata = api.get_zone_metadata(zone, PDNSKEYROLLER_CONFIG_metadata_kind)
raise FileNotFoundError
if len(metadata.metadata) > 1:
- raise Exception("More than one {} Domain Metadata found for {}!".format(PDNSKEYROLLER_CONFIG_metadata_kind,
- zone))
+ raise Exception(
+ "More than one {} Domain Metadata found for {}!".format(PDNSKEYROLLER_CONFIG_metadata_kind, zone)
+ )
try:
state = json_tricks.loads(metadata.metadata[0])
except Exception as e:
return DomainConfig(**state)
+
def to_api(zone, api, config):
"""
:return:
"""
if not isinstance(api, pdnsapi.api.PDNSApi):
- raise Exception('api must be a PDNSApi instance, not a {}'.format(type(api)))
+ raise Exception("api must be a PDNSApi instance, not a {}".format(type(api)))
if not isinstance(config, DomainConfig):
- raise Exception('config must be a DomainConfig instance, not a {}'.format(type(config)))
+ raise Exception("config must be a DomainConfig instance, not a {}".format(type(config)))
api.set_zone_metadata(zone, PDNSKEYROLLER_CONFIG_metadata_kind, str(config))
__zsk_method = "prepublish"
__key_style = "split"
- def __init__(self, version=DOMAINCONFIG_VERSION, ksk_frequency=0, ksk_algo=13, ksk_keysize=3096, ksk_method="prepublish",
- zsk_frequency="6w", zsk_algo=13, zsk_keysize=3096, zsk_method="prepublish", key_style="split", **kwargs):
+ def __init__(
+ self,
+ version=DOMAINCONFIG_VERSION,
+ ksk_frequency=0,
+ ksk_algo=13,
+ ksk_keysize=3096,
+ ksk_method="prepublish",
+ zsk_frequency="6w",
+ zsk_algo=13,
+ zsk_keysize=3096,
+ zsk_method="prepublish",
+ key_style="split",
+ **kwargs,
+ ):
self.version = version
self.key_style = key_style
if kwargs:
- logger.warning('Unknown keys passed: {}'.format(', '.join(
- [k for k, v in kwargs.items()])))
+ logger.warning("Unknown keys passed: {}".format(", ".join([k for k, v in kwargs.items()])))
@property
def ksk_frequency(self):
@key_style.setter
def key_style(self, value):
- if value not in ('single', 'split'):
- raise Exception('Invalid key_style: {}'. format(value))
+ if value not in ("single", "split"):
+ raise Exception("Invalid key_style: {}".format(value))
self.__key_style = value
@property
@version.setter
def version(self, val):
if val != 1:
- raise Exception('{} is not a valid version!')
+ raise Exception("{} is not a valid version!")
self.__version = val
def __repr__(self):
- return 'DomainConfig({})'.format(
- ', '.join(['{} = "{}"'.format(k, self.__getattribute__(k)) for k in
- ["version", "ksk_frequency", "ksk_algo", "ksk_keysize", "ksk_method", "zsk_frequency",
- "zsk_algo", "zsk_keysize", "zsk_method", "key_style"]]))
+ return "DomainConfig({})".format(
+ ", ".join(
+ [
+ '{} = "{}"'.format(k, self.__getattribute__(k))
+ for k in [
+ "version",
+ "ksk_frequency",
+ "ksk_algo",
+ "ksk_keysize",
+ "ksk_method",
+ "zsk_frequency",
+ "zsk_algo",
+ "zsk_keysize",
+ "zsk_method",
+ "key_style",
+ ]
+ ]
+ )
+ )
+
def __str__(self):
- return(json_tricks.dumps({
- 'version': self.version,
- 'ksk_frequency': self.ksk_frequency,
- 'ksk_algo': self.ksk_algo,
- 'ksk_keysize': self.ksk_keysize,
- 'ksk_method': self.ksk_method,
- 'zsk_frequency': self.zsk_frequency,
- 'zsk_algo': self.zsk_algo,
- 'zsk_keysize': self.zsk_keysize,
- 'zsk_method': self.zsk_method,
- 'key_style': self.key_style,
- }))
+ return json_tricks.dumps(
+ {
+ "version": self.version,
+ "ksk_frequency": self.ksk_frequency,
+ "ksk_algo": self.ksk_algo,
+ "ksk_keysize": self.ksk_keysize,
+ "ksk_method": self.ksk_method,
+ "zsk_frequency": self.zsk_frequency,
+ "zsk_algo": self.zsk_algo,
+ "zsk_keysize": self.zsk_keysize,
+ "zsk_method": self.zsk_method,
+ "key_style": self.key_style,
+ }
+ )
:raises: ValueError if the JSON from the domain metadata cannot be unpacked
"""
if not isinstance(api, pdnsapi.api.PDNSApi):
- raise Exception('api must be a PDNSApi instance, not a {}'.format(type(api)))
+ raise Exception("api must be a PDNSApi instance, not a {}".format(type(api)))
tmp_state = api.get_zone_metadata(zone, PDNSKEYROLLER_STATE_metadata_kind).metadata
if not tmp_state:
return DomainState()
if len(tmp_state) > 1:
- raise Exception('More than one {} metadata found!'.format(PDNSKEYROLLER_STATE_metadata_kind))
+ raise Exception("More than one {} metadata found!".format(PDNSKEYROLLER_STATE_metadata_kind))
try:
state = json_tricks.loads(tmp_state[0])
:return:
"""
if not isinstance(api, pdnsapi.api.PDNSApi):
- raise Exception('api must be a PDNSApi instance, not a {}'.format(type(api)))
+ raise Exception("api must be a PDNSApi instance, not a {}".format(type(api)))
if not isinstance(state, DomainState):
- raise Exception('state must be a DomainState instance, not a {}'.format(type(state)))
+ raise Exception("state must be a DomainState instance, not a {}".format(type(state)))
if state.current_roll.complete:
state.set_last_roll_date(state.current_roll.keytype, state.current_roll.step_datetimes[-1])
__current_roll = None
__version = DOMAINSTATE_VERSION
- def __init__(self, version=DOMAINSTATE_VERSION, last_ksk_roll_datetime=datetime.min,
- last_zsk_roll_datetime=datetime.min, current_roll=KeyRoll(), **kwargs):
+ def __init__(
+ self,
+ version=DOMAINSTATE_VERSION,
+ last_ksk_roll_datetime=datetime.min,
+ last_zsk_roll_datetime=datetime.min,
+ current_roll=KeyRoll(),
+ **kwargs,
+ ):
self.version = version
- self.last_ksk_roll_datetime = last_ksk_roll_datetime if isinstance(last_ksk_roll_datetime, datetime) else datetime.fromtimestamp(last_ksk_roll_datetime)
- self.last_zsk_roll_datetime = last_zsk_roll_datetime if isinstance(last_zsk_roll_datetime, datetime) else datetime.fromtimestamp(last_zsk_roll_datetime)
+ self.last_ksk_roll_datetime = (
+ last_ksk_roll_datetime
+ if isinstance(last_ksk_roll_datetime, datetime)
+ else datetime.fromtimestamp(last_ksk_roll_datetime)
+ )
+ self.last_zsk_roll_datetime = (
+ last_zsk_roll_datetime
+ if isinstance(last_zsk_roll_datetime, datetime)
+ else datetime.fromtimestamp(last_zsk_roll_datetime)
+ )
self.current_roll = current_roll
if kwargs:
- logger.warning('Unknown keys passed: {}'.format(', '.join(
- [k for k, v in kwargs.items()])))
+ logger.warning("Unknown keys passed: {}".format(", ".join([k for k, v in kwargs.items()])))
@property
def last_zsk_roll_datetime(self):
@last_zsk_roll_datetime.setter
def last_zsk_roll_datetime(self, val):
if not isinstance(val, datetime):
- raise Exception('Can not set last_zsk_roll_datetime: not a datetime object')
+ raise Exception("Can not set last_zsk_roll_datetime: not a datetime object")
self.__last_zsk_roll_datetime = val
@property
@last_ksk_roll_datetime.setter
def last_ksk_roll_datetime(self, val):
if not isinstance(val, datetime):
- raise Exception('Can not set last_ksk_roll_datetime: not a datetime object')
+ raise Exception("Can not set last_ksk_roll_datetime: not a datetime object")
self.__last_ksk_roll_datetime = val
@property
def last_ksk_roll_str(self):
- return "never" if self.last_ksk_roll_datetime == datetime.min else \
- str(self.last_ksk_roll_datetime)
+ return "never" if self.last_ksk_roll_datetime == datetime.min else str(self.last_ksk_roll_datetime)
+
@property
def last_zsk_roll_str(self):
- return "never" if self.last_zsk_roll_datetime == datetime.min else \
- str(self.last_zsk_roll_datetime)
+ return "never" if self.last_zsk_roll_datetime == datetime.min else str(self.last_zsk_roll_datetime)
@property
def current_roll(self):
@current_roll.setter
def current_roll(self, val):
if not isinstance(val, (KeyRoll, PrePublishKeyRoll)):
- raise Exception('Roll is not a KeyRoll')
+ raise Exception("Roll is not a KeyRoll")
self.__current_roll = val
@property
@version.setter
def version(self, val):
if val != 1:
- raise Exception('{} is not a valid version!')
+ raise Exception("{} is not a valid version!")
self.__version = val
def __repr__(self):
- return 'DomainState({})'.format(
- ', '.join(['{}={}'.format(k, v) for k, v in [
- ('version', self.version),
- ('last_ksk_roll_datetime', self.last_ksk_roll_datetime.timestamp() if self.last_ksk_roll_datetime > datetime.fromtimestamp(0) else 0),
- ('last_zsk_roll_datetime', self.last_zsk_roll_datetime.timestamp() if self.last_zsk_roll_datetime > datetime.fromtimestamp(0) else 0),
- ('current_roll', self.current_roll),
- ]])
+ return "DomainState({})".format(
+ ", ".join(
+ [
+ "{}={}".format(k, v)
+ for k, v in [
+ ("version", self.version),
+ (
+ "last_ksk_roll_datetime",
+ self.last_ksk_roll_datetime.timestamp()
+ if self.last_ksk_roll_datetime > datetime.fromtimestamp(0)
+ else 0,
+ ),
+ (
+ "last_zsk_roll_datetime",
+ self.last_zsk_roll_datetime.timestamp()
+ if self.last_zsk_roll_datetime > datetime.fromtimestamp(0)
+ else 0,
+ ),
+ ("current_roll", self.current_roll),
+ ]
+ ]
+ )
)
def __str__(self):
- return(json_tricks.dumps({
- 'version': self.version,
- 'last_ksk_roll_datetime': self.last_ksk_roll_datetime.timestamp() if self.last_ksk_roll_datetime > datetime.fromtimestamp(0) else 0,
- 'last_zsk_roll_datetime': self.last_zsk_roll_datetime.timestamp() if self.last_zsk_roll_datetime > datetime.fromtimestamp(0) else 0,
- 'current_roll': self.current_roll,
- }))
+ return json_tricks.dumps(
+ {
+ "version": self.version,
+ "last_ksk_roll_datetime": self.last_ksk_roll_datetime.timestamp()
+ if self.last_ksk_roll_datetime > datetime.fromtimestamp(0)
+ else 0,
+ "last_zsk_roll_datetime": self.last_zsk_roll_datetime.timestamp()
+ if self.last_zsk_roll_datetime > datetime.fromtimestamp(0)
+ else 0,
+ "current_roll": self.current_roll,
+ }
+ )
def set_last_roll_date(self, keytype, date):
- self.__setattr__('last_{}_roll_datetime'.format(keytype), date)
+ self.__setattr__("last_{}_roll_datetime".format(keytype), date)
def last_roll_date(self, keytype):
- return self.__getattribute__('last_{}_roll_datetime'.format(keytype))
+ return self.__getattribute__("last_{}_roll_datetime".format(keytype))
@property
def is_rolling(self):
class KeyRoll:
def __init__(self, **kwargs):
- self.rolltype = kwargs.get('rolltype')
+ self.rolltype = kwargs.get("rolltype")
self.complete = False
def initiate(self, zone, api, **kwargs):
raise NotImplementedError()
def __str__(self):
- return ''
+ return ""
def __repr__(self):
raise NotImplementedError()
logger = logging.getLogger(__name__)
+
class KeyrollerDomain:
def __init__(self, zone, api, config=None, state=None):
if not isinstance(api, PDNSApi):
- raise Exception('api is not a PDNSApi')
+ raise Exception("api is not a PDNSApi")
self.zone = zone
self.api = api
config = pdnskeyroller.domainconfig.from_api(zone, api)
if not isinstance(config, pdnskeyroller.domainconfig.DomainConfig):
- raise Exception('config is not a DomainConfig')
+ raise Exception("config is not a DomainConfig")
self.config = config
state = pdnskeyroller.domainstate.from_api(zone, api)
if not isinstance(state, pdnskeyroller.domainstate.DomainState):
- raise Exception('state is not a DomainState')
+ raise Exception("state is not a DomainState")
self.state = state
def next_ksk_roll(self):
if not self.state.is_rolling:
- if self.config.ksk_frequency != 0 :
- return self.state.last_roll_date('ksk') + datetime.timedelta(seconds=timeparse(self.config.ksk_frequency))
+ if self.config.ksk_frequency != 0:
+ return self.state.last_roll_date("ksk") + datetime.timedelta(
+ seconds=timeparse(self.config.ksk_frequency)
+ )
return None
def next_zsk_roll(self):
if not self.state.is_rolling:
if self.config.zsk_frequency != 0:
- return self.state.last_roll_date('zsk') + datetime.timedelta(seconds=timeparse(self.config.zsk_frequency))
+ return self.state.last_roll_date("zsk") + datetime.timedelta(
+ seconds=timeparse(self.config.zsk_frequency)
+ )
return None
@property
return ret[0]
return None
-
def __repr__(self):
- return 'keyrollerDomain("{}", {}, {}, {})'.format(
- self.zone, self.api, self.config, self.state
- )
+ return 'keyrollerDomain("{}", {}, {}, {})'.format(self.zone, self.api, self.config, self.state)
import pdnsapi.api
import json_tricks.nonp as json_tricks
-from pdnskeyroller.util import (get_keys_of_type, DNSKEY_ALGO_TO_MNEMONIC, DNSKEY_MNEMONIC_TO_ALGO, validate_api)
+from pdnskeyroller.util import get_keys_of_type, DNSKEY_ALGO_TO_MNEMONIC, DNSKEY_MNEMONIC_TO_ALGO, validate_api
from datetime import datetime, timedelta
from pdnskeyroller.keyroll import KeyRoll
_step_to_name = {
- 0: 'initial',
- 1: 'new DNSKEY',
- 2: 'new DS/new RRSIGs',
- 3: 'DNSKEY removal',
+ 0: "initial",
+ 1: "new DNSKEY",
+ 2: "new DS/new RRSIGs",
+ 3: "DNSKEY removal",
}
class PrePublishKeyRoll(KeyRoll):
def __init__(self, **kwargs):
- super().__init__(rolltype='prepublish')
- self.current_step = kwargs.get('current_step', 0)
- self.complete = kwargs.get('complete', False)
- self.step_datetimes = list(map(lambda x: datetime.fromtimestamp(x), kwargs.get('step_datetimes', [])))
- self.current_step_datetime = datetime.fromtimestamp(kwargs.get('current_step_datetime', datetime.now().timestamp()))
- self.keytype = kwargs.get('keytype')
- self.algo = kwargs.get('algo')
- self.old_keyids = kwargs.get('old_keyids')
- self.new_keyid = kwargs.get('new_keyid')
+ super().__init__(rolltype="prepublish")
+ self.current_step = kwargs.get("current_step", 0)
+ self.complete = kwargs.get("complete", False)
+ self.step_datetimes = list(map(lambda x: datetime.fromtimestamp(x), kwargs.get("step_datetimes", [])))
+ self.current_step_datetime = datetime.fromtimestamp(
+ kwargs.get("current_step_datetime", datetime.now().timestamp())
+ )
+ self.keytype = kwargs.get("keytype")
+ self.algo = kwargs.get("algo")
+ self.old_keyids = kwargs.get("old_keyids")
+ self.new_keyid = kwargs.get("new_keyid")
def initiate(self, zone, api, keytype, algo, bits=None, published=True):
"""
- Initiate a pre-publish rollover (:rfc:`RFC 6781 §4.1.1.1 <6781#section-4.1.1.1>`) for the ``keytype`` key of algorithm
- ``algo`` for ``zone``.
+ Initiate a pre-publish rollover (:rfc:`RFC 6781 §4.1.1.1 <6781#section-4.1.1.1>`) for the ``keytype`` key of algorithm
+ ``algo`` for ``zone``.
- The roll will **only** be initiated if there exists a ``keytype`` key of algorithm ``algo`` for the domain ``zone``.
+ The roll will **only** be initiated if there exists a ``keytype`` key of algorithm ``algo`` for the domain ``zone``.
- :param string zone: The zone to roll for
- :param pdnsapi.api.PDNSApi api: The API endpoint to use
- :param string keytype: The keytype to roll, must be one of 'ksk', 'zsk' or 'csk'
- :param string algo: The algorithm to roll the ``keytype`` for
- :param int bits: If needed, use this many bits for the new key for ``algo``
+ :param string zone: The zone to roll for
+ :param pdnsapi.api.PDNSApi api: The API endpoint to use
+ :param string keytype: The keytype to roll, must be one of 'ksk', 'zsk' or 'csk'
+ :param string algo: The algorithm to roll the ``keytype`` for
+ :param int bits: If needed, use this many bits for the new key for ``algo``
"""
if self.started:
- raise Exception('Already rolling the {} for {}'.format(
- self.keytype, zone))
+ raise Exception("Already rolling the {} for {}".format(self.keytype, zone))
validate_api(api)
keytype = keytype.lower()
- if keytype not in ('ksk', 'zsk'):
- raise Exception('Invalid key type: {}'.format(keytype))
+ if keytype not in ("ksk", "zsk"):
+ raise Exception("Invalid key type: {}".format(keytype))
current_keys = get_keys_of_type(zone, api, keytype)
algo = DNSKEY_ALGO_TO_MNEMONIC.get(algo, algo)
if not current_keys:
- raise Exception('There are no keys of type {} in zone {}, cannot roll!'.format(keytype, zone))
+ raise Exception("There are no keys of type {} in zone {}, cannot roll!".format(keytype, zone))
if not any([k.algo == algo and k.keytype == keytype for k in current_keys]):
- raise Exception('No keys for algorithm {} in zone {}, cannot roll!'.format(algo, zone))
+ raise Exception("No keys for algorithm {} in zone {}, cannot roll!".format(algo, zone))
active = True
published = True
httl = self._get_highest_ttl(zone, api)
self.current_step_datetime = datetime.now() + timedelta(seconds=httl)
- api.bump_soa(zone);
+ api.bump_soa(zone)
def _get_highest_ttl(self, zone, api, zoneobject=None):
if zoneobject is None:
"""
validate_api(api)
if not self.validate(zone, api):
- raise Exception('Keys for zone {} do not match keys initially found. Refusing to continue'.format(zone))
+ raise Exception("Keys for zone {} do not match keys initially found. Refusing to continue".format(zone))
if not self.started:
- raise Exception('Can not go to the next step in phase "{}", did you mean to call initialize()?'.format(
- self.current_step_name))
+ raise Exception(
+ 'Can not go to the next step in phase "{}", did you mean to call initialize()?'.format(
+ self.current_step_name
+ )
+ )
# make sure we are passed the expected datetime
if self.current_step_datetime > datetime.now():
for keyid in self.old_keyids:
api.set_cryptokey_active(zone, keyid, active=False)
- api.bump_soa(zone);
-
+ api.bump_soa(zone)
httl = self._get_highest_ttl(zone, api)
self.current_step_datetime = datetime.now() + timedelta(seconds=httl)
self.step_datetimes.append(datetime.now())
# remove the old keys
for keyid in self.old_keyids:
api.delete_cryptokey(zone, keyid)
- api.bump_soa(zone);
+ api.bump_soa(zone)
# rollover is finished
self.complete = True
self.step_datetimes.append(datetime.now())
-
elif self.current_step == 3:
if self.keytype == "ksk":
# remove the old keys
for keyid in self.old_keyids:
api.delete_cryptokey(zone, keyid)
- api.bump_soa(zone);
+ api.bump_soa(zone)
# rollover is finished
self.complete = True
self.step_datetimes.append(datetime.now())
validate_api(api)
to_match = self.old_keyids.copy()
to_match.append(self.new_keyid)
- return all([k.id in to_match for k in api.get_cryptokeys(zone)
- if k.algo == self.algo and k.keytype == self.keytype])
+ return all(
+ [k.id in to_match for k in api.get_cryptokeys(zone) if k.algo == self.algo and k.keytype == self.keytype]
+ )
def __str__(self):
- return json_tricks.dumps({
- 'rolltype': 'prepublish',
- 'current_step': self.current_step,
- 'complete': self.complete,
- 'current_step_datetime': self.current_step_datetime.timestamp(),
- 'step_datetimes': list(map(lambda d: d.timestamp(), self.step_datetimes)),
- 'keytype': self.keytype,
- 'algo': self.algo,
- 'old_keyids': self.old_keyids,
- 'new_keyid': self.new_keyid,
- })
+ return json_tricks.dumps(
+ {
+ "rolltype": "prepublish",
+ "current_step": self.current_step,
+ "complete": self.complete,
+ "current_step_datetime": self.current_step_datetime.timestamp(),
+ "step_datetimes": list(map(lambda d: d.timestamp(), self.step_datetimes)),
+ "keytype": self.keytype,
+ "algo": self.algo,
+ "old_keyids": self.old_keyids,
+ "new_keyid": self.new_keyid,
+ }
+ )
+
def __json_encode__(self):
# should return primitive, serializable types like dict, list, int, string, float...
return {
- 'rolltype': 'prepublish',
- 'current_step': self.current_step,
- 'complete': self.complete,
- 'current_step_datetime': self.current_step_datetime.timestamp(),
- 'step_datetimes': list(map(lambda d: d.timestamp(), self.step_datetimes)),
- 'keytype': self.keytype,
- 'algo': self.algo,
- 'old_keyids': self.old_keyids,
- 'new_keyid': self.new_keyid,
+ "rolltype": "prepublish",
+ "current_step": self.current_step,
+ "complete": self.complete,
+ "current_step_datetime": self.current_step_datetime.timestamp(),
+ "step_datetimes": list(map(lambda d: d.timestamp(), self.step_datetimes)),
+ "keytype": self.keytype,
+ "algo": self.algo,
+ "old_keyids": self.old_keyids,
+ "new_keyid": self.new_keyid,
}
def __json_decode__(self, **kwargs):
- super().__init__(rolltype='prepublish')
- self.current_step = kwargs.get('current_step', 0)
- self.complete = kwargs.get('complete', False)
- self.step_datetimes = list(map(lambda x: datetime.fromtimestamp(x), kwargs.get('step_datetimes', [])))
- self.current_step_datetime = datetime.fromtimestamp(kwargs.get('current_step_datetime', datetime.now().timestamp()))
- self.keytype = kwargs.get('keytype')
- self.algo = kwargs.get('algo')
- self.old_keyids = kwargs.get('old_keyids')
- self.new_keyid = kwargs.get('new_keyid')
+ super().__init__(rolltype="prepublish")
+ self.current_step = kwargs.get("current_step", 0)
+ self.complete = kwargs.get("complete", False)
+ self.step_datetimes = list(map(lambda x: datetime.fromtimestamp(x), kwargs.get("step_datetimes", [])))
+ self.current_step_datetime = datetime.fromtimestamp(
+ kwargs.get("current_step_datetime", datetime.now().timestamp())
+ )
+ self.keytype = kwargs.get("keytype")
+ self.algo = kwargs.get("algo")
+ self.old_keyids = kwargs.get("old_keyids")
+ self.new_keyid = kwargs.get("new_keyid")
def __repr__(self):
- return 'PrePublishRoll({})'.format(
- ', '.join(['{}={}'.format(k, v) for k, v in [
- ('current_step', self.current_step),
- ('complete', self.complete),
- ('current_step_datetime', self.current_step_datetime.timestamp()),
- ('step_datetimes', list(map(lambda d: d.timestamp(), self.step_datetimes))),
- ('keytype', self.keytype),
- ('algo', self.algo),
- ('old_keyids', self.old_keyids),
- ('new_keyid', self.new_keyid),
- ]]))
+ return "PrePublishRoll({})".format(
+ ", ".join(
+ [
+ "{}={}".format(k, v)
+ for k, v in [
+ ("current_step", self.current_step),
+ ("complete", self.complete),
+ ("current_step_datetime", self.current_step_datetime.timestamp()),
+ ("step_datetimes", list(map(lambda d: d.timestamp(), self.step_datetimes))),
+ ("keytype", self.keytype),
+ ("algo", self.algo),
+ ("old_keyids", self.old_keyids),
+ ("new_keyid", self.new_keyid),
+ ]
+ ]
+ )
+ )
@property
def started(self):
"""
DNSKEY_ALGO_TO_MNEMONIC = {
- 1: "RSAMD5",
- 2: "DH",
- 3: "DSA",
- 5: "RSASHA1",
- 6: "DSA-NSEC3-SHA1",
- 7: "RSASHA1-NSEC3-SHA1",
- 8: "RSASHA256",
- 10: "RSASHA512",
- 12: "ECC-GOST",
- 13: "ECDSAP256",
- 14: "ECDSAP384",
- 15: "ED25519",
- 16: "ED448",
- }
+ 1: "RSAMD5",
+ 2: "DH",
+ 3: "DSA",
+ 5: "RSASHA1",
+ 6: "DSA-NSEC3-SHA1",
+ 7: "RSASHA1-NSEC3-SHA1",
+ 8: "RSASHA256",
+ 10: "RSASHA512",
+ 12: "ECC-GOST",
+ 13: "ECDSAP256",
+ 14: "ECDSAP384",
+ 15: "ED25519",
+ 16: "ED448",
+}
DNSKEY_MNEMONIC_TO_ALGO = {v: k for k, v in DNSKEY_ALGO_TO_MNEMONIC.items()}
+
def parse_algo(algo):
res = 0
try:
res = DNSKEY_MNEMONIC_TO_ALGO.get(algo.upper())
if DNSKEY_ALGO_TO_MNEMONIC.get(res) is None:
- raise Exception('Unknown key algorithm {}'.format(algo))
+ raise Exception("Unknown key algorithm {}".format(algo))
return res
def validate_api(api):
if not isinstance(api, pdnsapi.api.PDNSApi):
- raise Exception('api is not a PDNSApi')
+ raise Exception("api is not a PDNSApi")
+
def validate_keytype(keytype):
keytype = keytype.lower()
- if keytype not in ('ksk', 'zsk', 'csk'):
- raise Exception('{} is not a valid key type'.format(keytype))
+ if keytype not in ("ksk", "zsk", "csk"):
+ raise Exception("{} is not a valid key type".format(keytype))
def get_keystyle(zone, api):
cryptokeys = api.get_cryptokeys(zone)
if not len(cryptokeys):
- raise Exception('No cryptokeys for zone {}'.format(zone))
+ raise Exception("No cryptokeys for zone {}".format(zone))
- got_ksk = any([cryptokey.keytype.lower() == 'ksk' for cryptokey in cryptokeys])
- got_zsk = any([cryptokey.keytype.lower() == 'zsk' for cryptokey in cryptokeys])
- got_csk = any([cryptokey.keytype.lower() == 'csk' for cryptokey in cryptokeys])
+ got_ksk = any([cryptokey.keytype.lower() == "ksk" for cryptokey in cryptokeys])
+ got_zsk = any([cryptokey.keytype.lower() == "zsk" for cryptokey in cryptokeys])
+ got_csk = any([cryptokey.keytype.lower() == "csk" for cryptokey in cryptokeys])
if got_csk and not any([got_ksk, got_zsk]):
- return 'single'
+ return "single"
if all([got_ksk, got_zsk]) and not got_csk:
- return 'split'
+ return "split"
if all([got_ksk, got_zsk, got_csk]):
- return 'mixed'
+ return "mixed"
def get_keys_of_type(zone, api, keytype):
import pathlib
from setuptools import setup, find_packages
+
# reads requirements.txt file and extracts package_name and version (if set)
def read_requirements_file(fname):
reqs = []
for line in f:
line = line.strip()
# do not consider comments, hashes and remove trailing "\" if needed
- if line and not line.startswith(('#', '-')):
- reqs.append(line.rstrip('\\').strip())
+ if line and not line.startswith(("#", "-")):
+ reqs.append(line.rstrip("\\").strip())
return reqs
# README file and 2) it's easier to type in the README file than to put a raw
# string in below ...
def read(fname):
- with open(os.path.join(os.path.dirname(__file__), fname),
- 'r', encoding='utf-8') as f:
+ with open(os.path.join(os.path.dirname(__file__), fname), "r", encoding="utf-8") as f:
return f.read()
-version = os.environ.get('BUILDER_VERSION', '0.0.0')
+version = os.environ.get("BUILDER_VERSION", "0.0.0")
-if exists('version.txt'):
- version = read('version.txt').strip()
+if exists("version.txt"):
+ version = read("version.txt").strip()
setup(
- name = "pdns-keyroller",
- version = version,
- author = "PowerDNS.COM BV",
- author_email = "powerdns.support@powerdns.com",
- description = ("PowerDNS keyroller"),
- license = "GNU GPLv2",
- keywords = "PowerDNS keyroller",
- url = "https://www.powerdns.com/",
- packages = find_packages(),
+ name="pdns-keyroller",
+ version=version,
+ author="PowerDNS.COM BV",
+ author_email="powerdns.support@powerdns.com",
+ description=("PowerDNS keyroller"),
+ license="GNU GPLv2",
+ keywords="PowerDNS keyroller",
+ url="https://www.powerdns.com/",
+ packages=find_packages(),
install_requires=read_requirements_file("requirements.txt"),
- include_package_data = True,
- scripts=['pdns-keyroller.py', 'pdns-keyroller-ctl.py'],
- long_description=read('README.md'),
+ include_package_data=True,
+ scripts=["pdns-keyroller.py", "pdns-keyroller-ctl.py"],
+ long_description=read("README.md"),
classifiers=[],
)
#
# needs_sphinx = '1.0'
-sys.path.append(str(Path('.').resolve()))
+sys.path.append(str(Path(".").resolve()))
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
-#extensions = []
-#extensions = ['redjack.sphinx.lua', 'sphinxcontrib.httpdomain', 'sphinxjsondomain']
-extensions = ['redjack.sphinx.lua', 'sphinxcontrib.httpdomain', 'sphinxjsondomain',
- 'sphinxcontrib.fulltoc', 'changelog', 'depfile']
-primary_domain = 'lua'
+# extensions = []
+# extensions = ['redjack.sphinx.lua', 'sphinxcontrib.httpdomain', 'sphinxjsondomain']
+extensions = [
+ "redjack.sphinx.lua",
+ "sphinxcontrib.httpdomain",
+ "sphinxjsondomain",
+ "sphinxcontrib.fulltoc",
+ "changelog",
+ "depfile",
+]
+primary_domain = "lua"
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ".rst"
# The master toctree document.
-master_doc = 'indexTOC'
+master_doc = "indexTOC"
# General information about the project.
-project = 'PowerDNS Recursor'
-copyright = 'PowerDNS.COM BV'
-author = 'PowerDNS.COM BV'
+project = "PowerDNS Recursor"
+copyright = "PowerDNS.COM BV"
+author = "PowerDNS.COM BV"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-#version = '4.1'
+# version = '4.1'
# The full version, including alpha/beta/rc tags.
-#release = '4.1.0-alpha1'
+# release = '4.1.0-alpha1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
-exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '.venv',
- 'http-api/override.rst',
- 'common/zonemetadata.rst',
- 'common/endpoint-servers-config.rst',
- 'common/secpoll.rst',
- 'common/api/zone.rst']
+exclude_patterns = [
+ "_build",
+ "Thumbs.db",
+ ".DS_Store",
+ ".venv",
+ "http-api/override.rst",
+ "common/zonemetadata.rst",
+ "common/endpoint-servers-config.rst",
+ "common/secpoll.rst",
+ "common/api/zone.rst",
+]
# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
changelog_render_pullreq = "https://github.com/PowerDNS/pdns/pull/%s"
changelog_render_changeset = "https://github.com/PowerDNS/pdns/commit/%s"
-changelog_sections = ['New Features', 'Improvements', 'Bug Fixes', 'Removals']
-changelog_inner_tag_sort = ['General', 'DNSSEC', 'Protobuf', 'RPZ']
+changelog_sections = ["New Features", "Improvements", "Bug Fixes", "Removals"]
+changelog_inner_tag_sort = ["General", "DNSSEC", "Protobuf", "RPZ"]
changelog_hide_tags_in_entry = True
# a list of builtin themes.
#
html_theme_path = guzzle_sphinx_theme.html_theme_path()
-html_theme = 'guzzle_sphinx_theme'
+html_theme = "guzzle_sphinx_theme"
extensions.append("guzzle_sphinx_theme")
# Set the name of the project to appear in the sidebar
"project_nav_name": "PowerDNS Recursor",
}
-html_favicon = 'common/favicon.ico'
+html_favicon = "common/favicon.ico"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-html_style = 'pdns.css'
+html_static_path = ["_static"]
+html_style = "pdns.css"
-html_sidebars = { '**': ['logo-text.html', 'searchbox.html', 'relations.html', 'localtoc.html', 'sourcelink.html'] }
+html_sidebars = {"**": ["logo-text.html", "searchbox.html", "relations.html", "localtoc.html", "sourcelink.html"]}
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
-htmlhelp_basename = 'PowerDNSRecursordoc'
+htmlhelp_basename = "PowerDNSRecursordoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
- 'papersize': 'a4paper',
-
+ "papersize": "a4paper",
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
-
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
-
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, 'PowerDNS-Recursor.tex', 'PowerDNS Recursor Documentation',
- 'PowerDNS.COM BV', 'manual'),
+ (master_doc, "PowerDNS-Recursor.tex", "PowerDNS Recursor Documentation", "PowerDNS.COM BV", "manual"),
]
-latex_logo = 'common/powerdns-logo-500px.png'
+latex_logo = "common/powerdns-logo-500px.png"
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- ('manpages/rec_control.1', 'rec_control', 'Command line tool to control a running Recursor', [author], 1),
- ('manpages/pdns_recursor.1', 'pdns_recursor', 'The PowerDNS Recursor binary', [author], 1)
+ ("manpages/rec_control.1", "rec_control", "Command line tool to control a running Recursor", [author], 1),
+ ("manpages/pdns_recursor.1", "pdns_recursor", "The PowerDNS Recursor binary", [author], 1),
]
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
-#texinfo_documents = [
+# texinfo_documents = [
# (master_doc, 'PowerDNSRecursor', 'PowerDNS Recursor Documentation',
# author, 'PowerDNSRecursor', 'One line description of project.',
# 'Miscellaneous'),
-#]
-
+# ]
# -- Options for Epub output ----------------------------------------------
# epub_uid = ''
# A list of files that should not be packed into the epub file.
-epub_exclude_files = ['search.html']
+epub_exclude_files = ["search.html"]
-depfile = 'sphinx.d'
-depfile_stamp = 'sphinx.stamp'
+depfile = "sphinx.d"
+depfile_stamp = "sphinx.stamp"
import shutil
import os.path
-for extdir in ['yahttp', 'json11', 'probds']:
+for extdir in ["yahttp", "json11", "probds"]:
try:
- shutil.rmtree(os.path.join('ext', extdir))
+ shutil.rmtree(os.path.join("ext", extdir))
except OSError:
pass
try:
- os.rmdir(os.path.join('ext', extdir))
+ os.rmdir(os.path.join("ext", extdir))
except OSError:
pass
- for root, dirs, files in os.walk(os.path.join('../../ext', extdir)):
- stripped_root = root.replace('../', '')
+ for root, dirs, files in os.walk(os.path.join("../../ext", extdir)):
+ stripped_root = root.replace("../", "")
os.mkdir(stripped_root)
- num_dirs = len(root.split('/')) - root.split('/').count('..')
+ num_dirs = len(root.split("/")) - root.split("/").count("..")
for dirfile in files:
- if dirfile == '.gitignore':
- shutil.copyfile(os.path.join(num_dirs * '../', stripped_root, dirfile), os.path.join(stripped_root, dirfile))
+ if dirfile == ".gitignore":
+ shutil.copyfile(
+ os.path.join(num_dirs * "../", stripped_root, dirfile), os.path.join(stripped_root, dirfile)
+ )
else:
- os.symlink(os.path.join(num_dirs * '../', root, dirfile), os.path.join(stripped_root, dirfile))
+ os.symlink(os.path.join(num_dirs * "../", root, dirfile), os.path.join(stripped_root, dirfile))
# default: 'type': uint64
# ptype: "'counter' (vs gauge')
-srcdir = '.'
-builddir = '.'
+srcdir = "."
+builddir = "."
if len(sys.argv) == 3:
print("metrics.py: using srcdir and builddir from arguments")
srcdir = sys.argv[1]
print("metrics.py srcdir: " + srcdir + " = " + os.path.realpath(srcdir))
print("metrics.py builddir: " + builddir + " = " + os.path.realpath(builddir))
+
def dedashForSNMP(name):
cap = False
- ret = ''
+ ret = ""
for ch in name:
- if ch == '-':
+ if ch == "-":
cap = True
elif cap:
ret += ch.upper()
cap = False
else:
ret += ch
- ret = ret.replace('Nsec', 'NSEC')
+ ret = ret.replace("Nsec", "NSEC")
return ret
+
table = metrics_table.table
#
# We create various files in the srcdir but copy them into the builddir if needed to satisfy meson
# FIXME: only generate in builddir once autotools have been dropped
#
-with open(srcdir + '/rec-oids-gen.h', 'w', encoding='utf-8') as file:
- file.write('// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE metrics.py AND metrics_table.py\n')
+with open(srcdir + "/rec-oids-gen.h", "w", encoding="utf-8") as file:
+ file.write("// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE metrics.py AND metrics_table.py\n")
for entry in table:
- if 'snmp' not in entry:
+ if "snmp" not in entry:
continue
- if 'ifdef' in entry:
- ifdef = entry['ifdef']
- file.write(f'#ifdef {ifdef}\n')
- name = dedashForSNMP(entry['name'])
- snmp = entry['snmp']
- file.write(f'static const oid10 {name}OID = {{RECURSOR_STATS_OID, {snmp}}};\n')
- if 'ifdef' in entry:
- file.write(f'#endif\n')
+ if "ifdef" in entry:
+ ifdef = entry["ifdef"]
+ file.write(f"#ifdef {ifdef}\n")
+ name = dedashForSNMP(entry["name"])
+ snmp = entry["snmp"]
+ file.write(f"static const oid10 {name}OID = {{RECURSOR_STATS_OID, {snmp}}};\n")
+ if "ifdef" in entry:
+ file.write(f"#endif\n")
if srcdir != builddir:
- shutil.copy(srcdir + '/rec-oids-gen.h', builddir)
+ shutil.copy(srcdir + "/rec-oids-gen.h", builddir)
-with open(srcdir + '/rec-snmp-gen.h', 'w', encoding='utf-8') as file:
- file.write('// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE metrics.py AND metrics_table.py\n')
+with open(srcdir + "/rec-snmp-gen.h", "w", encoding="utf-8") as file:
+ file.write("// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE metrics.py AND metrics_table.py\n")
for entry in table:
- if 'snmp' not in entry:
+ if "snmp" not in entry:
continue
- name = entry['name']
+ name = entry["name"]
dname = dedashForSNMP(name)
- if 'ifdef' in entry:
- ifdef = entry['ifdef']
- file.write(f'#ifdef {ifdef}\n')
+ if "ifdef" in entry:
+ ifdef = entry["ifdef"]
+ file.write(f"#ifdef {ifdef}\n")
file.write(f'registerCounter64Stat("{name}", {dname}OID);\n')
- if 'ifdef' in entry:
- file.write(f'#endif\n')
+ if "ifdef" in entry:
+ file.write(f"#endif\n")
if srcdir != builddir:
- shutil.copy(srcdir + '/rec-snmp-gen.h', builddir)
+ shutil.copy(srcdir + "/rec-snmp-gen.h", builddir)
-with open(srcdir + '/rec-prometheus-gen.h', 'w', encoding='utf-8') as file:
- file.write('// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE metrics.py AND metrics_table.py\n')
+with open(srcdir + "/rec-prometheus-gen.h", "w", encoding="utf-8") as file:
+ file.write("// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE metrics.py AND metrics_table.py\n")
for entry in table:
- name = entry['name']
- if 'pname' in entry:
- name = entry['pname']
- desc = ''
- desc = entry['desc']
- if 'pdesc' in entry:
- desc = entry['pdesc']
- if desc == '':
+ name = entry["name"]
+ if "pname" in entry:
+ name = entry["pname"]
+ desc = ""
+ desc = entry["desc"]
+ if "pdesc" in entry:
+ desc = entry["pdesc"]
+ if desc == "":
continue
- ptype = 'counter'
- if 'ptype' in entry:
- ptype = entry['ptype']
+ ptype = "counter"
+ if "ptype" in entry:
+ ptype = entry["ptype"]
file.write(f'{{"{name}", MetricDefinition(PrometheusMetricType::{ptype}, "{desc}")}},\n')
if srcdir != builddir:
- shutil.copy(srcdir + '/rec-prometheus-gen.h', builddir)
+ shutil.copy(srcdir + "/rec-prometheus-gen.h", builddir)
-with open(srcdir + '/rec-metrics-gen.h', 'w', encoding='utf-8') as file:
- file.write('// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE metrics.py AND metrics_table.py\n')
+with open(srcdir + "/rec-metrics-gen.h", "w", encoding="utf-8") as file:
+ file.write("// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE metrics.py AND metrics_table.py\n")
for entry in table:
- name = entry['name']
- if 'lambda' not in entry:
+ name = entry["name"]
+ if "lambda" not in entry:
continue
- lam = entry['lambda']
- if 'ifdef' in entry:
- ifdef = entry['ifdef']
- file.write(f'#ifdef {ifdef}\n')
- if 'if' in entry:
- iff = entry['if']
- file.write(f'if ({iff}) {{\n')
+ lam = entry["lambda"]
+ if "ifdef" in entry:
+ ifdef = entry["ifdef"]
+ file.write(f"#ifdef {ifdef}\n")
+ if "if" in entry:
+ iff = entry["if"]
+ file.write(f"if ({iff}) {{\n")
file.write(f'addGetStat("{name}", {lam});\n')
- if 'if' in entry:
- file.write(f'}}\n')
- if 'ifdef' in entry:
- file.write(f'#endif\n')
+ if "if" in entry:
+ file.write(f"}}\n")
+ if "ifdef" in entry:
+ file.write(f"#endif\n")
if srcdir != builddir:
- shutil.copy(srcdir + '/rec-metrics-gen.h', builddir)
+ shutil.copy(srcdir + "/rec-metrics-gen.h", builddir)
-if os.path.isdir(srcdir + '/docs'):
- with open(srcdir + '/docs/rec-metrics-gen.rst', 'w', encoding='utf-8') as file:
- file.write('.. THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE metrics.py AND metrics_table.py\n')
- sortedtable = sorted(table, key = lambda value: value['name'])
- file.write('.. csv-table:: **Metrics**\n')
+if os.path.isdir(srcdir + "/docs"):
+ with open(srcdir + "/docs/rec-metrics-gen.rst", "w", encoding="utf-8") as file:
+ file.write(".. THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE metrics.py AND metrics_table.py\n")
+ sortedtable = sorted(table, key=lambda value: value["name"])
+ file.write(".. csv-table:: **Metrics**\n")
file.write(' :header: "rec_control name", "Description", "SNMP Object and OID"\n')
- file.write(' :widths: 20, 50, 20\n\n')
+ file.write(" :widths: 20, 50, 20\n\n")
for entry in sortedtable:
- name = entry['name']
- desc = entry['desc']
- if not desc.endswith('.'):
- desc += '.'
- if 'longdesc' in entry:
- desc = desc + ' ' + entry['longdesc'].strip()
- if not desc.endswith('.'):
- desc += '.'
+ name = entry["name"]
+ desc = entry["desc"]
+ if not desc.endswith("."):
+ desc += "."
+ if "longdesc" in entry:
+ desc = desc + " " + entry["longdesc"].strip()
+ if not desc.endswith("."):
+ desc += "."
file.write(f' "**{name}**", ')
file.write(f'"{desc}"')
- if 'snmp' in entry:
- snmp = entry['snmp']
+ if "snmp" in entry:
+ snmp = entry["snmp"]
snmpname = dedashForSNMP(name)
- if 'snmpname' in entry:
- name = entry['snmpname']
+ if "snmpname" in entry:
+ name = entry["snmpname"]
file.write(f', "{snmpname} ({snmp})"')
else:
- file.write(',')
- file.write('\n')
+ file.write(",")
+ file.write("\n")
-str1 = ''
+str1 = ""
for entry in table:
- if 'snmp' not in entry:
+ if "snmp" not in entry:
continue
- name = dedashForSNMP(entry['name'])
- if 'snmpname' in entry:
- name = entry['snmpname']
- snmp = entry['snmp']
- desc = entry['desc']
- type = 'Counter64'
- if 'ptype' in entry and entry['ptype'] == 'gauge':
- type = 'CounterBasedGauge64'
+ name = dedashForSNMP(entry["name"])
+ if "snmpname" in entry:
+ name = entry["snmpname"]
+ snmp = entry["snmp"]
+ desc = entry["desc"]
+ type = "Counter64"
+ if "ptype" in entry and entry["ptype"] == "gauge":
+ type = "CounterBasedGauge64"
str1 += f'''
{name} OBJECT-TYPE
SYNTAX {type}
::= {{ stats {snmp} }}
'''
-str2 = ' trapReason'
+str2 = " trapReason"
for entry in table:
- if 'snmp' not in entry:
+ if "snmp" not in entry:
continue
- name = dedashForSNMP(entry['name'])
- if 'snmpname' in entry:
- name = entry['snmpname']
- str2 += f',\n {name}'
+ name = dedashForSNMP(entry["name"])
+ if "snmpname" in entry:
+ name = entry["snmpname"]
+ str2 += f",\n {name}"
-with open(srcdir + '/RECURSOR-MIB.in', mode='r', encoding='utf-8') as file:
+with open(srcdir + "/RECURSOR-MIB.in", mode="r", encoding="utf-8") as file:
text = file.read()
- text = text.replace('REPL_OBJECTS1', str1)
- text = text.replace('REPL_OBJECTS2', str2)
- with open(srcdir + '/RECURSOR-MIB.txt', 'w', encoding='utf-8') as file2:
+ text = text.replace("REPL_OBJECTS1", str1)
+ text = text.replace("REPL_OBJECTS2", str2)
+ with open(srcdir + "/RECURSOR-MIB.txt", "w", encoding="utf-8") as file2:
file2.write(text)
if srcdir != builddir:
- shutil.copy(srcdir + '/RECURSOR-MIB.txt', builddir)
+ shutil.copy(srcdir + "/RECURSOR-MIB.txt", builddir)
table = [
{
- 'name': 'questions',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::qcounter); }',
- 'desc': 'Number of questions',
- 'longdesc': 'Counts all end-user initiated queries with the RD bit set',
- 'snmp': 1,
+ "name": "questions",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::qcounter); }",
+ "desc": "Number of questions",
+ "longdesc": "Counts all end-user initiated queries with the RD bit set",
+ "snmp": 1,
},
{
- 'name': 'ipv6-questions',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::ipv6qcounter); }',
- 'desc': 'Number of IPv6 questions',
- 'longdesc': 'Counts all end-user initiated queries with the RD bit set, received over IPv6 UDP',
- 'snmp': 2,
+ "name": "ipv6-questions",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::ipv6qcounter); }",
+ "desc": "Number of IPv6 questions",
+ "longdesc": "Counts all end-user initiated queries with the RD bit set, received over IPv6 UDP",
+ "snmp": 2,
},
{
- 'name': 'tcp-questions',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::tcpqcounter); }',
- 'desc': 'Number of TCP questions',
- 'snmp': 3,
+ "name": "tcp-questions",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::tcpqcounter); }",
+ "desc": "Number of TCP questions",
+ "snmp": 3,
},
{
- 'name': 'cache-hits',
- 'lambda': '[]() { return g_recCache->getCacheHits(); }',
- 'desc': 'Number of cache hits',
- 'longdesc': 'This does *not* include hits that got answered from the packet-cache',
- 'snmp': 4,
+ "name": "cache-hits",
+ "lambda": "[]() { return g_recCache->getCacheHits(); }",
+ "desc": "Number of cache hits",
+ "longdesc": "This does *not* include hits that got answered from the packet-cache",
+ "snmp": 4,
},
{
- 'name': 'cache-misses',
- 'lambda': '[]() { return g_recCache->getCacheMisses(); }',
- 'desc': 'Number of cache misses',
- 'longdesc': 'This does *not* include hits that got answered from the packet-cache',
- 'snmp': 5,
+ "name": "cache-misses",
+ "lambda": "[]() { return g_recCache->getCacheMisses(); }",
+ "desc": "Number of cache misses",
+ "longdesc": "This does *not* include hits that got answered from the packet-cache",
+ "snmp": 5,
},
{
- 'name': 'cache-entries',
- 'lambda': 'doGetCacheSize',
- 'ptype': 'gauge',
- 'desc': 'Number of record cache entries',
- 'snmp': 6,
+ "name": "cache-entries",
+ "lambda": "doGetCacheSize",
+ "ptype": "gauge",
+ "desc": "Number of record cache entries",
+ "snmp": 6,
},
{
- 'name': 'max-cache-entries',
- 'lambda': '[]() { return g_maxCacheEntries.load(); }',
- 'desc': 'Currently configured maximum number of cache entries',
- 'ptype': 'gauge',
+ "name": "max-cache-entries",
+ "lambda": "[]() { return g_maxCacheEntries.load(); }",
+ "desc": "Currently configured maximum number of cache entries",
+ "ptype": "gauge",
# No SNMP
},
{
- 'name': 'max-packetcache-entries',
- 'lambda': '[]() { return g_maxPacketCacheEntries.load(); }',
- 'desc': 'Currently configured maximum number of packet cache entries',
- 'ptype': 'gauge',
+ "name": "max-packetcache-entries",
+ "lambda": "[]() { return g_maxPacketCacheEntries.load(); }",
+ "desc": "Currently configured maximum number of packet cache entries",
+ "ptype": "gauge",
# No SNMP
},
{
- 'name': 'cache-bytes',
- 'lambda': 'doGetCacheBytes',
- 'ptype': 'gauge',
- 'desc': 'Size of the cache in bytes',
- 'longdesc': '''Since version 5.3.0 this metric computes a rough estimate of the number of bytes allocated by the record cache. Older versions return a number that cannot be relied upon. Disabled by default, as computing this number is CPU intensive, see :ref:`setting-yaml-recursor.stats_rec_control_disabled_list`.''',
- 'snmp': 7,
+ "name": "cache-bytes",
+ "lambda": "doGetCacheBytes",
+ "ptype": "gauge",
+ "desc": "Size of the cache in bytes",
+ "longdesc": """Since version 5.3.0 this metric computes a rough estimate of the number of bytes allocated by the record cache. Older versions return a number that cannot be relied upon. Disabled by default, as computing this number is CPU intensive, see :ref:`setting-yaml-recursor.stats_rec_control_disabled_list`.""",
+ "snmp": 7,
},
{
- 'name': 'packetcache-hits',
- 'lambda': '[] { return g_packetCache ? g_packetCache->getHits() : 0; }',
- 'desc': 'Number of packetcache hits',
- 'snmp': 8,
+ "name": "packetcache-hits",
+ "lambda": "[] { return g_packetCache ? g_packetCache->getHits() : 0; }",
+ "desc": "Number of packetcache hits",
+ "snmp": 8,
},
{
- 'name': 'packetcache-misses',
- 'lambda': '[] { return g_packetCache ? g_packetCache->getMisses() : 0; }',
- 'desc': 'Number of packetcache misses',
- 'snmp': 9,
+ "name": "packetcache-misses",
+ "lambda": "[] { return g_packetCache ? g_packetCache->getMisses() : 0; }",
+ "desc": "Number of packetcache misses",
+ "snmp": 9,
},
{
- 'name': 'packetcache-entries',
- 'lambda': '[] { return g_packetCache ? g_packetCache->size() : 0; }',
- 'desc': 'Number of packetcache entries',
- 'ptype': 'gauge',
- 'snmp': 10,
+ "name": "packetcache-entries",
+ "lambda": "[] { return g_packetCache ? g_packetCache->size() : 0; }",
+ "desc": "Number of packetcache entries",
+ "ptype": "gauge",
+ "snmp": 10,
},
{
- 'name': 'packetcache-bytes',
- 'lambda': '[] { return g_packetCache ? g_packetCache->bytes() : 0; }',
- 'ptype': 'gauge',
- 'desc': 'Size of the packetcache in bytes',
- 'longdesc': '''Disabled by default, see :ref:`setting-yaml-recursor.stats_rec_control_disabled_list`. This metric is currently broken, it always is 0.''',
- 'snmp': 11,
+ "name": "packetcache-bytes",
+ "lambda": "[] { return g_packetCache ? g_packetCache->bytes() : 0; }",
+ "ptype": "gauge",
+ "desc": "Size of the packetcache in bytes",
+ "longdesc": """Disabled by default, see :ref:`setting-yaml-recursor.stats_rec_control_disabled_list`. This metric is currently broken, it always is 0.""",
+ "snmp": 11,
},
{
- 'name': 'malloc-bytes',
- 'lambda': 'doGetMallocated',
- 'ptype': 'gauge',
- 'desc': 'Number of bytes allocated by malloc',
- 'longdesc': 'Broken, always returns 0',
- 'snmp': 12,
+ "name": "malloc-bytes",
+ "lambda": "doGetMallocated",
+ "ptype": "gauge",
+ "desc": "Number of bytes allocated by malloc",
+ "longdesc": "Broken, always returns 0",
+ "snmp": 12,
},
{
- 'name': 'servfail-answers',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::servFails); }',
- 'desc': 'Number of servfail answers',
- 'snmp': 13,
+ "name": "servfail-answers",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::servFails); }",
+ "desc": "Number of servfail answers",
+ "snmp": 13,
},
{
- 'name': 'nxdomain-answers',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::nxDomains); }',
- 'desc': 'Number of nxdomain answers',
- 'snmp': 14,
+ "name": "nxdomain-answers",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::nxDomains); }",
+ "desc": "Number of nxdomain answers",
+ "snmp": 14,
},
{
- 'name': 'noerror-answers',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::noErrors); }',
- 'desc': 'Number of noerror answers',
- 'snmp': 15,
+ "name": "noerror-answers",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::noErrors); }",
+ "desc": "Number of noerror answers",
+ "snmp": 15,
},
{
- 'name': 'unauthorized-udp',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::unauthorizedUDP); }',
- 'desc': 'Number of unauthorized UDP queries',
- 'snmp': 16,
+ "name": "unauthorized-udp",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::unauthorizedUDP); }",
+ "desc": "Number of unauthorized UDP queries",
+ "snmp": 16,
},
{
- 'name': 'unauthorized-tcp',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::unauthorizedTCP); }',
- 'desc': 'Number of unauthorized TCP queries',
- 'snmp': 17,
+ "name": "unauthorized-tcp",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::unauthorizedTCP); }",
+ "desc": "Number of unauthorized TCP queries",
+ "snmp": 17,
},
{
- 'name': 'tcp-client-overflow',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::tcpClientOverflow); }',
- 'desc': 'Number of TCP client connections refused because of too many connections',
- 'snmp': 18,
+ "name": "tcp-client-overflow",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::tcpClientOverflow); }",
+ "desc": "Number of TCP client connections refused because of too many connections",
+ "snmp": 18,
},
{
- 'name': 'client-parse-errors',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::clientParseError); }',
- 'desc': 'Number of client parse errors',
- 'snmp': 19,
+ "name": "client-parse-errors",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::clientParseError); }",
+ "desc": "Number of client parse errors",
+ "snmp": 19,
},
{
- 'name': 'server-parse-errors',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::serverParseError); }',
- 'desc': 'Number of server parse errors',
- 'snmp': 20,
+ "name": "server-parse-errors",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::serverParseError); }",
+ "desc": "Number of server parse errors",
+ "snmp": 20,
},
{
- 'name': 'too-old-drops',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::tooOldDrops); }',
- 'desc': 'Number of queries dropped because of a timeout',
- 'snmp': 21,
+ "name": "too-old-drops",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::tooOldDrops); }",
+ "desc": "Number of queries dropped because of a timeout",
+ "snmp": 21,
},
{
- 'name': 'answers0-1',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::answers).getCount(0); }',
- 'desc': 'Number of queries answered in less than 1 ms',
- 'snmp': 22,
+ "name": "answers0-1",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::answers).getCount(0); }",
+ "desc": "Number of queries answered in less than 1 ms",
+ "snmp": 22,
},
{
- 'name': 'answers1-10',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::answers).getCount(1); }',
- 'desc': 'Number of queries answered in 1-10 ms',
- 'snmp': 23,
+ "name": "answers1-10",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::answers).getCount(1); }",
+ "desc": "Number of queries answered in 1-10 ms",
+ "snmp": 23,
},
{
- 'name': 'answers10-100',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::answers).getCount(2); }',
- 'desc': 'Number of queries answered in 10-100 ms',
- 'snmp': 24,
+ "name": "answers10-100",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::answers).getCount(2); }",
+ "desc": "Number of queries answered in 10-100 ms",
+ "snmp": 24,
},
{
- 'name': 'answers100-1000',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::answers).getCount(3); }',
- 'desc': 'Number of queries answered in 100-1000 ms',
- 'snmp': 25,
+ "name": "answers100-1000",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::answers).getCount(3); }",
+ "desc": "Number of queries answered in 100-1000 ms",
+ "snmp": 25,
},
{
- 'name': 'answers-slow',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::answers).getCount(4); }',
- 'desc': 'Number of queries answered in more than 1000 ms',
- 'snmp': 26,
+ "name": "answers-slow",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::answers).getCount(4); }",
+ "desc": "Number of queries answered in more than 1000 ms",
+ "snmp": 26,
},
{
- 'name': 'x-ourtime0-1',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(0); }',
- 'desc': 'Counts responses where between 0 and 1 milliseconds was spent within the Recursor',
- 'longdesc': 'Not yet proven to be reliable'
+ "name": "x-ourtime0-1",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(0); }",
+ "desc": "Counts responses where between 0 and 1 milliseconds was spent within the Recursor",
+ "longdesc": "Not yet proven to be reliable",
},
{
- 'name': 'x-ourtime1-2',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(1); }',
- 'desc': 'Counts responses where between 1 and 2 milliseconds was spent within the Recursor',
- 'longdesc': 'Not yet proven to be reliable'
+ "name": "x-ourtime1-2",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(1); }",
+ "desc": "Counts responses where between 1 and 2 milliseconds was spent within the Recursor",
+ "longdesc": "Not yet proven to be reliable",
},
{
- 'name': 'x-ourtime2-4',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(2); }',
- 'desc': 'Counts responses where between 16 and 32 milliseconds was spent within the Recursor',
- 'longdesc': 'Not yet proven to be reliable'
+ "name": "x-ourtime2-4",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(2); }",
+ "desc": "Counts responses where between 16 and 32 milliseconds was spent within the Recursor",
+ "longdesc": "Not yet proven to be reliable",
},
{
- 'name': 'x-ourtime4-8',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(3); }',
- 'desc': 'Counts responses where between 4 and 8 milliseconds was spent within the Recursor',
- 'longdesc': 'Not yet proven to be reliable'
+ "name": "x-ourtime4-8",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(3); }",
+ "desc": "Counts responses where between 4 and 8 milliseconds was spent within the Recursor",
+ "longdesc": "Not yet proven to be reliable",
},
{
- 'name': 'x-ourtime8-16',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(4); }',
- 'desc': 'Counts responses where between 8 and 16 milliseconds was spent within the Recursor',
- 'longdesc': 'Not yet proven to be reliable'
+ "name": "x-ourtime8-16",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(4); }",
+ "desc": "Counts responses where between 8 and 16 milliseconds was spent within the Recursor",
+ "longdesc": "Not yet proven to be reliable",
},
{
- 'name': 'x-ourtime16-32',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(5); }',
- 'desc': 'Counts responses where between 16 and 32 milliseconds was spent within the Recursor',
- 'longdesc': 'Not yet proven to be reliable'
+ "name": "x-ourtime16-32",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(5); }",
+ "desc": "Counts responses where between 16 and 32 milliseconds was spent within the Recursor",
+ "longdesc": "Not yet proven to be reliable",
},
{
- 'name': 'x-ourtime-slow',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(6); }',
- 'desc': 'Counts responses where more than 32 milliseconds was spent within the Recursor',
- 'longdesc': 'Not yet proven to be reliable'
+ "name": "x-ourtime-slow",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::ourtime).getCount(6); }",
+ "desc": "Counts responses where more than 32 milliseconds was spent within the Recursor",
+ "longdesc": "Not yet proven to be reliable",
},
{
- 'name': 'auth4-answers0-1',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::auth4Answers).getCount(0); }',
- 'desc': 'Number of IPv4 queries answered in less than 1 ms',
- 'snmp': 27,
+ "name": "auth4-answers0-1",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::auth4Answers).getCount(0); }",
+ "desc": "Number of IPv4 queries answered in less than 1 ms",
+ "snmp": 27,
},
{
- 'name': 'auth4-answers1-10',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::auth4Answers).getCount(1); }',
- 'desc': 'Number of IPv4 queries answered in 1-10 ms',
- 'snmp': 28,
+ "name": "auth4-answers1-10",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::auth4Answers).getCount(1); }",
+ "desc": "Number of IPv4 queries answered in 1-10 ms",
+ "snmp": 28,
},
{
- 'name': 'auth4-answers10-100',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::auth4Answers).getCount(2); }',
- 'desc': 'Number of IPv4 queries answered in 10-100 ms',
- 'snmp': 29,
+ "name": "auth4-answers10-100",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::auth4Answers).getCount(2); }",
+ "desc": "Number of IPv4 queries answered in 10-100 ms",
+ "snmp": 29,
},
{
- 'name': 'auth4-answers100-1000',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::auth4Answers).getCount(3); }',
- 'desc': 'Number of IPv4 queries answered in 100-1000 ms',
- 'snmp': 30,
+ "name": "auth4-answers100-1000",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::auth4Answers).getCount(3); }",
+ "desc": "Number of IPv4 queries answered in 100-1000 ms",
+ "snmp": 30,
},
{
- 'name': 'auth4-answers-slow',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::auth4Answers).getCount(4); }',
- 'desc': 'Number of IPv4 queries answered in more than 1000 ms',
- 'snmp': 31,
- 'snmpname': 'auth4Answersslow',
+ "name": "auth4-answers-slow",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::auth4Answers).getCount(4); }",
+ "desc": "Number of IPv4 queries answered in more than 1000 ms",
+ "snmp": 31,
+ "snmpname": "auth4Answersslow",
},
{
- 'name': 'auth6-answers0-1',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::auth6Answers).getCount(0); }',
- 'desc': 'Number of IPv6 queries answered in less than 1 ms',
- 'snmp': 32,
+ "name": "auth6-answers0-1",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::auth6Answers).getCount(0); }",
+ "desc": "Number of IPv6 queries answered in less than 1 ms",
+ "snmp": 32,
},
{
- 'name': 'auth6-answers1-10',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::auth6Answers).getCount(1); }',
- 'desc': 'Number of IPv6 queries answered in 1-10 ms',
- 'snmp': 33,
+ "name": "auth6-answers1-10",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::auth6Answers).getCount(1); }",
+ "desc": "Number of IPv6 queries answered in 1-10 ms",
+ "snmp": 33,
},
{
- 'name': 'auth6-answers10-100',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::auth6Answers).getCount(2); }',
- 'desc': 'Number of IPv6 queries answered in 10-100 ms',
- 'snmp': 34,
+ "name": "auth6-answers10-100",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::auth6Answers).getCount(2); }",
+ "desc": "Number of IPv6 queries answered in 10-100 ms",
+ "snmp": 34,
},
{
- 'name': 'auth6-answers100-1000',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::auth6Answers).getCount(3); }',
- 'desc': 'Number of IPv6 queries answered in 100-1000 ms',
- 'snmp': 35,
+ "name": "auth6-answers100-1000",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::auth6Answers).getCount(3); }",
+ "desc": "Number of IPv6 queries answered in 100-1000 ms",
+ "snmp": 35,
},
{
- 'name': 'auth6-answers-slow',
- 'lambda': '[]() { return g_Counters.sum(rec::Histogram::auth6Answers).getCount(4); }',
- 'desc': 'Number of IPv6 queries answered in more than 1000 ms',
- 'snmp': 36,
+ "name": "auth6-answers-slow",
+ "lambda": "[]() { return g_Counters.sum(rec::Histogram::auth6Answers).getCount(4); }",
+ "desc": "Number of IPv6 queries answered in more than 1000 ms",
+ "snmp": 36,
},
{
- 'name': 'qa-latency',
- 'lambda': '[]() { return round(g_Counters.avg(rec::DoubleWAvgCounter::avgLatencyUsec)); }',
- 'ptype': 'gauge',
- 'desc': 'Shows the current latency average, in microseconds, exponentially weighted over past \'latency-statistic-size\' packets',
- 'snmp': 37,
+ "name": "qa-latency",
+ "lambda": "[]() { return round(g_Counters.avg(rec::DoubleWAvgCounter::avgLatencyUsec)); }",
+ "ptype": "gauge",
+ "desc": "Shows the current latency average, in microseconds, exponentially weighted over past 'latency-statistic-size' packets",
+ "snmp": 37,
},
{
- 'name': 'x-our-latency',
- 'lambda': '[]() { return round(g_Counters.avg(rec::DoubleWAvgCounter::avgLatencyOursUsec)); }',
- 'ptype': 'gauge',
- 'desc': 'Shows the averaged time spent within PowerDNS, in microseconds, exponentially weighted over past \'latency-statistic-size\' packets',
+ "name": "x-our-latency",
+ "lambda": "[]() { return round(g_Counters.avg(rec::DoubleWAvgCounter::avgLatencyOursUsec)); }",
+ "ptype": "gauge",
+ "desc": "Shows the averaged time spent within PowerDNS, in microseconds, exponentially weighted over past 'latency-statistic-size' packets",
},
{
- 'name': 'unexpected-packets',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::unexpectedCount); }',
- 'desc': 'Number of unexpected packets',
- 'snmp': 38,
+ "name": "unexpected-packets",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::unexpectedCount); }",
+ "desc": "Number of unexpected packets",
+ "snmp": 38,
},
{
- 'name': 'case-mismatches',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::caseMismatchCount); }',
- 'desc': 'Number of case mismatches',
- 'snmp': 39,
+ "name": "case-mismatches",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::caseMismatchCount); }",
+ "desc": "Number of case mismatches",
+ "snmp": 39,
},
{
- 'name': 'spoof-prevents',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::spoofCount); }',
- 'desc': 'Number of spoof prevents',
- 'snmp': 40,
+ "name": "spoof-prevents",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::spoofCount); }",
+ "desc": "Number of spoof prevents",
+ "snmp": 40,
},
{
- 'name': 'nsset-invalidations',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::nsSetInvalidations); }',
- 'desc': 'Number of nsset invalidations',
- 'snmp': 41,
+ "name": "nsset-invalidations",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::nsSetInvalidations); }",
+ "desc": "Number of nsset invalidations",
+ "snmp": 41,
},
{
- 'name': 'resource-limits',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::resourceLimits); }',
- 'desc': 'Number of resolution aborted because of a local resource limit',
- 'snmp': 42,
+ "name": "resource-limits",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::resourceLimits); }",
+ "desc": "Number of resolution aborted because of a local resource limit",
+ "snmp": 42,
},
{
- 'name': 'over-capacity-drops',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::overCapacityDrops); }',
- 'desc': 'Number of queries dropped because the threads limit was reached',
- 'snmp': 43,
+ "name": "over-capacity-drops",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::overCapacityDrops); }",
+ "desc": "Number of queries dropped because the threads limit was reached",
+ "snmp": 43,
},
{
- 'name': 'policy-drops',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::policyDrops); }',
- 'desc': 'Number of queries dropped because of a policy',
- 'snmp': 44,
+ "name": "policy-drops",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::policyDrops); }",
+ "desc": "Number of queries dropped because of a policy",
+ "snmp": 44,
},
{
- 'name': 'no-packet-error',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::noPacketError); }',
- 'desc': 'Number of calls to recvmsg() that returned no packet even though the socket was ready',
- 'snmp': 45,
+ "name": "no-packet-error",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::noPacketError); }",
+ "desc": "Number of calls to recvmsg() that returned no packet even though the socket was ready",
+ "snmp": 45,
},
{
- 'name': 'dlg-only-drops',
- 'desc': 'Obsolete',
- 'snmp': 46,
+ "name": "dlg-only-drops",
+ "desc": "Obsolete",
+ "snmp": 46,
},
{
- 'name': 'ignored-packets',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::ignoredCount); }',
- 'desc': 'Number of ignored packets',
- 'snmp': 47,
+ "name": "ignored-packets",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::ignoredCount); }",
+ "desc": "Number of ignored packets",
+ "snmp": 47,
},
{
- 'name': 'max-mthread-stack',
- 'lambda': '[] { return g_Counters.max(rec::Counter::maxMThreadStackUsage); }',
- 'ptype': 'counter',
- 'desc': 'Maximum amount of the mthread stack ever used',
- 'snmp': 48,
+ "name": "max-mthread-stack",
+ "lambda": "[] { return g_Counters.max(rec::Counter::maxMThreadStackUsage); }",
+ "ptype": "counter",
+ "desc": "Maximum amount of the mthread stack ever used",
+ "snmp": 48,
},
{
- 'name': 'negcache-entries',
- 'lambda': 'getNegCacheSize',
- 'ptype': 'gauge',
- 'snmp': 49,
- 'desc': 'Number of negcache entries',
+ "name": "negcache-entries",
+ "lambda": "getNegCacheSize",
+ "ptype": "gauge",
+ "snmp": 49,
+ "desc": "Number of negcache entries",
},
{
- 'name': 'throttle-entries',
- 'lambda': 'SyncRes::getThrottledServersSize',
- 'ptype': 'gauge',
- 'snmp': 50,
- 'desc': 'Number of throttle entries',
+ "name": "throttle-entries",
+ "lambda": "SyncRes::getThrottledServersSize",
+ "ptype": "gauge",
+ "snmp": 50,
+ "desc": "Number of throttle entries",
},
{
- 'name': 'nsspeeds-entries',
- 'lambda': 'SyncRes::getNSSpeedsSize',
- 'ptype': 'gauge',
- 'snmp': 51,
- 'desc': 'Number of nsspeeds entries',
+ "name": "nsspeeds-entries",
+ "lambda": "SyncRes::getNSSpeedsSize",
+ "ptype": "gauge",
+ "snmp": 51,
+ "desc": "Number of nsspeeds entries",
},
{
- 'name': 'failed-host-entries',
- 'lambda': 'SyncRes::getFailedServersSize',
- 'ptype': 'gauge',
- 'snmp': 52,
- 'desc': 'Number of entries in the failed NS cache',
+ "name": "failed-host-entries",
+ "lambda": "SyncRes::getFailedServersSize",
+ "ptype": "gauge",
+ "snmp": 52,
+ "desc": "Number of entries in the failed NS cache",
},
{
- 'name': 'concurrent-queries',
- 'lambda': 'getConcurrentQueries',
- 'ptype': 'gauge',
- 'snmp': 53,
- 'desc': 'Number of concurrent queries',
+ "name": "concurrent-queries",
+ "lambda": "getConcurrentQueries",
+ "ptype": "gauge",
+ "snmp": 53,
+ "desc": "Number of concurrent queries",
},
{
- 'name': 'security-status',
- 'lambda': '&g_security_status',
- 'ptype': 'gauge',
- 'snmp': 54,
- 'desc': 'Current security status',
+ "name": "security-status",
+ "lambda": "&g_security_status",
+ "ptype": "gauge",
+ "snmp": 54,
+ "desc": "Current security status",
},
{
- 'name': 'outgoing-timeouts',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::outgoingtimeouts); }',
- 'snmp': 55,
- 'desc': 'Number of outgoing timeouts',
+ "name": "outgoing-timeouts",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::outgoingtimeouts); }",
+ "snmp": 55,
+ "desc": "Number of outgoing timeouts",
},
{
- 'name': 'outgoing4-timeouts',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::outgoing4timeouts); }',
- 'snmp': 56,
- 'desc': 'Number of IPv4 outgoing timeouts',
+ "name": "outgoing4-timeouts",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::outgoing4timeouts); }",
+ "snmp": 56,
+ "desc": "Number of IPv4 outgoing timeouts",
},
{
- 'name': 'outgoing6-timeouts',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::outgoing6timeouts); }',
- 'snmp': 57,
- 'desc': 'Number of IPv6 outgoing timeouts',
+ "name": "outgoing6-timeouts",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::outgoing6timeouts); }",
+ "snmp": 57,
+ "desc": "Number of IPv6 outgoing timeouts",
},
{
- 'name': 'auth-zone-queries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::authzonequeries); }',
- 'desc': 'Number of queries to locally hosted authoritative zones (\'setting-auth-zones\')',
+ "name": "auth-zone-queries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::authzonequeries); }",
+ "desc": "Number of queries to locally hosted authoritative zones ('setting-auth-zones')",
},
{
- 'name': 'tcp-outqueries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::tcpoutqueries); }',
- 'snmp': 58,
- 'desc': 'Number of outgoing TCP queries sent',
+ "name": "tcp-outqueries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::tcpoutqueries); }",
+ "snmp": 58,
+ "desc": "Number of outgoing TCP queries sent",
},
{
- 'name': 'all-outqueries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::outqueries); }',
- 'snmp': 59,
- 'desc': 'Number of outgoing queries',
+ "name": "all-outqueries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::outqueries); }",
+ "snmp": 59,
+ "desc": "Number of outgoing queries",
},
{
- 'name': 'ipv6-outqueries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::ipv6queries); }',
- 'snmp': 60,
- 'desc': 'Number of IPv6 outgoing queries sent',
+ "name": "ipv6-outqueries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::ipv6queries); }",
+ "snmp": 60,
+ "desc": "Number of IPv6 outgoing queries sent",
},
{
- 'name': 'throttled-outqueries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::throttledqueries); }',
- 'snmp': 61,
- 'desc': 'Number of throttled outgoing queries',
+ "name": "throttled-outqueries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::throttledqueries); }",
+ "snmp": 61,
+ "desc": "Number of throttled outgoing queries",
},
{
- 'name': 'dont-outqueries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::dontqueries); }',
- 'snmp': 62,
- 'desc': 'Number of outgoing queries not sent because of a \'dont-query\' setting',
+ "name": "dont-outqueries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::dontqueries); }",
+ "snmp": 62,
+ "desc": "Number of outgoing queries not sent because of a 'dont-query' setting",
},
{
- 'name': 'throttled-out',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::throttledqueries); }',
- 'desc': 'Number of throttled outgoing queries',
+ "name": "throttled-out",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::throttledqueries); }",
+ "desc": "Number of throttled outgoing queries",
},
{
- 'name': 'unreachables',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::unreachables); }',
- 'snmp': 63,
- 'desc': 'Number of errors due to an unreachable server',
+ "name": "unreachables",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::unreachables); }",
+ "snmp": 63,
+ "desc": "Number of errors due to an unreachable server",
},
{
- 'name': 'ecs-queries',
- 'lambda': '&SyncRes::s_ecsqueries',
- 'desc': 'Number of outgoing queries adorned with an EDNS Client Subnet option',
+ "name": "ecs-queries",
+ "lambda": "&SyncRes::s_ecsqueries",
+ "desc": "Number of outgoing queries adorned with an EDNS Client Subnet option",
},
{
- 'name': 'ecs-responses',
- 'lambda': '&SyncRes::s_ecsresponses',
- 'desc': 'Number of responses received from authoritative servers with an EDNS Client Subnet option we used',
+ "name": "ecs-responses",
+ "lambda": "&SyncRes::s_ecsresponses",
+ "desc": "Number of responses received from authoritative servers with an EDNS Client Subnet option we used",
},
{
- 'name': 'chain-resends',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::chainResends); }',
- 'snmp': 64,
- 'desc': 'Number of chain resends',
+ "name": "chain-resends",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::chainResends); }",
+ "snmp": 64,
+ "desc": "Number of chain resends",
},
{
- 'name': 'tcp-clients',
- 'lambda': '[] { return TCPConnection::getCurrentConnections(); }',
- 'ptype': 'gauge',
- 'snmp': 65,
- 'desc': 'Number of TCP clients',
+ "name": "tcp-clients",
+ "lambda": "[] { return TCPConnection::getCurrentConnections(); }",
+ "ptype": "gauge",
+ "snmp": 65,
+ "desc": "Number of TCP clients",
},
{
- 'name': 'udp-recvbuf-errors',
- 'lambda': '[] { return udpErrorStats("udp-recvbuf-errors"); }',
- 'ifdef': '__linux',
- 'snmp': 66,
- 'desc': 'Number of UDP recvbuf errors (Linux only)',
+ "name": "udp-recvbuf-errors",
+ "lambda": '[] { return udpErrorStats("udp-recvbuf-errors"); }',
+ "ifdef": "__linux",
+ "snmp": 66,
+ "desc": "Number of UDP recvbuf errors (Linux only)",
},
{
- 'name': 'udp-sndbuf-errors',
- 'lambda': '[] { return udpErrorStats("udp-sndbuf-errors"); }',
- 'snmp': 67,
- 'ifdef': '__linux',
- 'desc': 'Number of UDP sndbuf errors (Linux only)',
+ "name": "udp-sndbuf-errors",
+ "lambda": '[] { return udpErrorStats("udp-sndbuf-errors"); }',
+ "snmp": 67,
+ "ifdef": "__linux",
+ "desc": "Number of UDP sndbuf errors (Linux only)",
},
{
- 'name': 'udp-noport-errors',
- 'lambda': '[] { return udpErrorStats("udp-noport-errors"); }',
- 'ifdef': '__linux',
- 'snmp': 68,
- 'desc': 'Number of UDP noport errors (Linux only)',
+ "name": "udp-noport-errors",
+ "lambda": '[] { return udpErrorStats("udp-noport-errors"); }',
+ "ifdef": "__linux",
+ "snmp": 68,
+ "desc": "Number of UDP noport errors (Linux only)",
},
{
- 'name': 'udp-in-errors',
- 'lambda': '[] { return udpErrorStats("udp-in-errors"); }',
- 'ifdef': '__linux',
- 'snmp': 69,
- 'snmpname': 'udpinErrors',
- 'desc': 'Number of UDP in errors (Linux only)',
+ "name": "udp-in-errors",
+ "lambda": '[] { return udpErrorStats("udp-in-errors"); }',
+ "ifdef": "__linux",
+ "snmp": 69,
+ "snmpname": "udpinErrors",
+ "desc": "Number of UDP in errors (Linux only)",
},
{
- 'name': 'edns-ping-matches',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::ednsPingMatches); }',
- 'snmp': 70,
- 'desc': 'Number of EDNS Ping matches',
+ "name": "edns-ping-matches",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::ednsPingMatches); }",
+ "snmp": 70,
+ "desc": "Number of EDNS Ping matches",
},
{
- 'name': 'edns-ping-mismatches',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::ednsPingMismatches); }',
- 'snmp': 71,
- 'desc': 'Number of EDNS Ping mismatches',
+ "name": "edns-ping-mismatches",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::ednsPingMismatches); }",
+ "snmp": 71,
+ "desc": "Number of EDNS Ping mismatches",
},
{
- 'name': 'dnssec-queries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::dnssecQueries); }',
- 'snmp': 72,
- 'desc': 'Number of DNSSEC queries',
+ "name": "dnssec-queries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::dnssecQueries); }",
+ "snmp": 72,
+ "desc": "Number of DNSSEC queries",
},
{
- 'name': 'noping-outqueries', # XXX obsolete?
- 'lambda': '[] { return g_Counters.sum(rec::Counter::noPingOutQueries); }',
- 'snmp': 73,
- 'desc': 'Number of outgoing queries without ping',
+ "name": "noping-outqueries", # XXX obsolete?
+ "lambda": "[] { return g_Counters.sum(rec::Counter::noPingOutQueries); }",
+ "snmp": 73,
+ "desc": "Number of outgoing queries without ping",
},
{
- 'name': 'noedns-outqueries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::noEdnsOutQueries); }',
- 'snmp': 74,
- 'desc': 'Number of outgoing queries without EDNS',
+ "name": "noedns-outqueries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::noEdnsOutQueries); }",
+ "snmp": 74,
+ "desc": "Number of outgoing queries without EDNS",
},
{
- 'name': 'uptime',
- 'lambda': '[] { return time(nullptr) - s_startupTime; }',
- 'snmp': 75,
- 'desc': 'Process uptime in seconds',
+ "name": "uptime",
+ "lambda": "[] { return time(nullptr) - s_startupTime; }",
+ "snmp": 75,
+ "desc": "Process uptime in seconds",
},
{
- 'name': 'real-memory-usage',
- 'lambda': '[] { return getRealMemoryUsage(string()); }',
- 'ptype': 'gauge',
- 'snmp': 76,
- 'desc': 'Memory usage',
+ "name": "real-memory-usage",
+ "lambda": "[] { return getRealMemoryUsage(string()); }",
+ "ptype": "gauge",
+ "snmp": 76,
+ "desc": "Memory usage",
},
{
- 'name': 'fd-usage',
- 'lambda': '[] { return getOpenFileDescriptors(string()); }',
- 'ptype': 'gauge',
- 'snmp': 77,
- 'desc': 'File descriptors usage',
+ "name": "fd-usage",
+ "lambda": "[] { return getOpenFileDescriptors(string()); }",
+ "ptype": "gauge",
+ "snmp": 77,
+ "desc": "File descriptors usage",
},
-
{
- 'name': 'user-msec',
- 'lambda': 'getUserTimeMsec',
- 'ptype': 'counter',
- 'snmp': 78,
- 'desc': 'CPU usage (user) in ms',
+ "name": "user-msec",
+ "lambda": "getUserTimeMsec",
+ "ptype": "counter",
+ "snmp": 78,
+ "desc": "CPU usage (user) in ms",
},
{
- 'name': 'sys-msec',
- 'lambda': 'getSysTimeMsec',
- 'ptype': 'counter',
- 'snmp': 79,
- 'desc': 'CPU usage (system) in ms',
+ "name": "sys-msec",
+ "lambda": "getSysTimeMsec",
+ "ptype": "counter",
+ "snmp": 79,
+ "desc": "CPU usage (system) in ms",
},
{
- 'name': 'dnssec-validations',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::dnssecValidations); }',
- 'snmp': 80,
- 'desc': 'Number of responses sent, packet-cache hits excluded, for which a DNSSEC validation was requested by either the client or the configuration',
+ "name": "dnssec-validations",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::dnssecValidations); }",
+ "snmp": 80,
+ "desc": "Number of responses sent, packet-cache hits excluded, for which a DNSSEC validation was requested by either the client or the configuration",
},
{
- 'name': 'dnssec-result-insecure',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::Insecure); }',
- 'snmp': 81,
- 'desc': 'Number of responses sent, excluding packet-cache hits, that were in the DNSSEC insecure state',
+ "name": "dnssec-result-insecure",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::Insecure); }",
+ "snmp": 81,
+ "desc": "Number of responses sent, excluding packet-cache hits, that were in the DNSSEC insecure state",
},
{
- 'name': 'dnssec-result-secure',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::Secure); }',
- 'snmp': 82,
- 'desc': 'Number of responses sent, excluding packet-cache hits, that were in the DNSSEC secure state',
+ "name": "dnssec-result-secure",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::Secure); }",
+ "snmp": 82,
+ "desc": "Number of responses sent, excluding packet-cache hits, that were in the DNSSEC secure state",
},
{
- 'name': 'dnssec-result-bogus',
- 'lambda': '''[]() {
+ "name": "dnssec-result-bogus",
+ "lambda": """[]() {
std::set<vState> const bogusStates = {vState::BogusNoValidDNSKEY, vState::BogusInvalidDenial, vState::BogusUnableToGetDSs, vState::BogusUnableToGetDNSKEYs, vState::BogusSelfSignedDS, vState::BogusNoRRSIG, vState::BogusNoValidRRSIG, vState::BogusMissingNegativeIndication, vState::BogusSignatureNotYetValid, vState::BogusSignatureExpired, vState::BogusUnsupportedDNSKEYAlgo, vState::BogusUnsupportedDSDigestType, vState::BogusNoZoneKeyBitSet, vState::BogusRevokedDNSKEY, vState::BogusInvalidDNSKEYProtocol};
auto counts = g_Counters.sum(rec::DNSSECHistogram::dnssec);
uint64_t total = 0;
total += counts.at(state);
}
return total;
- }''',
- 'snmp': 83,
- 'desc': 'Number of responses sent, excluding packet-cache hits, that were in the DNSSEC bogus state',
+ }""",
+ "snmp": 83,
+ "desc": "Number of responses sent, excluding packet-cache hits, that were in the DNSSEC bogus state",
},
{
- 'name': 'dnssec-result-indeterminate',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::Indeterminate); }',
- 'snmp': 84,
- 'desc': 'Number of responses sent, excluding packet-cache hits, that were in the DNSSEC indeterminate state',
+ "name": "dnssec-result-indeterminate",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::Indeterminate); }",
+ "snmp": 84,
+ "desc": "Number of responses sent, excluding packet-cache hits, that were in the DNSSEC indeterminate state",
},
{
- 'name': 'dnssec-result-nta',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::NTA); }',
- 'snmp': 85,
- 'desc': 'Number of responses sent, excluding packet-cache hits, that were in the DNSSEC NTA state',
+ "name": "dnssec-result-nta",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::NTA); }",
+ "snmp": 85,
+ "desc": "Number of responses sent, excluding packet-cache hits, that were in the DNSSEC NTA state",
},
{
- 'name': 'policy-result-noaction',
- 'lambda': '[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::NoAction); }',
- 'snmp': 86,
- 'desc': 'Number of policy-mandated no-action results',
+ "name": "policy-result-noaction",
+ "lambda": "[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::NoAction); }",
+ "snmp": 86,
+ "desc": "Number of policy-mandated no-action results",
},
{
- 'name': 'policy-result-drop',
- 'lambda': '[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::Drop); }',
- 'snmp': 87,
- 'desc': 'Number of policy-mandated drops',
+ "name": "policy-result-drop",
+ "lambda": "[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::Drop); }",
+ "snmp": 87,
+ "desc": "Number of policy-mandated drops",
},
{
- 'name': 'policy-result-nxdomain',
- 'lambda': '[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::NXDOMAIN); }',
- 'snmp': 88,
- 'desc': 'Number of policy-mandated NXdomain results',
+ "name": "policy-result-nxdomain",
+ "lambda": "[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::NXDOMAIN); }",
+ "snmp": 88,
+ "desc": "Number of policy-mandated NXdomain results",
},
{
- 'name': 'policy-result-nodata',
- 'lambda': '[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::NODATA); }',
- 'snmp': 89,
- 'desc': 'Number of policy-mandated nodata results',
+ "name": "policy-result-nodata",
+ "lambda": "[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::NODATA); }",
+ "snmp": 89,
+ "desc": "Number of policy-mandated nodata results",
},
{
- 'name': 'policy-result-truncate',
- 'lambda': '[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::Truncate); }',
- 'snmp': 90,
- 'desc': 'Number of policy-mandated truncate results',
+ "name": "policy-result-truncate",
+ "lambda": "[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::Truncate); }",
+ "snmp": 90,
+ "desc": "Number of policy-mandated truncate results",
},
{
- 'name': 'policy-result-custom',
- 'lambda': '[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::Custom); }',
- 'snmp': 91,
- 'desc': 'Number of policy-mandated custom results',
+ "name": "policy-result-custom",
+ "lambda": "[] { return g_Counters.sum(rec::PolicyHistogram::policy).at(DNSFilterEngine::PolicyKind::Custom); }",
+ "snmp": 91,
+ "desc": "Number of policy-mandated custom results",
},
{
- 'name': 'query-pipe-full-drops',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::queryPipeFullDrops); }',
- 'desc': 'Number of queries dropped because the query distribution pipe was full',
- 'snmp': 92,
+ "name": "query-pipe-full-drops",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::queryPipeFullDrops); }",
+ "desc": "Number of queries dropped because the query distribution pipe was full",
+ "snmp": 92,
},
{
- 'name': 'truncated-drops',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::truncatedDrops); }',
- 'desc': 'Number of queries dropped because they were larger than 512 bytes',
- 'snmp': 93,
+ "name": "truncated-drops",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::truncatedDrops); }",
+ "desc": "Number of queries dropped because they were larger than 512 bytes",
+ "snmp": 93,
},
{
- 'name': 'empty-queries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::emptyQueriesCount); }',
- 'desc': 'Number of queries dropped because they had a QD count of 0',
- 'snmp': 94,
+ "name": "empty-queries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::emptyQueriesCount); }",
+ "desc": "Number of queries dropped because they had a QD count of 0",
+ "snmp": 94,
},
{
- 'name': 'dnssec-authentic-data-queries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::dnssecAuthenticDataQueries); }',
- 'snmp': 95,
- 'desc': 'Number of queries received with the AD bit set',
+ "name": "dnssec-authentic-data-queries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::dnssecAuthenticDataQueries); }",
+ "snmp": 95,
+ "desc": "Number of queries received with the AD bit set",
},
{
- 'name': 'dnssec-check-disabled-queries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::dnssecCheckDisabledQueries); }',
- 'snmp': 96,
- 'desc': 'Number of queries received with the CD bit set',
+ "name": "dnssec-check-disabled-queries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::dnssecCheckDisabledQueries); }",
+ "snmp": 96,
+ "desc": "Number of queries received with the CD bit set",
},
{
- 'name': 'variable-responses',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::variableResponses); }',
- 'snmp': 97,
- 'desc': 'Number of variable responses',
+ "name": "variable-responses",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::variableResponses); }",
+ "snmp": 97,
+ "desc": "Number of variable responses",
},
{
- 'name': 'special-memory-usage',
- 'lambda': '[] { return getSpecialMemoryUsage(string()); }',
- 'ptype': 'gauge',
- 'snmp': 98,
- 'desc': 'Memory usage (more precise but expensive to retrieve)',
+ "name": "special-memory-usage",
+ "lambda": "[] { return getSpecialMemoryUsage(string()); }",
+ "ptype": "gauge",
+ "snmp": 98,
+ "desc": "Memory usage (more precise but expensive to retrieve)",
},
{
- 'name': 'rebalanced-queries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::rebalancedQueries); }',
- 'snmp': 99,
- 'desc': 'Number of queries re-distributed because the first selected worker thread was above the target load',
+ "name": "rebalanced-queries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::rebalancedQueries); }",
+ "snmp": 99,
+ "desc": "Number of queries re-distributed because the first selected worker thread was above the target load",
},
{
- 'name': 'qname-min-fallback-success',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::qnameminfallbacksuccess); }',
- 'snmp': 100,
- 'desc': 'Number of successful queries due to fallback mechanism within \'qname-minimization\' setting',
+ "name": "qname-min-fallback-success",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::qnameminfallbacksuccess); }",
+ "snmp": 100,
+ "desc": "Number of successful queries due to fallback mechanism within 'qname-minimization' setting",
},
{
- 'name': 'proxy-protocol-invalid',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::proxyProtocolInvalidCount); }',
- 'snmp': 101,
- 'desc': 'Number of invalid proxy protocol headers received',
+ "name": "proxy-protocol-invalid",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::proxyProtocolInvalidCount); }",
+ "snmp": 101,
+ "desc": "Number of invalid proxy protocol headers received",
},
{
- 'name': 'record-cache-contended',
- 'lambda': '[]() { return g_recCache->stats().first; }',
- 'desc': 'Number of contended record cache lock acquisitions',
- 'snmp': 102,
+ "name": "record-cache-contended",
+ "lambda": "[]() { return g_recCache->stats().first; }",
+ "desc": "Number of contended record cache lock acquisitions",
+ "snmp": 102,
},
{
- 'name': 'record-cache-acquired',
- 'lambda': '[]() { return g_recCache->stats().second; }',
- 'desc': 'Number of record cache lock acquisitions',
- 'snmp': 103,
+ "name": "record-cache-acquired",
+ "lambda": "[]() { return g_recCache->stats().second; }",
+ "desc": "Number of record cache lock acquisitions",
+ "snmp": 103,
},
{
- 'name': 'nod-lookups-dropped-oversize',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::nodLookupsDroppedOversize); }',
- 'snmp': 104,
- 'desc': 'Number of NOD lookups dropped because they would exceed the maximum name length',
+ "name": "nod-lookups-dropped-oversize",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::nodLookupsDroppedOversize); }",
+ "snmp": 104,
+ "desc": "Number of NOD lookups dropped because they would exceed the maximum name length",
},
{
- 'name': 'taskqueue-pushed',
- 'lambda': '[]() { return getTaskPushes(); }',
- 'snmp': 105,
- 'snmpname': 'taskQueuePushed',
- 'desc': 'Number of tasks pushed to the taskqueues',
+ "name": "taskqueue-pushed",
+ "lambda": "[]() { return getTaskPushes(); }",
+ "snmp": 105,
+ "snmpname": "taskQueuePushed",
+ "desc": "Number of tasks pushed to the taskqueues",
},
{
- 'name': 'taskqueue-expired',
- 'lambda': '[]() { return getTaskExpired(); }',
- 'snmp': 106,
- 'snmpname': 'taskQueueExpired',
- 'desc': 'Number of tasks expired before they could be run',
+ "name": "taskqueue-expired",
+ "lambda": "[]() { return getTaskExpired(); }",
+ "snmp": 106,
+ "snmpname": "taskQueueExpired",
+ "desc": "Number of tasks expired before they could be run",
},
{
- 'name': 'taskqueue-size',
- 'lambda': '[]() { return getTaskSize(); }',
- 'ptype': 'gauge',
- 'snmp': 107,
- 'snmpname': 'taskQueueSize',
- 'desc': 'Number of tasks currently in the taskqueues',
+ "name": "taskqueue-size",
+ "lambda": "[]() { return getTaskSize(); }",
+ "ptype": "gauge",
+ "snmp": 107,
+ "snmpname": "taskQueueSize",
+ "desc": "Number of tasks currently in the taskqueues",
},
{
- 'name': 'aggressive-nsec-cache-entries',
- 'lambda': '[]() { return g_aggressiveNSECCache ? g_aggressiveNSECCache->getEntriesCount() : 0; }',
- 'desc': 'Number of entries in the aggressive NSEC cache',
- 'snmp': 108,
+ "name": "aggressive-nsec-cache-entries",
+ "lambda": "[]() { return g_aggressiveNSECCache ? g_aggressiveNSECCache->getEntriesCount() : 0; }",
+ "desc": "Number of entries in the aggressive NSEC cache",
+ "snmp": 108,
},
{
- 'name': 'aggressive-nsec-cache-nsec-hits',
- 'lambda': '[]() { return g_aggressiveNSECCache ? g_aggressiveNSECCache->getNSECHits() : 0; }',
- 'desc': 'Number of NSEC-related hits from the aggressive NSEC cache',
- 'snmp': 109,
+ "name": "aggressive-nsec-cache-nsec-hits",
+ "lambda": "[]() { return g_aggressiveNSECCache ? g_aggressiveNSECCache->getNSECHits() : 0; }",
+ "desc": "Number of NSEC-related hits from the aggressive NSEC cache",
+ "snmp": 109,
},
{
- 'name': 'aggressive-nsec-cache-nsec3-hits',
- 'lambda': '[]() { return g_aggressiveNSECCache ? g_aggressiveNSECCache->getNSEC3Hits() : 0; }',
- 'desc': 'Number of NSEC3-related hits from the aggressive NSEC cache',
- 'snmp': 110,
+ "name": "aggressive-nsec-cache-nsec3-hits",
+ "lambda": "[]() { return g_aggressiveNSECCache ? g_aggressiveNSECCache->getNSEC3Hits() : 0; }",
+ "desc": "Number of NSEC3-related hits from the aggressive NSEC cache",
+ "snmp": 110,
},
{
- 'name': 'aggressive-nsec-cache-nsec-wc-hits',
- 'lambda': '[]() { return g_aggressiveNSECCache ? g_aggressiveNSECCache->getNSECWildcardHits() : 0; }',
- 'desc': 'Number of answers synthesized from the NSEC aggressive cache',
- 'snmp': 111
+ "name": "aggressive-nsec-cache-nsec-wc-hits",
+ "lambda": "[]() { return g_aggressiveNSECCache ? g_aggressiveNSECCache->getNSECWildcardHits() : 0; }",
+ "desc": "Number of answers synthesized from the NSEC aggressive cache",
+ "snmp": 111,
},
{
- 'name': 'aggressive-nsec-cache-nsec3-wc-hits',
- 'lambda': '[]() { return g_aggressiveNSECCache ? g_aggressiveNSECCache->getNSEC3WildcardHits() : 0; }',
- 'desc': 'Number of answers synthesized from the NSEC3 aggressive cache',
- 'snmp': 112
+ "name": "aggressive-nsec-cache-nsec3-wc-hits",
+ "lambda": "[]() { return g_aggressiveNSECCache ? g_aggressiveNSECCache->getNSEC3WildcardHits() : 0; }",
+ "desc": "Number of answers synthesized from the NSEC3 aggressive cache",
+ "snmp": 112,
},
{
- 'name': 'dot-outqueries',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::dotoutqueries); }',
- 'snmp': 113,
- 'desc': 'Number of outgoing DoT queries',
+ "name": "dot-outqueries",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::dotoutqueries); }",
+ "snmp": 113,
+ "desc": "Number of outgoing DoT queries",
},
{
- 'name': 'dns64-prefix-answers',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::dns64prefixanswers); }',
- 'snmp': 114,
- 'desc': 'Number of answers generated by dns64-prefix matching',
+ "name": "dns64-prefix-answers",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::dns64prefixanswers); }",
+ "snmp": 114,
+ "desc": "Number of answers generated by dns64-prefix matching",
},
{
- 'name': 'almost-expired-pushed',
- 'lambda': '[]() { return getAlmostExpiredTasksPushed(); }',
- 'snmp': 115,
- 'desc': 'Number of almost-expired tasks pushed',
+ "name": "almost-expired-pushed",
+ "lambda": "[]() { return getAlmostExpiredTasksPushed(); }",
+ "snmp": 115,
+ "desc": "Number of almost-expired tasks pushed",
},
{
- 'name': 'almost-expired-run',
- 'lambda': '[]() { return getAlmostExpiredTasksRun(); }',
- 'snmp': 116,
- 'desc': 'Number of almost-expired tasks run to completion',
+ "name": "almost-expired-run",
+ "lambda": "[]() { return getAlmostExpiredTasksRun(); }",
+ "snmp": 116,
+ "desc": "Number of almost-expired tasks run to completion",
},
{
- 'name': 'almost-expired-exceptions',
- 'lambda': '[]() { return getAlmostExpiredTaskExceptions(); }',
- 'snmp': 117,
- 'desc': 'Number of almost-expired tasks that caused an exception',
+ "name": "almost-expired-exceptions",
+ "lambda": "[]() { return getAlmostExpiredTaskExceptions(); }",
+ "snmp": 117,
+ "desc": "Number of almost-expired tasks that caused an exception",
},
{
- 'name': 'udp-in-csum-errors',
- 'lambda': '[] { return udpErrorStats("udp-in-csum-errors"); }',
- 'ifdef': '__linux',
- 'snmp': 118,
- 'desc': 'Number of UDP in checksum errors (Linux only)',
+ "name": "udp-in-csum-errors",
+ "lambda": '[] { return udpErrorStats("udp-in-csum-errors"); }',
+ "ifdef": "__linux",
+ "snmp": 118,
+ "desc": "Number of UDP in checksum errors (Linux only)",
},
{
- 'name': 'udp6-recvbuf-errors',
- 'lambda': '[] { return udp6ErrorStats("udp6-recvbuf-errors"); }',
- 'ifdef': '__linux',
- 'snmp': 119,
- 'desc': 'Number of UDP6 recvbuf errors (Linux only)',
+ "name": "udp6-recvbuf-errors",
+ "lambda": '[] { return udp6ErrorStats("udp6-recvbuf-errors"); }',
+ "ifdef": "__linux",
+ "snmp": 119,
+ "desc": "Number of UDP6 recvbuf errors (Linux only)",
},
{
- 'name': 'udp6-sndbuf-errors',
- 'lambda': '[] { return udp6ErrorStats("udp6-sndbuf-errors"); }',
- 'ifdef': '__linux',
- 'snmp': 120,
- 'desc': 'Number of UDP6 sndbuf errors (Linux only)',
+ "name": "udp6-sndbuf-errors",
+ "lambda": '[] { return udp6ErrorStats("udp6-sndbuf-errors"); }',
+ "ifdef": "__linux",
+ "snmp": 120,
+ "desc": "Number of UDP6 sndbuf errors (Linux only)",
},
{
- 'name': 'udp6-noport-errors',
- 'lambda': '[] { return udp6ErrorStats("udp6-noport-errors"); }',
- 'ifdef': '__linux',
- 'snmp': 121,
- 'desc': 'Number of UDP6 noport errors (Linux only)',
+ "name": "udp6-noport-errors",
+ "lambda": '[] { return udp6ErrorStats("udp6-noport-errors"); }',
+ "ifdef": "__linux",
+ "snmp": 121,
+ "desc": "Number of UDP6 noport errors (Linux only)",
},
{
- 'name': 'udp6-in-errors',
- 'lambda': '[] { return udp6ErrorStats("udp6-in-errors"); }',
- 'ifdef': '__linux',
- 'snmp': 122,
- 'snmpname': 'udp6inErrors',
- 'desc': 'Number of UDP6 in errors (Linux only)',
+ "name": "udp6-in-errors",
+ "lambda": '[] { return udp6ErrorStats("udp6-in-errors"); }',
+ "ifdef": "__linux",
+ "snmp": 122,
+ "snmpname": "udp6inErrors",
+ "desc": "Number of UDP6 in errors (Linux only)",
},
{
- 'name': 'udp6-in-csum-errors',
- 'ifdef': '__linux',
- 'lambda': '[] { return udp6ErrorStats("udp6-in-csum-errors"); }',
- 'snmp': 123,
- 'desc': 'Number of UDP6 in checksum errors (Linux only)',
+ "name": "udp6-in-csum-errors",
+ "ifdef": "__linux",
+ "lambda": '[] { return udp6ErrorStats("udp6-in-csum-errors"); }',
+ "snmp": 123,
+ "desc": "Number of UDP6 in checksum errors (Linux only)",
},
{
- 'name': 'cpu-iowait',
- 'lambda': '[] { return getCPUIOWait(string()); }',
- 'ifdef': '__linux',
- 'desc': 'Time spent waiting for I/O to complete by the whole system, in units of USER_HZ.'
+ "name": "cpu-iowait",
+ "lambda": "[] { return getCPUIOWait(string()); }",
+ "ifdef": "__linux",
+ "desc": "Time spent waiting for I/O to complete by the whole system, in units of USER_HZ.",
},
{
- 'name': 'cpu-steal',
- 'lambda': '[] { return getCPUSteal(string()); }',
- 'ifdef': '__linux',
- 'desc': 'Stolen time, which is the time spent by the whole system in other operating systems when running in a virtualized environment, in units of USER_HZ',
+ "name": "cpu-steal",
+ "lambda": "[] { return getCPUSteal(string()); }",
+ "ifdef": "__linux",
+ "desc": "Stolen time, which is the time spent by the whole system in other operating systems when running in a virtualized environment, in units of USER_HZ",
},
{
- 'name': 'cpu-msec-thread-n',
- 'lambda': '[]() { return toCPUStatsMap("cpu-msec"); }',
- 'desc': 'Number of milliseconds spent in thread n',
- 'ptype': 'multicounter',
- 'pname': 'cpu-msec-thread-0',
+ "name": "cpu-msec-thread-n",
+ "lambda": '[]() { return toCPUStatsMap("cpu-msec"); }',
+ "desc": "Number of milliseconds spent in thread n",
+ "ptype": "multicounter",
+ "pname": "cpu-msec-thread-0",
},
{
- 'name': 'memory-allocs',
- 'lambda': '[] { return g_mtracer->getAllocs(string()); }',
- 'ifdef': 'MALLOC_TRACE',
- 'desc': 'Only relevant for development and if malloc tracing is enabled',
+ "name": "memory-allocs",
+ "lambda": "[] { return g_mtracer->getAllocs(string()); }",
+ "ifdef": "MALLOC_TRACE",
+ "desc": "Only relevant for development and if malloc tracing is enabled",
},
{
- 'name': 'memory-alloc-flux',
- 'lambda': '[] { return g_mtracer->getAllocFlux(string()); }',
- 'ifdef': 'MALLOC_TRACE',
- 'desc': 'Only relevant for development and if malloc tracing is enabled',
+ "name": "memory-alloc-flux",
+ "lambda": "[] { return g_mtracer->getAllocFlux(string()); }",
+ "ifdef": "MALLOC_TRACE",
+ "desc": "Only relevant for development and if malloc tracing is enabled",
},
{
- 'name': 'memory-allocated',
- 'lambda': '[] { return g_mtracer->getTotAllocated(string()); }',
- 'ifdef': 'MALLOC_TRACE',
- 'desc': 'Only relevant for development and if malloc tracing is enabled',
+ "name": "memory-allocated",
+ "lambda": "[] { return g_mtracer->getTotAllocated(string()); }",
+ "ifdef": "MALLOC_TRACE",
+ "desc": "Only relevant for development and if malloc tracing is enabled",
},
{
- 'name': 'dnssec-result-bogus-no-valid-dnskey',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusNoValidDNSKEY); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a valid DNSKEY could not be found',
+ "name": "dnssec-result-bogus-no-valid-dnskey",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusNoValidDNSKEY); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a valid DNSKEY could not be found",
},
{
- 'name': 'dnssec-result-bogus-invalid-denial',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusInvalidDenial); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a valid denial of existence proof could not be found',
+ "name": "dnssec-result-bogus-invalid-denial",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusInvalidDenial); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a valid denial of existence proof could not be found",
},
{
- 'name': 'dnssec-result-bogus-unable-to-get-dss',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusUnableToGetDSs); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a valid DS could not be retrieved',
+ "name": "dnssec-result-bogus-unable-to-get-dss",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusUnableToGetDSs); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a valid DS could not be retrieved",
},
{
- 'name': 'dnssec-result-bogus-unable-to-get-dnskeys',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusUnableToGetDNSKEYs); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a valid DNSKEY could not be retrieved',
+ "name": "dnssec-result-bogus-unable-to-get-dnskeys",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusUnableToGetDNSKEYs); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a valid DNSKEY could not be retrieved",
},
{
- 'name': 'dnssec-result-bogus-self-signed-ds',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusSelfSignedDS); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a DS record was signed by itself',
+ "name": "dnssec-result-bogus-self-signed-ds",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusSelfSignedDS); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a DS record was signed by itself",
},
{
- 'name': 'dnssec-result-bogus-no-rrsig',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusNoRRSIG); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because required RRSIG records were not present in an answer',
+ "name": "dnssec-result-bogus-no-rrsig",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusNoRRSIG); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because required RRSIG records were not present in an answer",
},
{
- 'name': 'dnssec-result-bogus-no-valid-rrsig',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusNoValidRRSIG); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because only invalid RRSIG records were present in an answer',
+ "name": "dnssec-result-bogus-no-valid-rrsig",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusNoValidRRSIG); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because only invalid RRSIG records were present in an answer",
},
{
- 'name': 'dnssec-result-bogus-missing-negative-indication',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusMissingNegativeIndication); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a NODATA or NXDOMAIN answer lacked the required SOA and/or NSEC(3) records',
+ "name": "dnssec-result-bogus-missing-negative-indication",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusMissingNegativeIndication); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a NODATA or NXDOMAIN answer lacked the required SOA and/or NSEC(3) records",
},
{
- 'name': 'dnssec-result-bogus-signature-not-yet-valid',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusSignatureNotYetValid); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because the signature inception time in the RRSIG was not yet valid',
+ "name": "dnssec-result-bogus-signature-not-yet-valid",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusSignatureNotYetValid); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because the signature inception time in the RRSIG was not yet valid",
},
{
- 'name': 'dnssec-result-bogus-signature-expired',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusSignatureExpired); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because the signature expired time in the RRSIG was in the past',
+ "name": "dnssec-result-bogus-signature-expired",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusSignatureExpired); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because the signature expired time in the RRSIG was in the past",
},
{
- 'name': 'dnssec-result-bogus-unsupported-dnskey-algo',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusUnsupportedDNSKEYAlgo); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a DNSKEY RRset contained only unsupported DNSSEC algorithms',
+ "name": "dnssec-result-bogus-unsupported-dnskey-algo",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusUnsupportedDNSKEYAlgo); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a DNSKEY RRset contained only unsupported DNSSEC algorithms",
},
{
- 'name': 'dnssec-result-bogus-unsupported-ds-digest-type',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusUnsupportedDSDigestType); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a DS RRset contained only unsupported digest types',
+ "name": "dnssec-result-bogus-unsupported-ds-digest-type",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusUnsupportedDSDigestType); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because a DS RRset contained only unsupported digest types",
},
{
- 'name': 'dnssec-result-bogus-no-zone-key-bit-set',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusNoZoneKeyBitSet); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because no DNSKEY with the Zone Key bit set was found',
+ "name": "dnssec-result-bogus-no-zone-key-bit-set",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusNoZoneKeyBitSet); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because no DNSKEY with the Zone Key bit set was found",
},
{
- 'name': 'dnssec-result-bogus-revoked-dnskey',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusRevokedDNSKEY); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because all DNSKEYs were revoked',
+ "name": "dnssec-result-bogus-revoked-dnskey",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusRevokedDNSKEY); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because all DNSKEYs were revoked",
},
{
- 'name': 'dnssec-result-bogus-invalid-dnskey-protocol',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusInvalidDNSKEYProtocol); }',
- 'desc': 'Number of responses sent, packet-cache hits excluded, that were in the Bogus state because all DNSKEYs had invalid protocols',
+ "name": "dnssec-result-bogus-invalid-dnskey-protocol",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::dnssec).at(vState::BogusInvalidDNSKEYProtocol); }",
+ "desc": "Number of responses sent, packet-cache hits excluded, that were in the Bogus state because all DNSKEYs had invalid protocols",
},
{
- 'name': 'x-dnssec-result-bogus',
- 'lambda': '''[]() {
+ "name": "x-dnssec-result-bogus",
+ "lambda": """[]() {
std::set<vState> const bogusStates = {vState::BogusNoValidDNSKEY, vState::BogusInvalidDenial, vState::BogusUnableToGetDSs, vState::BogusUnableToGetDNSKEYs, vState::BogusSelfSignedDS, vState::BogusNoRRSIG, vState::BogusNoValidRRSIG, vState::BogusMissingNegativeIndication, vState::BogusSignatureNotYetValid, vState::BogusSignatureExpired, vState::BogusUnsupportedDNSKEYAlgo, vState::BogusUnsupportedDSDigestType, vState::BogusNoZoneKeyBitSet, vState::BogusRevokedDNSKEY, vState::BogusInvalidDNSKEYProtocol};
auto counts = g_Counters.sum(rec::DNSSECHistogram::xdnssec);
uint64_t total = 0;
total += counts.at(state);
}
return total;
- }''',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ }""",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-no-valid-dnskey',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusNoValidDNSKEY); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-no-valid-dnskey",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusNoValidDNSKEY); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-invalid-denial',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusInvalidDenial); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-invalid-denial",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusInvalidDenial); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-unable-to-get-dss',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusUnableToGetDSs); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-unable-to-get-dss",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusUnableToGetDSs); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-unable-to-get-dnskeys',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusUnableToGetDNSKEYs); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-unable-to-get-dnskeys",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusUnableToGetDNSKEYs); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-self-signed-ds',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusSelfSignedDS); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-self-signed-ds",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusSelfSignedDS); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-no-rrsig',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusNoRRSIG); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-no-rrsig",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusNoRRSIG); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-no-valid-rrsig',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusNoValidRRSIG); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-no-valid-rrsig",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusNoValidRRSIG); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-missing-negative-indication',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusMissingNegativeIndication); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-missing-negative-indication",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusMissingNegativeIndication); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-signature-not-yet-valid',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusSignatureNotYetValid); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-signature-not-yet-valid",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusSignatureNotYetValid); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-signature-expired',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusSignatureExpired); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-signature-expired",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusSignatureExpired); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-unsupported-dnskey-algo',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusUnsupportedDNSKEYAlgo); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-unsupported-dnskey-algo",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusUnsupportedDNSKEYAlgo); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-unsupported-ds-digest-type',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusUnsupportedDSDigestType); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-unsupported-ds-digest-type",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusUnsupportedDSDigestType); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-no-zone-key-bit-set',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusNoZoneKeyBitSet); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-no-zone-key-bit-set",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusNoZoneKeyBitSet); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-revoked-dnskey',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusRevokedDNSKEY); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-revoked-dnskey",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusRevokedDNSKEY); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-bogus-invalid-dnskey-protocol',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusInvalidDNSKEYProtocol); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-bogus-invalid-dnskey-protocol",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::BogusInvalidDNSKEYProtocol); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-indeterminate',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::Indeterminate); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-indeterminate",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::Indeterminate); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-nta',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::NTA); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-nta",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::NTA); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-insecure',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::Insecure); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-insecure",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::Insecure); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'x-dnssec-result-secure',
- 'lambda': '[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::Secure); }',
- 'if': '::arg()["x-dnssec-names"].length() > 0',
- 'desc': 'Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.'
+ "name": "x-dnssec-result-secure",
+ "lambda": "[] { return g_Counters.sum(rec::DNSSECHistogram::xdnssec).at(vState::Secure); }",
+ "if": '::arg()["x-dnssec-names"].length() > 0',
+ "desc": "Same as corresponding metric without ``x-`` prefix, for names in :ref:`setting-yaml-dnssec.x_dnssec_names`.",
},
{
- 'name': 'idle-tcpout-connections',
- 'lambda': 'getCurrentIdleTCPConnections',
- 'ptype': 'gauge',
- 'desc': 'Number of connections in the TCP idle outgoing connections pool',
+ "name": "idle-tcpout-connections",
+ "lambda": "getCurrentIdleTCPConnections",
+ "ptype": "gauge",
+ "desc": "Number of connections in the TCP idle outgoing connections pool",
},
{
- 'name': 'source-disallowed-notify',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::sourceDisallowedNotify); }',
- 'desc': 'Number of NOTIFY operations not allowed by allow-notify-from',
- 'snmp': 124,
+ "name": "source-disallowed-notify",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::sourceDisallowedNotify); }",
+ "desc": "Number of NOTIFY operations not allowed by allow-notify-from",
+ "snmp": 124,
},
{
- 'name': 'zone-disallowed-notify',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::zoneDisallowedNotify); }',
- 'desc': 'Number of NOTIFY operations not allowed by allow-notify-for',
- 'snmp': 125,
+ "name": "zone-disallowed-notify",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::zoneDisallowedNotify); }",
+ "desc": "Number of NOTIFY operations not allowed by allow-notify-for",
+ "snmp": 125,
},
{
- 'name': 'non-resolving-nameserver-entries',
- 'lambda': 'SyncRes::getNonResolvingNSSize',
- 'ptype': 'gauge',
- 'snmp': 126,
- 'desc': 'Number of entries in the non-resolving NS name cache',
+ "name": "non-resolving-nameserver-entries",
+ "lambda": "SyncRes::getNonResolvingNSSize",
+ "ptype": "gauge",
+ "snmp": 126,
+ "desc": "Number of entries in the non-resolving NS name cache",
},
{
- 'name': 'maintenance-usec',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::maintenanceUsec); }',
- 'snmp': 127,
- 'snmpname': 'maintenanceUSec',
- 'desc': 'Time spent doing internal maintenance, including Lua maintenance',
+ "name": "maintenance-usec",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::maintenanceUsec); }",
+ "snmp": 127,
+ "snmpname": "maintenanceUSec",
+ "desc": "Time spent doing internal maintenance, including Lua maintenance",
},
{
- 'name': 'maintenance-calls',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::maintenanceCalls); }',
- 'snmp': 128,
- 'snmpname': 'maintenanceCount',
- 'desc': 'Number of times internal maintenance has been called, including Lua maintenance',
+ "name": "maintenance-calls",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::maintenanceCalls); }",
+ "snmp": 128,
+ "snmpname": "maintenanceCount",
+ "desc": "Number of times internal maintenance has been called, including Lua maintenance",
},
-
# Entries for auth-rcode-answers are a bit different than others: separate rec_control and SNMP metrics, but a multicounter entry for Prometheus. The alphabetically first gets a real pname, the others an empty one.
# We only generate the Prometheus comment for the first one
{
- 'name': 'auth-noerror-answers',
- 'ptype': 'multicounter',
- 'snmp': 129,
- 'snmpname': 'authrcode0Count',
- 'desc': 'Number of rcode 0 (noerror) answers received',
- 'pdesc': '',
+ "name": "auth-noerror-answers",
+ "ptype": "multicounter",
+ "snmp": 129,
+ "snmpname": "authrcode0Count",
+ "desc": "Number of rcode 0 (noerror) answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-formerr-answers', # This entry is alphabetically the first, so state the Prometheus HELP text
- 'ptype': 'multicounter',
- 'snmp': 130,
- 'snmpname': 'authrcode1Count',
- 'desc': 'Number of rcode 1 (formerr) answers received',
- 'pdesc': 'Counts the rcodes returned by authoritative servers.'
+ "name": "auth-formerr-answers", # This entry is alphabetically the first, so state the Prometheus HELP text
+ "ptype": "multicounter",
+ "snmp": 130,
+ "snmpname": "authrcode1Count",
+ "desc": "Number of rcode 1 (formerr) answers received",
+ "pdesc": "Counts the rcodes returned by authoritative servers.",
},
{
- 'name': 'auth-servfail-answers',
- 'ptype': 'multicounter',
- 'snmp': 131,
- 'snmpname': 'authrcode2Count',
- 'desc': 'Number of rcode 2 (servfail) answers received',
- 'pdesc': '',
+ "name": "auth-servfail-answers",
+ "ptype": "multicounter",
+ "snmp": 131,
+ "snmpname": "authrcode2Count",
+ "desc": "Number of rcode 2 (servfail) answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-nxdomain-answers',
- 'ptype': 'multicounter',
- 'snmp': 132,
- 'snmpname': 'authrcode3Count',
- 'desc': 'Number of rcode 3 (nxdomain) answers received',
- 'pdesc': '',
+ "name": "auth-nxdomain-answers",
+ "ptype": "multicounter",
+ "snmp": 132,
+ "snmpname": "authrcode3Count",
+ "desc": "Number of rcode 3 (nxdomain) answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-notimp-answers',
- 'ptype': 'multicounter',
- 'snmp': 133,
- 'snmpname': 'authrcode4Count',
- 'desc': 'Number of rcode 4 (notimp) answers received',
- 'pdesc': '',
+ "name": "auth-notimp-answers",
+ "ptype": "multicounter",
+ "snmp": 133,
+ "snmpname": "authrcode4Count",
+ "desc": "Number of rcode 4 (notimp) answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-refused-answers',
- 'ptype': 'multicounter',
- 'snmp': 134,
- 'snmpname': 'authrcode5Count',
- 'desc': 'Number of rcode 5 (refused) answers received',
- 'pdesc': '',
+ "name": "auth-refused-answers",
+ "ptype": "multicounter",
+ "snmp": 134,
+ "snmpname": "authrcode5Count",
+ "desc": "Number of rcode 5 (refused) answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-yxdomain-answers',
- 'ptype': 'multicounter',
- 'snmp': 135,
- 'snmpname': 'authrcode6Count',
- 'desc': 'Number of rcode 6 (yxdomain) answers received',
- 'pdesc': '',
+ "name": "auth-yxdomain-answers",
+ "ptype": "multicounter",
+ "snmp": 135,
+ "snmpname": "authrcode6Count",
+ "desc": "Number of rcode 6 (yxdomain) answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-yxrrset-answers',
- 'ptype': 'multicounter',
- 'snmp': 136,
- 'snmpname': 'authrcode7Count',
- 'desc': 'Number of rcode 7 (yxrrset) answers received',
- 'pdesc': '',
+ "name": "auth-yxrrset-answers",
+ "ptype": "multicounter",
+ "snmp": 136,
+ "snmpname": "authrcode7Count",
+ "desc": "Number of rcode 7 (yxrrset) answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-nxrrset-answers',
- 'ptype': 'multicounter',
- 'snmp': 137,
- 'snmpname': 'authrcode8Count',
- 'desc': 'Number of rcode 8 (nxrrset) answers received',
- 'pdesc': '',
+ "name": "auth-nxrrset-answers",
+ "ptype": "multicounter",
+ "snmp": 137,
+ "snmpname": "authrcode8Count",
+ "desc": "Number of rcode 8 (nxrrset) answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-notauth-answers',
- 'ptype': 'multicounter',
- 'snmp': 138,
- 'snmpname': 'authrcode9Count',
- 'desc': 'Number of rcode 9 (notauth) answers received',
- 'pdesc': '',
+ "name": "auth-notauth-answers",
+ "ptype": "multicounter",
+ "snmp": 138,
+ "snmpname": "authrcode9Count",
+ "desc": "Number of rcode 9 (notauth) answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-rcode10-answers',
- 'ptype': 'multicounter',
- 'snmp': 139,
- 'snmpname': 'authrcode10Count',
- 'desc': 'Number of rcode 10 answers received',
- 'pdesc': '',
+ "name": "auth-rcode10-answers",
+ "ptype": "multicounter",
+ "snmp": 139,
+ "snmpname": "authrcode10Count",
+ "desc": "Number of rcode 10 answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-rcode11-answers',
- 'ptype': 'multicounter',
- 'snmp': 140,
- 'snmpname': 'authrcode11Count',
- 'desc': 'Number of rcode 11 answers received',
- 'pdesc': '',
+ "name": "auth-rcode11-answers",
+ "ptype": "multicounter",
+ "snmp": 140,
+ "snmpname": "authrcode11Count",
+ "desc": "Number of rcode 11 answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-rcode12-answers',
- 'ptype': 'multicounter',
- 'snmp': 141,
- 'snmpname': 'authrcode12Count',
- 'desc': 'Number of rcode 12 answers received',
- 'pdesc': '',
+ "name": "auth-rcode12-answers",
+ "ptype": "multicounter",
+ "snmp": 141,
+ "snmpname": "authrcode12Count",
+ "desc": "Number of rcode 12 answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-rcode13-answers',
- 'ptype': 'multicounter',
- 'snmp': 142,
- 'snmpname': 'authrcode13Count',
- 'desc': 'Number of rcode 13 answers received',
- 'pdesc': '',
+ "name": "auth-rcode13-answers",
+ "ptype": "multicounter",
+ "snmp": 142,
+ "snmpname": "authrcode13Count",
+ "desc": "Number of rcode 13 answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-rcode14-answers',
- 'ptype': 'multicounter',
- 'snmp': 143,
- 'snmpname': 'authrcode14Count',
- 'desc': 'Number of rcode 14 answers received',
- 'pdesc': '',
+ "name": "auth-rcode14-answers",
+ "ptype": "multicounter",
+ "snmp": 143,
+ "snmpname": "authrcode14Count",
+ "desc": "Number of rcode 14 answers received",
+ "pdesc": "",
},
{
- 'name': 'auth-rcode15-answers',
- 'ptype': 'multicounter',
- 'snmp': 144,
- 'snmpname': 'authrcode15Count',
- 'desc': 'Number of rcode 15 answers received',
- 'pdesc': '',
+ "name": "auth-rcode15-answers",
+ "ptype": "multicounter",
+ "snmp": 144,
+ "snmpname": "authrcode15Count",
+ "desc": "Number of rcode 15 answers received",
+ "pdesc": "",
},
{
- 'name': 'packetcache-contended',
- 'lambda': '[]() { return g_packetCache ? g_packetCache->stats().first : 0; }',
- 'desc': 'Number of contended packet cache lock acquisitions',
- 'snmp': 145,
- 'snmpname': 'packetCacheContended',
+ "name": "packetcache-contended",
+ "lambda": "[]() { return g_packetCache ? g_packetCache->stats().first : 0; }",
+ "desc": "Number of contended packet cache lock acquisitions",
+ "snmp": 145,
+ "snmpname": "packetCacheContended",
},
{
- 'name': 'packetcache-acquired',
- 'lambda': '[]() { return g_packetCache ? g_packetCache->stats().second : 0; }',
- 'desc': 'Number of packet cache lock acquisitions',
- 'snmp': 146,
- 'snmpname': 'packetCacheAcquired',
+ "name": "packetcache-acquired",
+ "lambda": "[]() { return g_packetCache ? g_packetCache->stats().second : 0; }",
+ "desc": "Number of packet cache lock acquisitions",
+ "snmp": 146,
+ "snmpname": "packetCacheAcquired",
},
{
- 'name': 'nod-events',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::nodCount); }',
- 'snmp': 147,
- 'desc': 'Count of NOD events',
+ "name": "nod-events",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::nodCount); }",
+ "snmp": 147,
+ "desc": "Count of NOD events",
},
{
- 'name': 'udr-events',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::udrCount); }',
- 'snmp': 148,
- 'desc': 'Count of UDR events',
+ "name": "udr-events",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::udrCount); }",
+ "snmp": 148,
+ "desc": "Count of UDR events",
},
{
- 'name': 'max-chain-length',
- 'lambda': '[] { return g_Counters.max(rec::Counter::maxChainLength); }',
- 'snmp': 149,
- 'desc': 'Maximum chain length',
+ "name": "max-chain-length",
+ "lambda": "[] { return g_Counters.max(rec::Counter::maxChainLength); }",
+ "snmp": 149,
+ "desc": "Maximum chain length",
},
{
- 'name': 'max-chain-weight',
- 'lambda': '[] { return g_Counters.max(rec::Counter::maxChainWeight); }',
- 'snmp': 150,
- 'desc': 'Maximum chain weight',
+ "name": "max-chain-weight",
+ "lambda": "[] { return g_Counters.max(rec::Counter::maxChainWeight); }",
+ "snmp": 150,
+ "desc": "Maximum chain weight",
},
{
- 'name': 'chain-limits',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::chainLimits); }',
- 'snmp': 151,
- 'desc': 'Chain limits reached',
+ "name": "chain-limits",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::chainLimits); }",
+ "snmp": 151,
+ "desc": "Chain limits reached",
},
{
- 'name': 'tcp-overflow',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::tcpOverflow); }',
- 'desc': 'Incoming TCP limits reached',
- 'snmp': 152,
+ "name": "tcp-overflow",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::tcpOverflow); }",
+ "desc": "Incoming TCP limits reached",
+ "snmp": 152,
},
{
- 'name': 'ecs-missing',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::ecsMissingCount); }',
- 'desc': 'Number of answers where ECS info was missing',
- 'snmp': 153,
+ "name": "ecs-missing",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::ecsMissingCount); }",
+ "desc": "Number of answers where ECS info was missing",
+ "snmp": 153,
},
{
- 'name': 'cookie-malformed',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::cookieMalformed); }',
- 'desc': 'Number of malformed cookies received',
- 'snmp': 154,
+ "name": "cookie-malformed",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::cookieMalformed); }",
+ "desc": "Number of malformed cookies received",
+ "snmp": 154,
},
{
- 'name': 'cookie-matched',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::cookieMatched); }',
- 'desc': 'Number of matching cookies received',
- 'snmp': 155,
+ "name": "cookie-matched",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::cookieMatched); }",
+ "desc": "Number of matching cookies received",
+ "snmp": 155,
},
{
- 'name': 'cookie-mismatch-tcp',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::cookieMismatchedOverTCP); }',
- 'desc': 'Number of mismatched cookies received over TCP',
- 'snmp': 156,
+ "name": "cookie-mismatch-tcp",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::cookieMismatchedOverTCP); }",
+ "desc": "Number of mismatched cookies received over TCP",
+ "snmp": 156,
},
{
- 'name': 'cookie-mismatch-udp',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::cookieMismatchedOverUDP); }',
- 'desc': 'Number of mismatched cookies received over UDP',
- 'snmp': 157,
+ "name": "cookie-mismatch-udp",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::cookieMismatchedOverUDP); }",
+ "desc": "Number of mismatched cookies received over UDP",
+ "snmp": 157,
},
{
- 'name': 'cookie-not-in-reply',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::cookieNotInReply); }',
- 'desc': 'Number of times an authoritative server sent a reply without a cookie',
- 'snmp': 158,
+ "name": "cookie-not-in-reply",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::cookieNotInReply); }",
+ "desc": "Number of times an authoritative server sent a reply without a cookie",
+ "snmp": 158,
},
{
- 'name': 'cookie-retry',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::cookieRetry); }',
- 'desc': 'Number of retries because authoritative server sent a BADCOOKIE reply',
- 'snmp': 159,
+ "name": "cookie-retry",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::cookieRetry); }",
+ "desc": "Number of retries because authoritative server sent a BADCOOKIE reply",
+ "snmp": 159,
},
{
- 'name': 'cookies-supported',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::cookieProbeSupported); }',
- 'desc': 'Number of authoritative server cookie probes resulting in success',
- 'snmp': 160,
+ "name": "cookies-supported",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::cookieProbeSupported); }",
+ "desc": "Number of authoritative server cookie probes resulting in success",
+ "snmp": 160,
},
{
- 'name': 'cookies-unsupported',
- 'lambda': '[] { return g_Counters.sum(rec::Counter::cookieProbeUnsupported); }',
- 'desc': 'Number of authoritative server cookie probes not resulting in success',
- 'snmp': 161,
+ "name": "cookies-unsupported",
+ "lambda": "[] { return g_Counters.sum(rec::Counter::cookieProbeUnsupported); }",
+ "desc": "Number of authoritative server cookie probes not resulting in success",
+ "snmp": 161,
},
{
- 'name': 'remote-logger-count',
- 'lambda': '''[]() {
+ "name": "remote-logger-count",
+ "lambda": """[]() {
return toRemoteLoggerStatsMap("remote-logger-count");
- }''',
- 'desc': 'Number of remote logging events',
- 'ptype': 'multicounter',
- 'pname': 'remote-logger-count-o-0', # For multicounters, state the first
+ }""",
+ "desc": "Number of remote logging events",
+ "ptype": "multicounter",
+ "pname": "remote-logger-count-o-0", # For multicounters, state the first
# No SNMP
},
{
- 'name': 'cumul-clientanswers-x',
+ "name": "cumul-clientanswers-x",
# No lambda
- 'desc': 'Cumulative counts of answer times of authoritative servers in buckets less than x microseconds.',
- 'longdesc': 'Disabled by default, see :ref:`setting-yaml-recursor.stats_rec_control_disabled_list`. These metrics are useful for Prometheus and not listed in other outputs by default.',
- 'ptype': 'histogram',
- 'pname': 'cumul-clientanswers-count', # For cumulative histogram, state the xxx_count name where xxx matches the name in rec_channel_rec
+ "desc": "Cumulative counts of answer times of authoritative servers in buckets less than x microseconds.",
+ "longdesc": "Disabled by default, see :ref:`setting-yaml-recursor.stats_rec_control_disabled_list`. These metrics are useful for Prometheus and not listed in other outputs by default.",
+ "ptype": "histogram",
+ "pname": "cumul-clientanswers-count", # For cumulative histogram, state the xxx_count name where xxx matches the name in rec_channel_rec
# No SNMP
},
{
- 'name': 'cumul-authanswers-x',
+ "name": "cumul-authanswers-x",
# No lambda
- 'desc': 'Cumulative counts of answer times to clients in buckets less than x microseconds.',
- 'longdesc': 'Disabled by default, see :ref:`setting-yaml-recursor.stats_rec_control_disabled_list`. These metrics are useful for Prometheus and not listed in other outputs by default.',
- 'ptype': 'histogram',
- 'pname': 'cumul-authanswers-count4', # For cumulative histogram, state the xxx_count name where xxx matches the name in rec_channel_rec
+ "desc": "Cumulative counts of answer times to clients in buckets less than x microseconds.",
+ "longdesc": "Disabled by default, see :ref:`setting-yaml-recursor.stats_rec_control_disabled_list`. These metrics are useful for Prometheus and not listed in other outputs by default.",
+ "ptype": "histogram",
+ "pname": "cumul-authanswers-count4", # For cumulative histogram, state the xxx_count name where xxx matches the name in rec_channel_rec
# No SNMP
},
{
- 'name': 'policy-hits',
+ "name": "policy-hits",
# No lambda
- 'desc': 'Number of policy decisions based on Lua',
- 'longdesc': '(``type = ""filter""``), or RPZ (``type = ""rpz""``). RPZ hits include the :ref:`rpz-policyName`. These metrics are useful for Prometheus and not listed in other outputs by default.',
- 'ptype': 'multicounter',
- 'pname': 'policy-hits', # For multicounters, state the first
+ "desc": "Number of policy decisions based on Lua",
+ "longdesc": '(``type = ""filter""``), or RPZ (``type = ""rpz""``). RPZ hits include the :ref:`rpz-policyName`. These metrics are useful for Prometheus and not listed in other outputs by default.',
+ "ptype": "multicounter",
+ "pname": "policy-hits", # For multicounters, state the first
# No SNMP
},
{
- 'name': 'proxy-mapping',
+ "name": "proxy-mapping",
# No lambda
- 'desc': 'Proxy mappings done',
- 'ptype': 'multicounter',
- 'pname': 'proxy-mapping-total-n-0', # For multicounters, state the first
+ "desc": "Proxy mappings done",
+ "ptype": "multicounter",
+ "pname": "proxy-mapping-total-n-0", # For multicounters, state the first
# No SNMP
},
]
"""The Python script that takes table.py and generates C++, Rust ahd .rst code."""
+
#
# For C++ it generates cxxsettings-generated.cc containing support for old style
# settings plus conversion from old style to new style.
import sys
from pathlib import Path
+
class LType(Enum):
"""The type we handle in table.py"""
+
Bool = auto()
Command = auto()
Double = auto()
ListNegativeTrustAnchors = auto()
ListProtobufServers = auto()
ListProxyMappings = auto()
- ListRPZs = auto();
+ ListRPZs = auto()
ListSocketAddresses = auto()
ListSortLists = auto()
ListStrings = auto()
String = auto()
Uint64 = auto()
-listOfStringTypes = (LType.ListSocketAddresses, LType.ListStrings, LType.ListSubnets)
-listOfStructuredTypes = (LType.ListAuthZones, LType.ListForwardZones, LType.ListTrustAnchors, LType.ListNegativeTrustAnchors,
- LType.ListProtobufServers, LType.ListDNSTapFrameStreamServers, LType.ListDNSTapNODFrameStreamServers,
- LType.ListSortLists, LType.ListRPZs, LType.ListZoneToCaches, LType.ListAllowedAdditionalQTypes,
- LType.ListProxyMappings, LType.ListForwardingCatalogZones, LType.ListIncomingWSConfigs,
- LType.ListOutgoingTLSConfigurations, LType.ListOpenTelemetryTraceConditions)
+
+listOfStringTypes = (LType.ListSocketAddresses, LType.ListStrings, LType.ListSubnets)
+listOfStructuredTypes = (
+ LType.ListAuthZones,
+ LType.ListForwardZones,
+ LType.ListTrustAnchors,
+ LType.ListNegativeTrustAnchors,
+ LType.ListProtobufServers,
+ LType.ListDNSTapFrameStreamServers,
+ LType.ListDNSTapNODFrameStreamServers,
+ LType.ListSortLists,
+ LType.ListRPZs,
+ LType.ListZoneToCaches,
+ LType.ListAllowedAdditionalQTypes,
+ LType.ListProxyMappings,
+ LType.ListForwardingCatalogZones,
+ LType.ListIncomingWSConfigs,
+ LType.ListOutgoingTLSConfigurations,
+ LType.ListOpenTelemetryTraceConditions,
+)
+
def get_olddoc_typename(typ):
"""Given a type from table.py, return the old-style type name"""
if typ == LType.Bool:
- return 'Boolean'
+ return "Boolean"
if typ == LType.Uint64:
- return 'Integer'
+ return "Integer"
if typ == LType.Double:
- return 'Double'
+ return "Double"
if typ == LType.String:
- return 'String'
+ return "String"
if typ == LType.ListSocketAddresses:
- return 'Comma separated list or IPs of IP:port combinations'
+ return "Comma separated list or IPs of IP:port combinations"
if typ == LType.ListSubnets:
- return 'Comma separated list of IP addresses or subnets, negation supported'
+ return "Comma separated list of IP addresses or subnets, negation supported"
if typ == LType.ListStrings:
- return 'Comma separated list of strings'
+ return "Comma separated list of strings"
if typ == LType.ListForwardZones:
- return 'Comma separated list of \'zonename=IP\' pairs'
+ return "Comma separated list of 'zonename=IP' pairs"
if typ == LType.ListAuthZones:
- return 'Comma separated list of \'zonename=filename\' pairs'
- return 'Unknown1' + str(typ)
+ return "Comma separated list of 'zonename=filename' pairs"
+ return "Unknown1" + str(typ)
+
def get_newdoc_typename(typ):
"""Given a type from table.py, return the new-style type name"""
if typ == LType.Bool:
- return 'Boolean'
+ return "Boolean"
if typ == LType.Uint64:
- return 'Integer'
+ return "Integer"
if typ == LType.Double:
- return 'Double'
+ return "Double"
if typ == LType.String:
- return 'String'
+ return "String"
if typ == LType.ListSocketAddresses:
- return 'Sequence of `Socket Address`_ (IP or IP:port combinations)'
+ return "Sequence of `Socket Address`_ (IP or IP:port combinations)"
if typ == LType.ListSubnets:
- return 'Sequence of `Subnet`_ (IP addresses or subnets, negation supported)'
+ return "Sequence of `Subnet`_ (IP addresses or subnets, negation supported)"
if typ == LType.ListStrings:
- return 'Sequence of strings'
+ return "Sequence of strings"
if typ == LType.ListForwardZones:
- return 'Sequence of `Forward Zone`_'
+ return "Sequence of `Forward Zone`_"
if typ == LType.ListAuthZones:
- return 'Sequence of `Auth Zone`_'
+ return "Sequence of `Auth Zone`_"
if typ == LType.ListTrustAnchors:
- return 'Sequence of `TrustAnchor`_'
+ return "Sequence of `TrustAnchor`_"
if typ == LType.ListNegativeTrustAnchors:
- return 'Sequence of `NegativeTrustAnchor`_'
+ return "Sequence of `NegativeTrustAnchor`_"
if typ == LType.ListProtobufServers:
- return 'Sequence of `ProtobufServer`_'
+ return "Sequence of `ProtobufServer`_"
if typ == LType.ListDNSTapFrameStreamServers:
- return 'Sequence of `DNSTapFrameStreamServers`_'
+ return "Sequence of `DNSTapFrameStreamServers`_"
if typ == LType.ListDNSTapNODFrameStreamServers:
- return 'Sequence of `DNSTapNODFrameStreamServers`_'
+ return "Sequence of `DNSTapNODFrameStreamServers`_"
if typ == LType.ListSortLists:
- return 'Sequence of `Sortlist`_'
+ return "Sequence of `Sortlist`_"
if typ == LType.ListRPZs:
- return 'Sequence of `RPZ`_'
+ return "Sequence of `RPZ`_"
if typ == LType.ListZoneToCaches:
- return 'Sequence of `ZoneToCache`_'
+ return "Sequence of `ZoneToCache`_"
if typ == LType.ListAllowedAdditionalQTypes:
- return 'Sequence of `AllowedAdditionalQType`_'
+ return "Sequence of `AllowedAdditionalQType`_"
if typ == LType.ListProxyMappings:
- return 'Sequence of `ProxyMapping`_'
+ return "Sequence of `ProxyMapping`_"
if typ == LType.ListForwardingCatalogZones:
- return 'Sequence of `ForwardingCatalogZone`_'
+ return "Sequence of `ForwardingCatalogZone`_"
if typ == LType.ListIncomingWSConfigs:
- return 'Sequence of `IncomingWSConfig`_'
+ return "Sequence of `IncomingWSConfig`_"
if typ == LType.ListOutgoingTLSConfigurations:
- return 'Sequence of `OutgoingTLSConfiguration`_'
+ return "Sequence of `OutgoingTLSConfiguration`_"
if typ == LType.ListOpenTelemetryTraceConditions:
- return 'Sequence of `OpenTelemetryTraceCondition`_'
- return 'Unknown2' + str(typ)
+ return "Sequence of `OpenTelemetryTraceCondition`_"
+ return "Unknown2" + str(typ)
+
def get_default_olddoc_value(typ, val):
"""Given a type and a value from table.py return the old doc representation of the value"""
if typ == LType.Bool:
- if val == 'false':
- return 'no'
- return 'yes'
- if val == '':
- return '(empty)'
+ if val == "false":
+ return "no"
+ return "yes"
+ if val == "":
+ return "(empty)"
return val
+
def get_default_newdoc_value(typ, val):
"""Given a type and a value from table.py return the new doc representation of the value"""
if typ in (LType.Bool, LType.Uint64, LType.Double):
- return '``' + val + '``'
- if typ == LType.String and val == '':
- return '(empty)'
+ return "``" + val + "``"
+ if typ == LType.String and val == "":
+ return "(empty)"
if typ == LType.String:
- return '``' + val + '``'
- parts = re.split('[ \t,]+', val)
+ return "``" + val + "``"
+ parts = re.split("[ \t,]+", val)
if len(parts) > 0:
- ret = ''
+ ret = ""
for part in parts:
- if part == '':
+ if part == "":
continue
- if ret != '':
- ret += ', '
- if ':' in part or '!' in part:
+ if ret != "":
+ ret += ", "
+ if ":" in part or "!" in part:
ret += "'" + part + "'"
else:
ret += part
else:
- ret = ''
- return '``[' + ret + ']``'
+ ret = ""
+ return "``[" + ret + "]``"
+
def list_to_base_type(typ):
typeName = typ.name
- if typeName.startswith('List') and typeName.endswith('s'):
- baseName = typeName[4:len(typeName) - 1]
+ if typeName.startswith("List") and typeName.endswith("s"):
+ baseName = typeName[4 : len(typeName) - 1]
return baseName
- return 'Unknown3: ' + typeName
+ return "Unknown3: " + typeName
+
def get_rust_type(typ):
"""Determine which Rust type is used for a logical type"""
if typ == LType.Bool:
- return 'bool'
+ return "bool"
if typ == LType.Uint64:
- return 'u64'
+ return "u64"
if typ == LType.Double:
- return 'f64'
+ return "f64"
if typ == LType.String:
- return 'String'
+ return "String"
# These vectors map to Vec<String>
if typ == LType.ListSocketAddresses:
- return 'Vec<String>'
+ return "Vec<String>"
if typ == LType.ListSubnets:
- return 'Vec<String>'
+ return "Vec<String>"
if typ == LType.ListStrings:
- return 'Vec<String>'
+ return "Vec<String>"
# These vectors map to Vec<Type>
- return 'Vec<' + list_to_base_type(typ) + '>'
+ return "Vec<" + list_to_base_type(typ) + ">"
+
def quote(arg):
"""Return a quoted string"""
return '"' + arg + '"'
+
def isEnvVar(name):
- return name in ('SYSCONFDIR', 'NODCACHEDIRNOD', 'NODCACHEDIRUDR')
+ return name in ("SYSCONFDIR", "NODCACHEDIRNOD", "NODCACHEDIRUDR")
+
def gen_cxx_defineoldsettings(file, entries):
"""Generate C++ code to declare old-style settings"""
- file.write('void pdns::settings::rec::defineOldStyleSettings()\n{\n')
+ file.write("void pdns::settings::rec::defineOldStyleSettings()\n{\n")
for entry in entries:
- helptxt = quote(entry['help'])
- oldname = quote(entry['oldname'])
- if entry['type'] == LType.Bool:
- if entry['default'] == "true":
+ helptxt = quote(entry["help"])
+ oldname = quote(entry["oldname"])
+ if entry["type"] == LType.Bool:
+ if entry["default"] == "true":
cxxdef = "yes"
else:
cxxdef = "no"
cxxdef = quote(cxxdef)
file.write(f" ::arg().setSwitch({oldname}, {helptxt}) = {cxxdef};\n")
- elif entry['type'] == LType.Command:
+ elif entry["type"] == LType.Command:
file.write(f" ::arg().setCmd({oldname}, {helptxt});\n")
else:
- cxxdef = entry['default'] if isEnvVar(entry['default']) else quote(entry['default'])
+ cxxdef = entry["default"] if isEnvVar(entry["default"]) else quote(entry["default"])
file.write(f" ::arg().set({oldname}, {helptxt}) = {cxxdef};\n")
- file.write('}\n\n')
+ file.write("}\n\n")
+
def gen_cxx_oldstylesettingstobridgestruct(file, entries):
"""Generate C++ code the convert old-style settings to new-style struct"""
- file.write('void pdns::settings::rec::oldStyleSettingsToBridgeStruct')
- file.write('(Recursorsettings& settings)\n{\n')
+ file.write("void pdns::settings::rec::oldStyleSettingsToBridgeStruct")
+ file.write("(Recursorsettings& settings)\n{\n")
for entry in entries:
- if entry['type'] == LType.Command:
+ if entry["type"] == LType.Command:
continue
- if 'skip-yaml' in entry:
+ if "skip-yaml" in entry:
continue
- if 'skip-old' in entry:
+ if "skip-old" in entry:
continue
- rust_type = get_rust_type(entry['type'])
- name = entry['name']
- oldname = entry['oldname']
- section = entry['section']
- file.write(f' settings.{section}.{name} = ')
- if rust_type == 'bool':
+ rust_type = get_rust_type(entry["type"])
+ name = entry["name"]
+ oldname = entry["oldname"]
+ section = entry["section"]
+ file.write(f" settings.{section}.{name} = ")
+ if rust_type == "bool":
file.write(f'arg().mustDo("{oldname}")')
- elif rust_type == 'u64':
+ elif rust_type == "u64":
file.write(f'static_cast<uint64_t>(arg().asNum("{oldname}"))')
- elif rust_type == 'f64':
+ elif rust_type == "f64":
file.write(f'arg().asDouble("{oldname}")')
- elif rust_type == 'String':
+ elif rust_type == "String":
file.write(f'arg()["{oldname}"]')
- elif rust_type == 'Vec<String>':
+ elif rust_type == "Vec<String>":
file.write(f'getStrings("{oldname}")')
- elif rust_type == 'Vec<ForwardZone>':
+ elif rust_type == "Vec<ForwardZone>":
file.write(f'getForwardZones("{oldname}")')
- elif rust_type == 'Vec<AuthZone>':
+ elif rust_type == "Vec<AuthZone>":
file.write(f'getAuthZones("{oldname}")')
else:
- file.write(f'Unknown3 type {rust_type}\n')
- file.write(';\n')
- file.write('}\n\n')
+ file.write(f"Unknown3 type {rust_type}\n")
+ file.write(";\n")
+ file.write("}\n\n")
+
def gen_cxx_oldkvtobridgestruct(file, entries):
"""Generate C++ code for oldKVToBridgeStruct"""
- file.write('// Inefficient, but only meant to be used for one-time conversion purposes\n')
- file.write('bool pdns::settings::rec::oldKVToBridgeStruct(std::string& key, ')
- file.write('const std::string& value, ::rust::String& section, ::rust::String& fieldname, ')
- file.write('::rust::String& type_name, pdns::rust::settings::rec::Value& rustvalue)')
- file.write('{ // NOLINT(readability-function-cognitive-complexity)\n')
- file.write(' if (const auto& newname = arg().isDeprecated(key); !newname.empty()) {\n')
- file.write(' key = newname;\n')
- file.write(' }\n')
+ file.write("// Inefficient, but only meant to be used for one-time conversion purposes\n")
+ file.write("bool pdns::settings::rec::oldKVToBridgeStruct(std::string& key, ")
+ file.write("const std::string& value, ::rust::String& section, ::rust::String& fieldname, ")
+ file.write("::rust::String& type_name, pdns::rust::settings::rec::Value& rustvalue)")
+ file.write("{ // NOLINT(readability-function-cognitive-complexity)\n")
+ file.write(" if (const auto& newname = arg().isDeprecated(key); !newname.empty()) {\n")
+ file.write(" key = newname;\n")
+ file.write(" }\n")
for entry in entries:
- if entry['type'] == LType.Command:
+ if entry["type"] == LType.Command:
continue
- if 'skip-yaml' in entry:
+ if "skip-yaml" in entry:
continue
- if 'skip-old' in entry:
+ if "skip-old" in entry:
continue
- rust_type = get_rust_type(entry['type'])
- extra = ''
- if entry['oldname'] == 'forward-zones-recurse':
- extra = ', true'
- name = entry['name']
- section = entry['section']
- oldname = entry['oldname']
+ rust_type = get_rust_type(entry["type"])
+ extra = ""
+ if entry["oldname"] == "forward-zones-recurse":
+ extra = ", true"
+ name = entry["name"]
+ section = entry["section"]
+ oldname = entry["oldname"]
file.write(f' if (key == "{oldname}") {{\n')
file.write(f' section = "{section}";\n')
file.write(f' fieldname = "{name}";\n')
file.write(f' type_name = "{rust_type}";\n')
- if rust_type == 'bool':
- file.write(f' to_yaml(rustvalue.bool_val, value{extra});\n')
- file.write(' return true;\n }\n')
- elif rust_type == 'u64':
- file.write(f' to_yaml(rustvalue.u64_val, value{extra});\n')
- file.write(' return true;\n }\n')
- elif rust_type == 'f64':
- file.write(f' to_yaml(rustvalue.f64_val, value{extra});\n')
- file.write(' return true;\n }\n')
- elif rust_type == 'String':
- file.write(f' to_yaml(rustvalue.string_val, value{extra});\n')
- file.write(' return true;\n }\n')
- elif rust_type == 'Vec<String>':
- file.write(f' to_yaml(rustvalue.vec_string_val, value{extra});\n')
- file.write(' return true;\n }\n')
- elif rust_type == 'Vec<ForwardZone>':
- file.write(f' to_yaml(rustvalue.vec_forwardzone_val, value{extra});\n')
- file.write(' return true;\n }\n')
- elif rust_type == 'Vec<AuthZone>':
- file.write(f' to_yaml(rustvalue.vec_authzone_val, value{extra});\n')
- file.write(' return true;\n }\n')
+ if rust_type == "bool":
+ file.write(f" to_yaml(rustvalue.bool_val, value{extra});\n")
+ file.write(" return true;\n }\n")
+ elif rust_type == "u64":
+ file.write(f" to_yaml(rustvalue.u64_val, value{extra});\n")
+ file.write(" return true;\n }\n")
+ elif rust_type == "f64":
+ file.write(f" to_yaml(rustvalue.f64_val, value{extra});\n")
+ file.write(" return true;\n }\n")
+ elif rust_type == "String":
+ file.write(f" to_yaml(rustvalue.string_val, value{extra});\n")
+ file.write(" return true;\n }\n")
+ elif rust_type == "Vec<String>":
+ file.write(f" to_yaml(rustvalue.vec_string_val, value{extra});\n")
+ file.write(" return true;\n }\n")
+ elif rust_type == "Vec<ForwardZone>":
+ file.write(f" to_yaml(rustvalue.vec_forwardzone_val, value{extra});\n")
+ file.write(" return true;\n }\n")
+ elif rust_type == "Vec<AuthZone>":
+ file.write(f" to_yaml(rustvalue.vec_authzone_val, value{extra});\n")
+ file.write(" return true;\n }\n")
else:
- file.write(f'Unknown4 type {rust_type}\n')
- file.write(' return false;\n')
- file.write('}\n\n')
+ file.write(f"Unknown4 type {rust_type}\n")
+ file.write(" return false;\n")
+ file.write("}\n\n")
+
def gen_cxx_brigestructtoldstylesettings(file, entries):
"""Generate C++ Code for bridgeStructToOldStyleSettings"""
- file.write('void pdns::settings::rec::bridgeStructToOldStyleSettings')
- file.write('(const Recursorsettings& settings)\n{\n')
+ file.write("void pdns::settings::rec::bridgeStructToOldStyleSettings")
+ file.write("(const Recursorsettings& settings)\n{\n")
for entry in entries:
- if entry['type'] == LType.Command:
+ if entry["type"] == LType.Command:
continue
- if 'skip-yaml' in entry:
+ if "skip-yaml" in entry:
continue
- if 'skip-old' in entry:
+ if "skip-old" in entry:
continue
- section = entry['section'].lower()
- name = entry['name']
- oldname = entry['oldname']
+ section = entry["section"].lower()
+ name = entry["name"]
+ oldname = entry["oldname"]
file.write(f' ::arg().set("{oldname}") = ')
- file.write(f'to_arg(settings.{section}.{name});\n')
- file.write('}\n')
+ file.write(f"to_arg(settings.{section}.{name});\n")
+ file.write("}\n")
+
def gen_cxx(gendir, entries):
"""Generate the C++ code from the defs in table.py"""
- with open(gendir + '/cxxsettings-generated.cc', mode='w', encoding="UTF-8") as file:
- file.write('// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE: see settings dir\n\n')
+ with open(gendir + "/cxxsettings-generated.cc", mode="w", encoding="UTF-8") as file:
+ file.write("// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE: see settings dir\n\n")
file.write('#include "arguments.hh"\n')
file.write('#include "cxxsettings.hh"\n')
file.write('#include "cxxsettings-private.hh"\n\n')
gen_cxx_oldkvtobridgestruct(file, entries)
gen_cxx_brigestructtoldstylesettings(file, entries)
+
def is_value_rust_default(typ, value):
"""Is a value (represented as string) the same as its corresponding Rust default?"""
- if typ == 'bool':
- return value == 'false'
- if typ == 'u64':
- return value in ('0', '')
- if typ == 'f64':
- return value == '0.0'
- if typ == 'String':
- return value == ''
+ if typ == "bool":
+ return value == "false"
+ if typ == "u64":
+ return value in ("0", "")
+ if typ == "f64":
+ return value == "0.0"
+ if typ == "String":
+ return value == ""
return False
+
def gen_rust_vec_default_functions(name, typeName, defvalue):
"""Generate Rust code for the default handling of a vector for typeName"""
- ret = f'// DEFAULT HANDLING for {name}\n'
- ret += f'fn default_value_{name}() -> Vec<recsettings::{typeName}> {{\n'
+ ret = f"// DEFAULT HANDLING for {name}\n"
+ ret += f"fn default_value_{name}() -> Vec<recsettings::{typeName}> {{\n"
ret += f' let msg = "default value defined for `{name}\' should be valid YAML";'
- ret += f' let deserialized: Vec<recsettings::{typeName}> = serde_yaml::from_str({quote(defvalue)}).expect(msg);\n'
- ret += f' deserialized\n'
- ret += '}\n'
- ret += f'fn default_value_equal_{name}(value: &Vec<recsettings::{typeName}>)'
- ret += '-> bool {\n'
- ret += f' let def = default_value_{name}();\n'
- ret += ' &def == value\n'
- ret += '}\n\n'
+ ret += (
+ f" let deserialized: Vec<recsettings::{typeName}> = serde_yaml::from_str({quote(defvalue)}).expect(msg);\n"
+ )
+ ret += f" deserialized\n"
+ ret += "}\n"
+ ret += f"fn default_value_equal_{name}(value: &Vec<recsettings::{typeName}>)"
+ ret += "-> bool {\n"
+ ret += f" let def = default_value_{name}();\n"
+ ret += " &def == value\n"
+ ret += "}\n\n"
return ret
+
# Example snippet generated
# fn default_value_general_query_local_address() -> Vec<String> {
# vec![String::from("0.0.0.0"), ]
-#}
-#fn default_value_equal_general_query_local_address(value: &Vec<String>) -> bool {
+# }
+# fn default_value_equal_general_query_local_address(value: &Vec<String>) -> bool {
# let def = default_value_general_query_local_address();
# &def == value
-#}
+# }
def gen_rust_stringvec_default_functions(entry, name):
"""Generate Rust code for the default handling of a vector for Strings"""
- ret = f'// DEFAULT HANDLING for {name}\n'
- ret += f'fn default_value_{name}() -> Vec<String> {{\n'
- parts = re.split('[ \t,]+', entry['default'])
+ ret = f"// DEFAULT HANDLING for {name}\n"
+ ret += f"fn default_value_{name}() -> Vec<String> {{\n"
+ parts = re.split("[ \t,]+", entry["default"])
if len(parts) > 0:
- ret += ' vec![\n'
+ ret += " vec![\n"
for part in parts:
- if part == '':
+ if part == "":
continue
- ret += f' String::from({quote(part)}),\n'
- ret += ' ]\n'
+ ret += f" String::from({quote(part)}),\n"
+ ret += " ]\n"
else:
- ret += ' vec![]\n'
- ret += '}\n'
- ret += f'fn default_value_equal_{name}(value: &Vec<String>) -> bool {{\n'
- ret += f' let def = default_value_{name}();\n'
- ret += ' &def == value\n'
- ret += '}\n\n'
+ ret += " vec![]\n"
+ ret += "}\n"
+ ret += f"fn default_value_equal_{name}(value: &Vec<String>) -> bool {{\n"
+ ret += f" let def = default_value_{name}();\n"
+ ret += " &def == value\n"
+ ret += "}\n\n"
return ret
+
def gen_rust_default_functions(entry, name, rust_type):
"""Generate Rust code for the default handling"""
- defvalue = entry['default']
- if entry['type'] in listOfStringTypes:
+ defvalue = entry["default"]
+ if entry["type"] in listOfStringTypes:
return gen_rust_stringvec_default_functions(entry, name)
- if entry['type'] in listOfStructuredTypes:
- baseName = list_to_base_type(entry['type'])
+ if entry["type"] in listOfStructuredTypes:
+ baseName = list_to_base_type(entry["type"])
return gen_rust_vec_default_functions(name, baseName, defvalue)
- ret = f'// DEFAULT HANDLING for {name}\n'
- ret += f'fn default_value_{name}() -> {rust_type} {{\n'
+ ret = f"// DEFAULT HANDLING for {name}\n"
+ ret += f"fn default_value_{name}() -> {rust_type} {{\n"
rustdef = f'env!("{defvalue}")' if isEnvVar(defvalue) else quote(defvalue)
ret += f" String::from({rustdef})\n"
- ret += '}\n'
- if rust_type == 'String':
- rust_type = 'str'
- ret += f'fn default_value_equal_{name}(value: &{rust_type})'
- ret += '-> bool {\n'
- ret += f' value == default_value_{name}()\n'
- ret += '}\n\n'
+ ret += "}\n"
+ if rust_type == "String":
+ rust_type = "str"
+ ret += f"fn default_value_equal_{name}(value: &{rust_type})"
+ ret += "-> bool {\n"
+ ret += f" value == default_value_{name}()\n"
+ ret += "}\n\n"
return ret
+
def write_rust_field(file, entry, default_funcs):
"""Generate Rust code for a field with Serde annotations"""
- rust_type = get_rust_type(entry['type'])
- the_default = entry['default']
+ rust_type = get_rust_type(entry["type"])
+ the_default = entry["default"]
is_rust_default = is_value_rust_default(rust_type, the_default)
if is_rust_default:
file.write(' #[serde(default, skip_serializing_if = "crate::is_default")]\n')
else:
- if entry['type'] == LType.Bool:
+ if entry["type"] == LType.Bool:
file.write(' #[serde(default = "crate::Bool::<true>::value", ')
file.write('skip_serializing_if = "crate::if_true")]\n')
- elif entry['type'] == LType.Uint64:
+ elif entry["type"] == LType.Uint64:
file.write(f' #[serde(default = "crate::U64::<{the_default}>::value", ')
file.write(f'skip_serializing_if = "crate::U64::<{the_default}>::is_equal")]\n')
else:
- basename = entry['section'] + '_' + entry['name']
+ basename = entry["section"] + "_" + entry["name"]
file.write(f' #[serde(default = "crate::default_value_{basename}", ')
file.write(f'skip_serializing_if = "crate::default_value_equal_{basename}")]\n')
default_funcs.append(gen_rust_default_functions(entry, basename, rust_type))
file.write(f" {entry['name']}: {rust_type},\n\n")
+
def write_rust_section(file, section, entries, default_funcs):
"""Generate Rust code for a Section with Serde annotations"""
- file.write(f' // SECTION {section.capitalize()}\n')
- file.write(' #[derive(Deserialize, Serialize, Debug, PartialEq)]\n')
- file.write(' #[serde(deny_unknown_fields)]\n')
- file.write(f' pub struct {section.capitalize()} {{\n')
+ file.write(f" // SECTION {section.capitalize()}\n")
+ file.write(" #[derive(Deserialize, Serialize, Debug, PartialEq)]\n")
+ file.write(" #[serde(deny_unknown_fields)]\n")
+ file.write(f" pub struct {section.capitalize()} {{\n")
for entry in entries:
- if entry['section'] != section:
+ if entry["section"] != section:
continue
- if entry['type'] == LType.Command:
+ if entry["type"] == LType.Command:
continue
- if 'skip-yaml' in entry:
+ if "skip-yaml" in entry:
continue
write_rust_field(file, entry, default_funcs)
- file.write(f' }}\n // END SECTION {section.capitalize()}\n\n')
+ file.write(f" }}\n // END SECTION {section.capitalize()}\n\n")
+
#
# Each section als has a Default implementation, so that a section with all entries having a default
# value does not get generated into a yaml section. Such a trait looks like:
#
-#impl Default for recsettings::ForwardZone {
+# impl Default for recsettings::ForwardZone {
# fn default() -> Self {
# let deserialized: recsettings::ForwardZone = serde_yaml::from_str("").unwrap();
# deserialized
# }
-#}
+# }
+
def write_rust_default_trait_impl(file, section):
"""Generate Rust code for the default Trait for a section"""
- file.write(f'impl Default for recsettings::{section.capitalize()} {{\n')
- file.write(' fn default() -> Self {\n')
- file.write(' let deserialized: recsettings::')
+ file.write(f"impl Default for recsettings::{section.capitalize()} {{\n")
+ file.write(" fn default() -> Self {\n")
+ file.write(" let deserialized: recsettings::")
file.write(f'{section.capitalize()} = serde_yaml::from_str("").unwrap();\n')
- file.write(' deserialized\n')
- file.write(' }\n')
- file.write('}\n\n')
+ file.write(" deserialized\n")
+ file.write(" }\n")
+ file.write("}\n\n")
+
def write_validator(file, section, entries):
"""Generate Rust code for the Validator Trait for a section"""
- file.write(f'impl Validate for recsettings::{section.capitalize()} {{\n')
- file.write(' fn validate(&self) -> Result<(), ValidationError> {\n')
+ file.write(f"impl Validate for recsettings::{section.capitalize()} {{\n")
+ file.write(" fn validate(&self) -> Result<(), ValidationError> {\n")
for entry in entries:
- if entry['section'] != section:
+ if entry["section"] != section:
continue
- name = entry['name'].lower()
- typ = entry['type']
+ name = entry["name"].lower()
+ typ = entry["type"]
if typ == LType.ListSubnets:
- validator = 'validate_subnet'
+ validator = "validate_subnet"
elif typ == LType.ListSocketAddresses:
- validator = 'validate_socket_address'
+ validator = "validate_socket_address"
elif typ in listOfStructuredTypes:
- validator = '|field, element| element.validate(field)'
+ validator = "|field, element| element.validate(field)"
else:
continue
file.write(f' let fieldname = "{section.lower()}.{name}".to_string();\n')
- file.write(f' validate_vec(&fieldname, &self.{name}, {validator})?;\n')
- file.write(f' validate_{section.lower()}(self)\n')
- file.write(' }\n')
- file.write('}\n\n')
+ file.write(f" validate_vec(&fieldname, &self.{name}, {validator})?;\n")
+ file.write(f" validate_{section.lower()}(self)\n")
+ file.write(" }\n")
+ file.write("}\n\n")
+
def write_rust_merge_trait_impl(file, section, entries):
"""Generate Rust code for the Merge Trait for a section"""
- file.write(f'impl Merge for recsettings::{section.capitalize()} {{\n')
- file.write(' fn merge(&mut self, rhs: &mut Self, map: Option<&serde_yaml::Mapping>) {\n')
- file.write(' if let Some(m) = map {\n')
+ file.write(f"impl Merge for recsettings::{section.capitalize()} {{\n")
+ file.write(" fn merge(&mut self, rhs: &mut Self, map: Option<&serde_yaml::Mapping>) {\n")
+ file.write(" if let Some(m) = map {\n")
for entry in entries:
- if entry['section'] != section:
+ if entry["section"] != section:
continue
- if 'skip-yaml' in entry:
+ if "skip-yaml" in entry:
continue
- rtype = get_rust_type(entry['type'])
- name = entry['name']
+ rtype = get_rust_type(entry["type"])
+ name = entry["name"]
file.write(f' if m.contains_key("{name}") {{\n')
- if rtype in ('bool', 'u64', 'f64', 'String'):
- file.write(f' rhs.{name}.clone_into(&mut self.{name});\n')
+ if rtype in ("bool", "u64", "f64", "String"):
+ file.write(f" rhs.{name}.clone_into(&mut self.{name});\n")
else:
file.write(f' if is_overriding(m, "{name}") || ')
- file.write(f'self.{name} == DEFAULT_CONFIG.{section}.{name} {{\n')
- file.write(f' self.{name}.clear();\n')
- file.write(' }\n')
- file.write(f' merge_vec(&mut self.{name}, &mut rhs.{name});\n')
- file.write(' }\n')
- file.write(' }\n')
- file.write(' }\n')
- file.write('}\n\n')
+ file.write(f"self.{name} == DEFAULT_CONFIG.{section}.{name} {{\n")
+ file.write(f" self.{name}.clear();\n")
+ file.write(" }\n")
+ file.write(f" merge_vec(&mut self.{name}, &mut rhs.{name});\n")
+ file.write(" }\n")
+ file.write(" }\n")
+ file.write(" }\n")
+ file.write("}\n\n")
+
def gen_rust(srcdir, entries):
"""Generate Rust code all entries"""
def_functions = []
sections = {}
- with open(srcdir + '/rust/src/lib.rs', mode='w', encoding='UTF-8') as file:
- file.write('// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE: see settings dir\n')
- file.write('// START INCLUDE rust-preamble-in.rs\n')
- with open(srcdir + '/rust-preamble-in.rs', mode='r', encoding='UTF-8') as pre:
+ with open(srcdir + "/rust/src/lib.rs", mode="w", encoding="UTF-8") as file:
+ file.write("// THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE: see settings dir\n")
+ file.write("// START INCLUDE rust-preamble-in.rs\n")
+ with open(srcdir + "/rust-preamble-in.rs", mode="r", encoding="UTF-8") as pre:
file.write(pre.read())
- file.write('// END INCLUDE rust-preamble-in.rs\n\n')
+ file.write("// END INCLUDE rust-preamble-in.rs\n\n")
file.write('#[cxx::bridge(namespace = "pdns::rust::settings::rec")]\n')
- file.write('mod recsettings {\n')
- with open(srcdir + '/rust-bridge-in.rs', mode='r', encoding='UTF-8') as bridge:
- file.write(' // START INCLUDE rust-bridge-in.rs\n')
+ file.write("mod recsettings {\n")
+ with open(srcdir + "/rust-bridge-in.rs", mode="r", encoding="UTF-8") as bridge:
+ file.write(" // START INCLUDE rust-bridge-in.rs\n")
for line in bridge:
- file.write(' ' + line)
+ file.write(" " + line)
- file.write(' // END INCLUDE rust-bridge-in.rs\n\n')
+ file.write(" // END INCLUDE rust-bridge-in.rs\n\n")
for entry in entries:
- if entry['section'] == 'commands':
+ if entry["section"] == "commands":
continue
- sections[entry['section']] = entry['section']
+ sections[entry["section"]] = entry["section"]
for section in sections:
write_rust_section(file, section, entries, def_functions)
- file.write(' #[derive(Serialize, Deserialize, Debug)]\n')
- file.write(' #[serde(deny_unknown_fields)]\n')
- file.write(' pub struct Recursorsettings {\n')
+ file.write(" #[derive(Serialize, Deserialize, Debug)]\n")
+ file.write(" #[serde(deny_unknown_fields)]\n")
+ file.write(" pub struct Recursorsettings {\n")
for section in sections:
file.write(' #[serde(default, skip_serializing_if = "crate::is_default")]\n')
- file.write(f' {section.lower()}: {section.capitalize()},\n')
- file.write('} // End of generated structs\n')
- file.write('}\n')
+ file.write(f" {section.lower()}: {section.capitalize()},\n")
+ file.write("} // End of generated structs\n")
+ file.write("}\n")
for section in sections:
write_rust_default_trait_impl(file, section)
- write_rust_default_trait_impl(file, 'Recursorsettings')
+ write_rust_default_trait_impl(file, "Recursorsettings")
for section in sections:
write_validator(file, section, entries)
- file.write('impl crate::recsettings::Recursorsettings {\n')
- file.write(' fn validate(&self) -> Result<(), ValidationError> {\n')
+ file.write("impl crate::recsettings::Recursorsettings {\n")
+ file.write(" fn validate(&self) -> Result<(), ValidationError> {\n")
for section in sections:
- file.write(f' self.{section.lower()}.validate()?;\n')
- file.write(' Ok(())\n')
- file.write(' }\n')
- file.write('}\n\n')
+ file.write(f" self.{section.lower()}.validate()?;\n")
+ file.write(" Ok(())\n")
+ file.write(" }\n")
+ file.write("}\n\n")
for section in sections:
write_rust_merge_trait_impl(file, section, entries)
- file.write('impl Merge for crate::recsettings::Recursorsettings {\n')
- file.write(' fn merge(&mut self, rhs: &mut Self, map: Option<&serde_yaml::Mapping>) {\n')
- file.write(' if let Some(m) = map {\n')
+ file.write("impl Merge for crate::recsettings::Recursorsettings {\n")
+ file.write(" fn merge(&mut self, rhs: &mut Self, map: Option<&serde_yaml::Mapping>) {\n")
+ file.write(" if let Some(m) = map {\n")
for section in sections:
file.write(f' if let Some(s) = m.get("{section}") {{\n')
- file.write(' if s.is_mapping() {\n')
- file.write((f' self.{section}.merge(&mut rhs.{section},'
- ' s.as_mapping());\n'))
- file.write(' }\n')
- file.write(' }\n')
- file.write(' }\n')
- file.write(' }\n')
- file.write('}\n\n')
+ file.write(" if s.is_mapping() {\n")
+ file.write((f" self.{section}.merge(&mut rhs.{section}, s.as_mapping());\n"))
+ file.write(" }\n")
+ file.write(" }\n")
+ file.write(" }\n")
+ file.write(" }\n")
+ file.write("}\n\n")
for entry in def_functions:
file.write(entry)
file.close()
+
def gen_docs_meta(file, entry, name, is_tuple):
"""Write .. versionadded:: and related entries"""
if name in entry:
val = [val]
for vers in val:
if is_tuple:
- file.write(f'.. {name}:: {vers[0]}\n\n')
- file.write(f' {vers[1].strip()}\n')
+ file.write(f".. {name}:: {vers[0]}\n\n")
+ file.write(f" {vers[1].strip()}\n")
else:
- file.write(f'.. {name}:: {vers}\n')
+ file.write(f".. {name}:: {vers}\n")
+
def gen_oldstyle_docs(srcdir, entries):
"""Write old style docs"""
- with open(srcdir + '/../docs/settings.rst', mode='w', encoding='UTF-8') as file:
- file.write('.. THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE: see settings dir\n')
- file.write(' START INCLUDE docs-old-preamble-in.rst\n\n')
- with open(srcdir + '/docs-old-preamble-in.rst', mode='r', encoding='UTF-8') as pre:
+ with open(srcdir + "/../docs/settings.rst", mode="w", encoding="UTF-8") as file:
+ file.write(".. THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE: see settings dir\n")
+ file.write(" START INCLUDE docs-old-preamble-in.rst\n\n")
+ with open(srcdir + "/docs-old-preamble-in.rst", mode="r", encoding="UTF-8") as pre:
file.write(pre.read())
- file.write('.. END INCLUDE docs-old-preamble-in.rst\n\n')
+ file.write(".. END INCLUDE docs-old-preamble-in.rst\n\n")
for entry in entries:
- if entry['type'] == LType.Command:
+ if entry["type"] == LType.Command:
continue
- if entry['doc'].strip() == 'SKIP':
+ if entry["doc"].strip() == "SKIP":
continue
- if 'skip-old' in entry:
+ if "skip-old" in entry:
continue
- oldname = entry['oldname']
- section = entry['section']
- file.write(f'.. _setting-{oldname}:\n\n')
- file.write(f'``{oldname}``\n')
- dots = '~' * (len(entry['oldname']) + 4)
- file.write(f'{dots}\n')
- gen_docs_meta(file, entry, 'versionadded', False)
- gen_docs_meta(file, entry, 'versionchanged', True)
- gen_docs_meta(file, entry, 'deprecated', True)
- if 'doc-rst' in entry:
- file.write(entry['doc-rst'].strip())
- file.write('\n')
- file.write('\n')
- typ = get_olddoc_typename(entry['type'])
- file.write(f'- {typ}\n')
- if 'docdefault' in entry:
+ oldname = entry["oldname"]
+ section = entry["section"]
+ file.write(f".. _setting-{oldname}:\n\n")
+ file.write(f"``{oldname}``\n")
+ dots = "~" * (len(entry["oldname"]) + 4)
+ file.write(f"{dots}\n")
+ gen_docs_meta(file, entry, "versionadded", False)
+ gen_docs_meta(file, entry, "versionchanged", True)
+ gen_docs_meta(file, entry, "deprecated", True)
+ if "doc-rst" in entry:
+ file.write(entry["doc-rst"].strip())
+ file.write("\n")
+ file.write("\n")
+ typ = get_olddoc_typename(entry["type"])
+ file.write(f"- {typ}\n")
+ if "docdefault" in entry:
file.write(f"- Default: {entry['docdefault']}\n\n")
else:
- file.write((f"- Default: "
- f"{get_default_olddoc_value(entry['type'], entry['default'])}\n\n"))
- if 'skip-yaml' in entry:
- file.write('- YAML setting does not exist\n\n')
+ file.write((f"- Default: {get_default_olddoc_value(entry['type'], entry['default'])}\n\n"))
+ if "skip-yaml" in entry:
+ file.write("- YAML setting does not exist\n\n")
else:
file.write(f"- YAML setting: :ref:`setting-yaml-{section}.{entry['name']}`\n\n")
- if 'runtime' in entry:
- runtime = entry['runtime']
+ if "runtime" in entry:
+ runtime = entry["runtime"]
if not isinstance(runtime, list):
runtime = [runtime]
li = []
for v in runtime:
- if v == 'reload-yaml':
- continue
- li.append('``' + v + '``')
+ if v == "reload-yaml":
+ continue
+ li.append("``" + v + "``")
file.write(f"- Runtime modifiable using ``rec_control`` {', '.join(f'{w}' for w in li)}\n\n")
- file.write(entry['doc'].strip())
- file.write('\n\n')
+ file.write(entry["doc"].strip())
+ file.write("\n\n")
+
def fixxrefs(entries, arg):
"""Docs in table refer to old style names, we modify them to ref to new style"""
- matches = re.findall(':ref:`setting-(.*?)`', arg)
+ matches = re.findall(":ref:`setting-(.*?)`", arg)
# We want to replace longest match first, to avoid a short match modifying a long one
- matches = sorted(matches, key = lambda x: -len(x))
+ matches = sorted(matches, key=lambda x: -len(x))
for match in matches:
for entry in entries:
- if entry['oldname'] == match:
- key = ':ref:`setting-' + match
- repl = ':ref:`setting-yaml-' + entry['section'] + '.' + entry['name']
+ if entry["oldname"] == match:
+ key = ":ref:`setting-" + match
+ repl = ":ref:`setting-yaml-" + entry["section"] + "." + entry["name"]
arg = arg.replace(key, repl)
return arg
+
def gen_newstyle_docs(srcdir, argentries):
"""Write new style docs"""
- entries = sorted(argentries, key = lambda entry: [entry['section'], entry['name']])
- with open(srcdir + '/../docs/yamlsettings.rst', 'w', encoding='utf-8') as file:
- file.write('.. THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE: see settings dir\n')
- file.write(' START INCLUDE docs-new-preamble-in.rst\n\n')
- with open(srcdir + '/docs-new-preamble-in.rst', mode='r', encoding='utf-8') as pre:
+ entries = sorted(argentries, key=lambda entry: [entry["section"], entry["name"]])
+ with open(srcdir + "/../docs/yamlsettings.rst", "w", encoding="utf-8") as file:
+ file.write(".. THIS IS A GENERATED FILE. DO NOT EDIT. SOURCE: see settings dir\n")
+ file.write(" START INCLUDE docs-new-preamble-in.rst\n\n")
+ with open(srcdir + "/docs-new-preamble-in.rst", mode="r", encoding="utf-8") as pre:
file.write(pre.read())
- file.write('.. END INCLUDE docs-new-preamble-in.rst\n\n')
+ file.write(".. END INCLUDE docs-new-preamble-in.rst\n\n")
for entry in entries:
- if entry['type'] == LType.Command:
+ if entry["type"] == LType.Command:
continue
- if entry['doc'].strip() == 'SKIP':
+ if entry["doc"].strip() == "SKIP":
continue
- if 'skip-yaml' in entry:
+ if "skip-yaml" in entry:
continue
- section = entry['section']
- name = entry['name']
- fullname = section + '.' + name
- file.write(f'.. _setting-yaml-{fullname}:\n\n')
- file.write(f'``{fullname}``\n')
- dots = '^' * (len(fullname) + 4)
- file.write(f'{dots}\n')
- gen_docs_meta(file, entry, 'versionadded', False)
- gen_docs_meta(file, entry, 'versionchanged', True)
- gen_docs_meta(file, entry, 'deprecated', True)
- if 'doc-rst' in entry:
- file.write(fixxrefs(entries, entry['doc-rst'].strip()))
- file.write('\n')
- file.write('\n')
+ section = entry["section"]
+ name = entry["name"]
+ fullname = section + "." + name
+ file.write(f".. _setting-yaml-{fullname}:\n\n")
+ file.write(f"``{fullname}``\n")
+ dots = "^" * (len(fullname) + 4)
+ file.write(f"{dots}\n")
+ gen_docs_meta(file, entry, "versionadded", False)
+ gen_docs_meta(file, entry, "versionchanged", True)
+ gen_docs_meta(file, entry, "deprecated", True)
+ if "doc-rst" in entry:
+ file.write(fixxrefs(entries, entry["doc-rst"].strip()))
+ file.write("\n")
+ file.write("\n")
file.write(f"- {get_newdoc_typename(entry['type'])}\n")
- if 'docdefault' in entry:
+ if "docdefault" in entry:
file.write(f"- Default: {entry['docdefault']}\n\n")
else:
- file.write((f"- Default: "
- f"{get_default_newdoc_value(entry['type'], entry['default'])}\n\n"))
- if 'skip-old' in entry:
+ file.write((f"- Default: {get_default_newdoc_value(entry['type'], entry['default'])}\n\n"))
+ if "skip-old" in entry:
file.write(f"- {entry['skip-old']}\n\n")
else:
file.write(f"- Old style setting: :ref:`setting-{entry['oldname']}`\n\n")
- if 'runtime' in entry:
- runtime = entry['runtime']
+ if "runtime" in entry:
+ runtime = entry["runtime"]
if not isinstance(runtime, list):
runtime = [runtime]
li = []
for v in runtime:
- vv = '``' + v + '``'
- if v == 'reload-yaml':
- vv = 'since 5.2.0: ' + vv
+ vv = "``" + v + "``"
+ if v == "reload-yaml":
+ vv = "since 5.2.0: " + vv
li.append(vv)
file.write(f"- Runtime modifiable using ``rec_control`` {', '.join(f'{w}' for w in li)}\n\n")
- if 'doc-new' in entry:
- file.write(fixxrefs(entries, entry['doc-new'].strip()))
+ if "doc-new" in entry:
+ file.write(fixxrefs(entries, entry["doc-new"].strip()))
else:
- file.write(fixxrefs(entries, entry['doc'].strip()))
- file.write('\n\n')
+ file.write(fixxrefs(entries, entry["doc"].strip()))
+ file.write("\n\n")
+
+
+RUNTIME = "*runtime determined*"
-RUNTIME = '*runtime determined*'
def unlinkMissingOK(path):
try:
except FileNotFoundError:
pass
+
def generate():
"""Read table, validate and generate C++, Rust and .rst files"""
- srcdir = '.'
- gendir = '.'
+ srcdir = "."
+ gendir = "."
if len(sys.argv) == 3:
print("Generate: using srcdir and gendir from arguments")
srcdir = sys.argv[1]
print("Generate gendir: " + gendir + " = " + os.path.realpath(gendir))
# read table
- with open(srcdir + '/table.py', mode='r', encoding="utf-8") as file:
+ with open(srcdir + "/table.py", mode="r", encoding="utf-8") as file:
entries = eval(file.read())
for entry in entries:
- the_oldname = entry['name'].replace('_', '-')
- if 'oldname' in entry:
- if entry['oldname'] == the_oldname:
+ the_oldname = entry["name"].replace("_", "-")
+ if "oldname" in entry:
+ if entry["oldname"] == the_oldname:
sys.stderr.write(f"Redundant old name {entry['oldname']}\n")
else:
- entry['oldname'] = the_oldname
+ entry["oldname"] = the_oldname
dupcheck1 = {}
dupcheck2 = {}
for entry in entries:
- if entry['oldname'] in dupcheck1:
+ if entry["oldname"] in dupcheck1:
sys.stderr.write(f"duplicate entries with oldname = {entry['oldname']}\n")
sys.exit(1)
- if entry['section'] + '.' + entry['name'] in dupcheck2:
- sys.stderr.write((f"duplicate entries with section.name = "
- f"{entry['section']}.{ entry['name']}\n"))
+ if entry["section"] + "." + entry["name"] in dupcheck2:
+ sys.stderr.write((f"duplicate entries with section.name = {entry['section']}.{entry['name']}\n"))
sys.exit(1)
- dupcheck1[entry['oldname']] = True
- dupcheck2[entry['section'] + '.' + entry['name']] = True
+ dupcheck1[entry["oldname"]] = True
+ dupcheck2[entry["section"] + "." + entry["name"]] = True
# And generate C++, Rust and docs code based on table
# C++ code goes int build dir
gen_cxx(gendir, entries)
# with mixed sources both in build and src dir
gen_rust(srcdir, entries)
# Avoid generating doc files in a sdist based build
- if os.path.isdir(srcdir + '/../docs'):
+ if os.path.isdir(srcdir + "/../docs"):
gen_oldstyle_docs(srcdir, entries)
gen_newstyle_docs(srcdir, entries)
# Remove cxx generated files, they need to be re-generated after a table change and the rust dependency tracking does
# not do that in some cases.
- unlinkMissingOK(Path(gendir, 'rust', 'librecrust.a'))
- unlinkMissingOK(Path(gendir, 'rust', 'lib.rs.h'))
- unlinkMissingOK(Path(gendir, 'rust', 'web.rs.h'))
- unlinkMissingOK(Path(gendir, 'rust', 'cxx.h'))
- unlinkMissingOK(Path(gendir, 'rust', 'misc.rs.h'))
+ unlinkMissingOK(Path(gendir, "rust", "librecrust.a"))
+ unlinkMissingOK(Path(gendir, "rust", "lib.rs.h"))
+ unlinkMissingOK(Path(gendir, "rust", "web.rs.h"))
+ unlinkMissingOK(Path(gendir, "rust", "cxx.h"))
+ unlinkMissingOK(Path(gendir, "rust", "misc.rs.h"))
# Path.walk exists only in very recent versions of Python
# With meson, target is in toplevel build dir
# With autotools, target exists in rec-rust-lib/rust and this Python script is executed with cwd rec-rust-lib
- for topdir in ['target', 'rust/target']:
+ for topdir in ["target", "rust/target"]:
for root, dirs, files in os.walk(topdir, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
+
generate()
[
{
- 'name' : 'aggressive_nsec_cache_size',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '100000',
- 'help' : 'The number of records to cache in the aggressive cache. If set to a value greater than 0, and DNSSEC processing or validation is enabled, the recursor will cache NSEC and NSEC3 records to generate negative answers, as defined in rfc8198',
- 'doc' : '''
+ "name": "aggressive_nsec_cache_size",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "100000",
+ "help": "The number of records to cache in the aggressive cache. If set to a value greater than 0, and DNSSEC processing or validation is enabled, the recursor will cache NSEC and NSEC3 records to generate negative answers, as defined in rfc8198",
+ "doc": """
The number of records to cache in the aggressive cache. If set to a value greater than 0, the recursor will cache NSEC and NSEC3 records to generate negative answers, as defined in :rfc:`8198`.
To use this, DNSSEC processing or validation must be enabled by setting :ref:`setting-dnssec` to ``process``, ``log-fail`` or ``validate``.
- ''',
- 'versionadded': '4.5.0',
- 'runtime': 'set-max-aggr-nsec-cache-size',
+ """,
+ "versionadded": "4.5.0",
+ "runtime": "set-max-aggr-nsec-cache-size",
},
{
- 'name' : 'aggressive_cache_min_nsec3_hit_ratio',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '2000',
- 'help' : 'The minimum expected hit ratio to store NSEC3 records into the aggressive cache',
- 'doc' : '''
+ "name": "aggressive_cache_min_nsec3_hit_ratio",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "2000",
+ "help": "The minimum expected hit ratio to store NSEC3 records into the aggressive cache",
+ "doc": """
The limit for which to put NSEC3 records into the aggressive cache.
A value of ``n`` means that an NSEC3 record is only put into the aggressive cache if the estimated probability of a random name hitting the NSEC3 record is higher than ``1/n``.
A higher ``n`` will cause more records to be put into the aggressive cache, e.g. a value of 4000 will cause records to be put in the aggressive cache even if the estimated probability of hitting them is twice as low as would be the case for ``n=2000``.
For large zones the effectiveness of the NSEC3 cache is reduced since each NSEC3 record only covers a randomly distributed subset of all possible names.
This setting avoids doing unnecessary work for such large zones.
- ''',
- 'versionadded' : '4.9.0',
+ """,
+ "versionadded": "4.9.0",
},
{
- 'name' : 'allow_from',
- 'section' : 'incoming',
- 'type' : LType.ListSubnets,
- 'default' : '127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10',
- 'help' : 'If set, only allow these comma separated netmasks to recurse',
- 'doc' : '''
+ "name": "allow_from",
+ "section": "incoming",
+ "type": LType.ListSubnets,
+ "default": "127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10",
+ "help": "If set, only allow these comma separated netmasks to recurse",
+ "doc": """
Netmasks (both IPv4 and IPv6) that are allowed to use the server.
The default allows access only from :rfc:`1918` private IP addresses.
An empty value means no checking is done, all clients are allowed.
When the Proxy Protocol is enabled (see :ref:`setting-proxy-protocol-from`), the recursor will check the address of the client IP advertised in the Proxy Protocol header instead of the one of the proxy.
Note that specifying an IP address without a netmask uses an implicit netmask of /32 or /128.
- ''',
- 'runtime': ['reload-acls'],
+ """,
+ "runtime": ["reload-acls"],
},
{
- 'name' : 'allow_from_file',
- 'section' : 'incoming',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'If set, load allowed netmasks from this file',
- 'doc' : '''
+ "name": "allow_from_file",
+ "section": "incoming",
+ "type": LType.String,
+ "default": "",
+ "help": "If set, load allowed netmasks from this file",
+ "doc": """
Like :ref:`setting-allow-from`, except reading from file.
Overrides the :ref:`setting-allow-from` setting. To use this feature, supply one netmask per line, with optional comments preceded by a '#'.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Like :ref:`setting-allow-from`, except reading a sequence of `Subnet`_ from file.
Overrides the :ref:`setting-allow-from` setting. Example content of th specified file:
- 127.0.0.1
- ::1
- ''',
- 'runtime': ['reload-acls'],
+ """,
+ "runtime": ["reload-acls"],
},
{
- 'name' : 'allow_notify_for',
- 'section' : 'incoming',
- 'type' : LType.ListStrings,
- 'default' : '',
- 'help' : 'If set, NOTIFY requests for these zones will be allowed',
- 'doc' : '''
+ "name": "allow_notify_for",
+ "section": "incoming",
+ "type": LType.ListStrings,
+ "default": "",
+ "help": "If set, NOTIFY requests for these zones will be allowed",
+ "doc": """
Domain names specified in this list are used to permit incoming
NOTIFY operations to wipe any cache entries that match the domain
name. If this list is empty, all NOTIFY operations will be ignored.
Matching is done using suffix matching, it is allowed to NOTIFY a subdomain of a listed domain.
- ''',
- 'versionadded': '4.6.0',
- 'runtime': ['reload-acls'],
+ """,
+ "versionadded": "4.6.0",
+ "runtime": ["reload-acls"],
},
{
- 'name' : 'allow_notify_for_file',
- 'section' : 'incoming',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'If set, load NOTIFY-allowed zones from this file',
- 'doc' : '''
+ "name": "allow_notify_for_file",
+ "section": "incoming",
+ "type": LType.String,
+ "default": "",
+ "help": "If set, load NOTIFY-allowed zones from this file",
+ "doc": """
Like :ref:`setting-allow-notify-for`, except reading from file. To use this
feature, supply one domain name per line, with optional comments
preceded by a '#'.
NOTIFY-allowed zones can also be specified using :ref:`setting-forward-zones-file`.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Like :ref:`setting-allow-notify-for`, except reading a sequence of names from file. Example contents of specified file:
.. code-block:: yaml
- example.com
- example.org
- ''',
- 'versionadded': '4.6.0',
- 'runtime': ['reload-acls'],
+ """,
+ "versionadded": "4.6.0",
+ "runtime": ["reload-acls"],
},
{
- 'name' : 'allow_notify_from',
- 'section' : 'incoming',
- 'type' : LType.ListSubnets,
- 'default' : '',
- 'help' : 'If set, NOTIFY requests from these comma separated netmasks will be allowed',
- 'doc' : '''
+ "name": "allow_notify_from",
+ "section": "incoming",
+ "type": LType.ListSubnets,
+ "default": "",
+ "help": "If set, NOTIFY requests from these comma separated netmasks will be allowed",
+ "doc": """
Netmasks (both IPv4 and IPv6) that are allowed to issue NOTIFY operations
to the server. NOTIFY operations from IP addresses not listed here are
ignored and do not get an answer.
the zone specified in the NOTIFY operation, but only if that zone (or
one of its parents) is included in :ref:`setting-allow-notify-for`,
:ref:`setting-allow-notify-for-file`, or :ref:`setting-forward-zones-file` with a '^' prefix.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Subnets (both IPv4 and IPv6) that are allowed to issue NOTIFY operations
to the server. NOTIFY operations from IP addresses not listed here are
ignored and do not get an answer.
the zone specified in the NOTIFY operation, but only if that zone (or
one of its parents) is included in :ref:`setting-allow-notify-for`,
:ref:`setting-allow-notify-for-file`, or :ref:`setting-forward-zones-file` with a ``allow_notify`` set to ``true``.
- ''',
- 'versionadded': '4.6.0',
- 'runtime': ['reload-acls'],
+ """,
+ "versionadded": "4.6.0",
+ "runtime": ["reload-acls"],
},
{
- 'name' : 'allow_notify_from_file',
- 'section' : 'incoming',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'If set, load NOTIFY-allowed netmasks from this file',
- 'doc' : '''
+ "name": "allow_notify_from_file",
+ "section": "incoming",
+ "type": LType.String,
+ "default": "",
+ "help": "If set, load NOTIFY-allowed netmasks from this file",
+ "doc": """
Like :ref:`setting-allow-notify-from`, except reading from file. To use this
feature, supply one netmask per line, with optional comments preceded
by a '#'.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Like :ref:`setting-allow-notify-from`, except reading a sequence of `Subnet`_ from file.
- ''',
- 'versionadded': '4.6.0',
- 'runtime': ['reload-acls'],
+ """,
+ "versionadded": "4.6.0",
+ "runtime": ["reload-acls"],
},
{
- 'name' : 'allow_no_rd',
- 'section' : 'incoming',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Allow \'no recursion desired (RD=0)\' queries.',
- 'doc' : '''
+ "name": "allow_no_rd",
+ "section": "incoming",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Allow 'no recursion desired (RD=0)' queries.",
+ "doc": """
Allow ``no recursion desired (RD=0) queries`` to query cache contents.
If not set (the default), these queries are answered with rcode ``Refused``.
- ''',
- 'versionadded': '5.0.0'
+ """,
+ "versionadded": "5.0.0",
},
{
- 'name' : 'any_to_tcp',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Answer ANY queries with tc=1, shunting to TCP',
- 'doc' : '''
+ "name": "any_to_tcp",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Answer ANY queries with tc=1, shunting to TCP",
+ "doc": """
Answer questions for the ANY type on UDP with a truncated packet that refers the remote client to TCP.
Useful for mitigating ANY reflection attacks.
- ''',
- 'versionchanged': ('5.4.0', 'Default is enabled now, was disabled before 5.4.0'),
+ """,
+ "versionchanged": ("5.4.0", "Default is enabled now, was disabled before 5.4.0"),
},
{
- 'name' : 'any_to_tcp',
- 'oldname': 'out-any-to-tcp',
- 'section' : 'outgoing',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Use TCP for ANY queries to authoritative servers',
- 'doc' : '''
+ "name": "any_to_tcp",
+ "oldname": "out-any-to-tcp",
+ "section": "outgoing",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Use TCP for ANY queries to authoritative servers",
+ "doc": """
Send out requests with qtype `ANY` using TCP.
- ''',
- 'versionadded': '5.4.0',
+ """,
+ "versionadded": "5.4.0",
},
{
- 'name' : 'allow_trust_anchor_query',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Allow queries for trustanchor.server CH TXT and negativetrustanchor.server CH TXT',
- 'doc' : '''
+ "name": "allow_trust_anchor_query",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Allow queries for trustanchor.server CH TXT and negativetrustanchor.server CH TXT",
+ "doc": """
Allow ``trustanchor.server CH TXT`` and ``negativetrustanchor.server CH TXT`` queries to view the configured :doc:`DNSSEC <dnssec>` (negative) trust anchors.
- ''',
- 'versionadded': '4.3.0'
+ """,
+ "versionadded": "4.3.0",
},
{
- 'name' : 'api_dir',
- 'section' : 'webservice',
- 'oldname' : 'api-config-dir',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Directory where REST API stores config and zones',
- 'doc' : '''
+ "name": "api_dir",
+ "section": "webservice",
+ "oldname": "api-config-dir",
+ "type": LType.String,
+ "default": "",
+ "help": "Directory where REST API stores config and zones",
+ "doc": """
Directory where the REST API stores its configuration and zones.
For configuration updates to work, :ref:`setting-include-dir` should have the same value when using old-style settings.
When using YAML settings :ref:`setting-yaml-recursor.include_dir` and :ref:`setting-yaml-webservice.api_dir` must have a different value.
- ''',
- 'versionadded': '4.0.0'
- },
- {
- 'name' : 'api_key',
- 'section' : 'webservice',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Static pre-shared authentication key for access to the REST API',
- 'doc' : '''
+ """,
+ "versionadded": "4.0.0",
+ },
+ {
+ "name": "api_key",
+ "section": "webservice",
+ "type": LType.String,
+ "default": "",
+ "help": "Static pre-shared authentication key for access to the REST API",
+ "doc": """
Static pre-shared authentication key for access to the REST API. Since 4.6.0 the key can be hashed and salted using ``rec_control hash-password`` instead of being stored in the configuration in plaintext, but the plaintext version is still supported.
- ''',
- 'versionadded': '4.0.0',
- 'versionchanged': ('4.6.0', 'This setting now accepts a hashed and salted version.')
+ """,
+ "versionadded": "4.0.0",
+ "versionchanged": ("4.6.0", "This setting now accepts a hashed and salted version."),
},
{
- 'name' : 'auth_zones',
- 'section' : 'recursor',
- 'type' : LType.ListAuthZones,
- 'default' : '',
- 'help' : 'Zones for which we have authoritative data, comma separated domain=file pairs',
- 'doc' : '''
+ "name": "auth_zones",
+ "section": "recursor",
+ "type": LType.ListAuthZones,
+ "default": "",
+ "help": "Zones for which we have authoritative data, comma separated domain=file pairs",
+ "doc": """
Zones read from these files (in BIND format) are served authoritatively (but without the AA bit set in responses).
DNSSEC is not supported. Example:
.. code-block:: none
auth-zones=example.org=/var/zones/example.org, powerdns.com=/var/zones/powerdns.com
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Zones read from these files (in BIND format) are served authoritatively (but without the AA bit set in responses).
DNSSEC is not supported. Example:
file: /var/zones/example.org
- zone: powerdns.com
file: /var/zones/powerdns.com
- ''',
- 'runtime': ['reload-zones'],
+ """,
+ "runtime": ["reload-zones"],
},
{
- 'name' : 'interval',
- 'section' : 'carbon',
- 'oldname' : 'carbon-interval',
- 'type' : LType.Uint64,
- 'default' : '30',
- 'help' : 'Number of seconds between carbon (graphite) updates',
- 'doc' : '''
+ "name": "interval",
+ "section": "carbon",
+ "oldname": "carbon-interval",
+ "type": LType.Uint64,
+ "default": "30",
+ "help": "Number of seconds between carbon (graphite) updates",
+ "doc": """
If sending carbon updates, this is the interval between them in seconds.
See :doc:`metrics`.
- ''',
+ """,
},
{
- 'name' : 'ns',
- 'section' : 'carbon',
- 'oldname' : 'carbon-namespace',
- 'type' : LType.String,
- 'default' : 'pdns',
- 'help' : 'If set overwrites the first part of the carbon string',
- 'doc' : '''
+ "name": "ns",
+ "section": "carbon",
+ "oldname": "carbon-namespace",
+ "type": LType.String,
+ "default": "pdns",
+ "help": "If set overwrites the first part of the carbon string",
+ "doc": """
Change the namespace or first string of the metric key. The default is pdns.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'ourname',
- 'section' : 'carbon',
- 'oldname' : 'carbon-ourname',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'If set, overrides our reported hostname for carbon stats',
- 'doc' : '''
+ "name": "ourname",
+ "section": "carbon",
+ "oldname": "carbon-ourname",
+ "type": LType.String,
+ "default": "",
+ "help": "If set, overrides our reported hostname for carbon stats",
+ "doc": """
If sending carbon updates, if set, this will override our hostname.
Be careful not to include any dots in this setting, unless you know what you are doing.
See :ref:`metricscarbon`.
- ''',
+ """,
},
{
- 'name' : 'instance',
- 'section' : 'carbon',
- 'oldname' : 'carbon-instance',
- 'type' : LType.String,
- 'default' : 'recursor',
- 'help' : 'If set overwrites the instance name default',
- 'doc' : '''
+ "name": "instance",
+ "section": "carbon",
+ "oldname": "carbon-instance",
+ "type": LType.String,
+ "default": "recursor",
+ "help": "If set overwrites the instance name default",
+ "doc": """
Change the instance or third string of the metric key. The default is recursor.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'server',
- 'section' : 'carbon',
- 'oldname' : 'carbon-server',
- 'type' : LType.ListSocketAddresses,
- 'default' : '',
- 'help' : 'If set, send metrics in carbon (graphite) format to this server IP address',
- 'doc' : '''
+ "name": "server",
+ "section": "carbon",
+ "oldname": "carbon-server",
+ "type": LType.ListSocketAddresses,
+ "default": "",
+ "help": "If set, send metrics in carbon (graphite) format to this server IP address",
+ "doc": """
If set to an IP or IPv6 address, will send all available metrics to this server via the carbon protocol, which is used by graphite and metronome. Moreover you can specify more than one server using a comma delimited list, ex: carbon-server=10.10.10.10,10.10.10.20.
You may specify an alternate port by appending :port, for example: ``127.0.0.1:2004``.
See :doc:`metrics`.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Will send all available metrics to these servers via the carbon protocol, which is used by graphite and metronome.
See :doc:`metrics`.
- ''',
- 'runtime': 'set-carbon-server',
+ """,
+ "runtime": "set-carbon-server",
},
{
- 'name' : 'chroot',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'switch to chroot jail',
- 'doc' : '''
+ "name": "chroot",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "switch to chroot jail",
+ "doc": """
If set, chroot to this directory for more security.
This is not recommended; instead, we recommend containing PowerDNS using operating system features.
We ship systemd unit files with our packages to make this easy.
When running on a system where systemd manages services, ``chroot`` does not work out of the box, as PowerDNS cannot use the ``NOTIFY_SOCKET``.
Either do not ``chroot`` on these systems or set the 'Type' of this service to 'simple' instead of 'notify' (refer to the systemd documentation on how to modify unit-files).
- ''',
+ """,
},
{
- 'name' : 'tcp_timeout',
- 'section' : 'incoming',
- 'oldname' : 'client-tcp-timeout',
- 'type' : LType.Uint64,
- 'default' : '2',
- 'help' : 'Timeout in seconds when talking to TCP clients',
- 'doc' : '''
+ "name": "tcp_timeout",
+ "section": "incoming",
+ "oldname": "client-tcp-timeout",
+ "type": LType.Uint64,
+ "default": "2",
+ "help": "Timeout in seconds when talking to TCP clients",
+ "doc": """
Time to wait for data from TCP clients.
- ''',
+ """,
},
{
- 'name' : 'config',
- 'section' : 'commands',
- 'type' : LType.Command,
- 'default' : 'no',
- 'help' : 'Output blank configuration. You can use --config=check to test the config file and command line arguments.',
- 'doc' : '''
-EMPTY? '''
+ "name": "config",
+ "section": "commands",
+ "type": LType.Command,
+ "default": "no",
+ "help": "Output blank configuration. You can use --config=check to test the config file and command line arguments.",
+ "doc": """
+EMPTY? """,
},
{
- 'name' : 'config_dir',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : 'SYSCONFDIR',
- 'docdefault': 'Determined by distribution',
- 'help' : 'Location of configuration directory (recursor.conf or recursor.yml)',
- 'doc' : '''
+ "name": "config_dir",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "SYSCONFDIR",
+ "docdefault": "Determined by distribution",
+ "help": "Location of configuration directory (recursor.conf or recursor.yml)",
+ "doc": """
Location of configuration directory (where ``recursor.conf`` or ``recursor.yml`` is stored).
Usually ``/etc/powerdns``, but this depends on ``SYSCONFDIR`` during compile-time.
Use default or set on command line.
- ''',
+ """,
},
{
- 'name' : 'config_name',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Name of this virtual configuration - will rename the binary image',
- 'doc' : '''
+ "name": "config_name",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "Name of this virtual configuration - will rename the binary image",
+ "doc": """
When running multiple recursors on the same server, read settings from :file:`recursor-{name}.conf`, this will also rename the binary image.
- ''',
+ """,
},
{
- 'name' : 'cpu_map',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs',
- 'doc' : '''
+ "name": "cpu_map",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs",
+ "doc": """
Set CPU affinity for threads, asking the scheduler to run those threads on a single CPU, or a set of CPUs.
This parameter accepts a space separated list of thread-id=cpu-id, or thread-id=cpu-id-1,cpu-id-2,...,cpu-id-N.
For example, to make the worker thread 0 run on CPU id 0 and the worker thread 1 on CPUs 1 and 2::
Note that, depending on the configuration, the Recursor can start more threads.
Typically these threads will sleep most of the time.
These threads cannot be specified in this setting as their thread-ids are left unspecified.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Set CPU affinity for threads, asking the scheduler to run those threads on a single CPU, or a set of CPUs.
This parameter accepts a space separated list of thread-id=cpu-id, or thread-id=cpu-id-1,cpu-id-2,...,cpu-id-N.
For example, to make the worker thread 0 run on CPU id 0 and the worker thread 1 on CPUs 1 and 2:
Note that, depending on the configuration, the Recursor can start more threads.
Typically these threads will sleep most of the time.
These threads cannot be specified in this setting as their thread-ids are left unspecified.
- ''',
+ """,
},
{
- 'name' : 'daemon',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Operate as a daemon',
- 'doc' : '''
+ "name": "daemon",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Operate as a daemon",
+ "doc": """
Operate in the background.
- ''',
- 'versionchanged': ('4.0.0', 'Default is now ``no``, was ``yes`` before.')
+ """,
+ "versionchanged": ("4.0.0", "Default is now ``no``, was ``yes`` before."),
},
{
- 'name' : 'dont_throttle_names',
- 'section' : 'outgoing',
- 'type' : LType.ListStrings,
- 'default' : '',
- 'help' : 'Do not throttle nameservers with this name or suffix',
- 'doc' : '''
+ "name": "dont_throttle_names",
+ "section": "outgoing",
+ "type": LType.ListStrings,
+ "default": "",
+ "help": "Do not throttle nameservers with this name or suffix",
+ "doc": """
When an authoritative server does not answer a query or sends a reply that the recursor does not like, it is throttled.
Any servers' name suffix-matching the supplied names will never be throttled.
.. warning::
Most servers on the internet do not respond for a good reason (overloaded or unreachable), ``dont-throttle-names`` could make this load on the upstream server even higher, resulting in further service degradation.
- ''',
- 'versionadded': '4.2.0',
- 'runtime': ['add-dont-throttle-names', 'clear-dont-throttle-names'],
+ """,
+ "versionadded": "4.2.0",
+ "runtime": ["add-dont-throttle-names", "clear-dont-throttle-names"],
},
{
- 'name' : 'dont_throttle_netmasks',
- 'section' : 'outgoing',
- 'type' : LType.ListSubnets,
- 'default' : '',
- 'help' : 'Do not throttle nameservers with this IP netmask',
- 'doc' : '''
+ "name": "dont_throttle_netmasks",
+ "section": "outgoing",
+ "type": LType.ListSubnets,
+ "default": "",
+ "help": "Do not throttle nameservers with this IP netmask",
+ "doc": """
When an authoritative server does not answer a query or sends a reply that the recursor does not like, it is throttled.
Any servers matching the supplied netmasks will never be throttled.
.. warning::
Most servers on the internet do not respond for a good reason (overloaded or unreachable), ``dont-throttle-netmasks`` could make this load on the upstream server even higher, resulting in further service degradation.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
When an authoritative server does not answer a query or sends a reply that the recursor does not like, it is throttled.
Any servers matching the supplied netmasks will never be throttled.
.. warning::
Most servers on the internet do not respond for a good reason (overloaded or unreachable), ``dont-throttle-netmasks`` could make this load on the upstream server even higher, resulting in further service degradation.
- ''',
- 'versionadded': '4.2.0',
- 'runtime': ['rec_control add-dont-throttle-netmasks', 'rec_control clear-dont-throttle-netmask'],
- },
- {
- 'name' : 'devonly_regression_test_mode',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'internal use only',
- 'doc' : 'SKIP',
- },
- {
- 'name' : 'disable',
- 'section' : 'packetcache',
- 'oldname' : 'disable-packetcache',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Disable packetcache',
- 'doc' : '''
+ """,
+ "versionadded": "4.2.0",
+ "runtime": ["rec_control add-dont-throttle-netmasks", "rec_control clear-dont-throttle-netmask"],
+ },
+ {
+ "name": "devonly_regression_test_mode",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "internal use only",
+ "doc": "SKIP",
+ },
+ {
+ "name": "disable",
+ "section": "packetcache",
+ "oldname": "disable-packetcache",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Disable packetcache",
+ "doc": """
Turn off the packet cache. Useful when running with Lua scripts that modify answers in such a way they cannot be cached, though individual answer caching can be controlled from Lua as well.
- ''',
+ """,
},
{
- 'name' : 'disable_syslog',
- 'section' : 'logging',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Disable logging to syslog, useful when running inside a supervisor that logs stderr',
- 'doc' : '''
+ "name": "disable_syslog",
+ "section": "logging",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Disable logging to syslog, useful when running inside a supervisor that logs stderr",
+ "doc": """
Do not log to syslog, only to stderr.
Use this setting when running inside a supervisor that handles logging (like systemd).
**Note**: do not use this setting in combination with :ref:`setting-daemon` as all logging will disappear.
- ''',
+ """,
},
{
- 'name' : 'distribution_load_factor',
- 'section' : 'incoming',
- 'type' : LType.Double,
- 'default' : '0.0',
- 'help' : 'The load factor used when PowerDNS is distributing queries to worker threads',
- 'doc' : '''
+ "name": "distribution_load_factor",
+ "section": "incoming",
+ "type": LType.Double,
+ "default": "0.0",
+ "help": "The load factor used when PowerDNS is distributing queries to worker threads",
+ "doc": """
If :ref:`setting-pdns-distributes-queries` is set and this setting is set to another value
than 0, the distributor thread will use a bounded load-balancing algorithm while
distributing queries to worker threads, making sure that no thread is assigned
average load. This helps making sure that all the workers have roughly the same
share of queries, even if the incoming traffic is very skewed, with a larger
number of requests asking for the same qname.
- ''',
- 'versionadded': '4.1.12'
+ """,
+ "versionadded": "4.1.12",
},
{
- 'name' : 'distribution_pipe_buffer_size',
- 'section' : 'incoming',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'Size in bytes of the internal buffer of the pipe used by the distributor to pass incoming queries to a worker thread',
- 'doc' : '''
+ "name": "distribution_pipe_buffer_size",
+ "section": "incoming",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "Size in bytes of the internal buffer of the pipe used by the distributor to pass incoming queries to a worker thread",
+ "doc": """
Size in bytes of the internal buffer of the pipe used by the distributor to pass incoming queries to a worker thread.
Requires support for `F_SETPIPE_SZ` which is present in Linux since 2.6.35. The actual size might be rounded up to
a multiple of a page size. 0 means that the OS default size is used.
A large buffer might allow the recursor to deal with very short-lived load spikes during which a worker thread gets
overloaded, but it will be at the cost of an increased latency.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'distributor_threads',
- 'section' : 'incoming',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'docdefault' : '1 if :ref:`setting-pdns-distributes-queries` is set, 0 otherwise',
- 'help' : 'Launch this number of distributor threads, distributing queries to other threads',
- 'doc' : '''
+ "name": "distributor_threads",
+ "section": "incoming",
+ "type": LType.Uint64,
+ "default": "0",
+ "docdefault": "1 if :ref:`setting-pdns-distributes-queries` is set, 0 otherwise",
+ "help": "Launch this number of distributor threads, distributing queries to other threads",
+ "doc": """
If :ref:`setting-pdns-distributes-queries` is set, spawn this number of distributor threads on startup. Distributor threads
handle incoming queries and distribute them to other threads based on a hash of the query.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'dot_to_auth_names',
- 'section' : 'outgoing',
- 'type' : LType.ListStrings,
- 'default' : '',
- 'help' : 'Use DoT to authoritative servers with these names or suffixes',
- 'doc' : '''
+ "name": "dot_to_auth_names",
+ "section": "outgoing",
+ "type": LType.ListStrings,
+ "default": "",
+ "help": "Use DoT to authoritative servers with these names or suffixes",
+ "doc": """
Force DoT to the listed authoritative nameservers. For this to work, DoT support has to be compiled in.
Currently, the certificate is not checked for validity in any way.
- ''',
- 'versionadded': '4.6.0'
+ """,
+ "versionadded": "4.6.0",
},
{
- 'name' : 'dot_to_port_853',
- 'section' : 'outgoing',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Force DoT connection to target port 853 if DoT compiled in',
- 'doc' : '''
+ "name": "dot_to_port_853",
+ "section": "outgoing",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Force DoT connection to target port 853 if DoT compiled in",
+ "doc": """
Enable DoT to forwarders that specify port 853.
- ''',
- 'versionadded': '4.6.0'
+ """,
+ "versionadded": "4.6.0",
},
{
- 'name' : 'dns64_prefix',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'DNS64 prefix',
- 'doc' : '''
+ "name": "dns64_prefix",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "DNS64 prefix",
+ "doc": """
Enable DNS64 (:rfc:`6147`) support using the supplied /96 IPv6 prefix. This will generate 'fake' ``AAAA`` records for names
with only ``A`` records, as well as 'fake' ``PTR`` records to make sure that reverse lookup of DNS64-generated IPv6 addresses
generate the right name.
See :doc:`dns64` for more flexible but slower alternatives using Lua.
- ''',
- 'versionadded': '4.4.0'
+ """,
+ "versionadded": "4.4.0",
},
{
- 'name' : 'validation',
- 'section' : 'dnssec',
- 'oldname' : 'dnssec',
- 'type' : LType.String,
- 'default' : 'process',
- 'help' : 'DNSSEC mode: off/process-no-validate/process (default)/log-fail/validate',
- 'doc' : '''
+ "name": "validation",
+ "section": "dnssec",
+ "oldname": "dnssec",
+ "type": LType.String,
+ "default": "process",
+ "help": "DNSSEC mode: off/process-no-validate/process (default)/log-fail/validate",
+ "doc": """
One of ``off``, ``process-no-validate``, ``process``, ``log-fail``, ``validate``
Set the mode for DNSSEC processing, as detailed in :doc:`dnssec`.
Similar behaviour to ``process``, but validate RRSIGs on responses and log bogus responses.
``validate``
Full blown DNSSEC validation. Send SERVFAIL to clients on bogus responses.
- ''',
- 'versionadded': '4.0.0',
- 'versionchanged': ('4.5.0',
- 'The default changed from ``process-no-validate`` to ``process``')
- },
- {
- 'name' : 'disabled_algorithms',
- 'section' : 'dnssec',
- 'oldname' : 'dnssec-disabled-algorithms',
- 'type' : LType.ListStrings,
- 'default' : '',
- 'help' : 'List of DNSSEC algorithm numbers that are considered unsupported',
- 'doc' : '''
+ """,
+ "versionadded": "4.0.0",
+ "versionchanged": ("4.5.0", "The default changed from ``process-no-validate`` to ``process``"),
+ },
+ {
+ "name": "disabled_algorithms",
+ "section": "dnssec",
+ "oldname": "dnssec-disabled-algorithms",
+ "type": LType.ListStrings,
+ "default": "",
+ "help": "List of DNSSEC algorithm numbers that are considered unsupported",
+ "doc": """
A list of DNSSEC algorithm numbers that should be considered disabled.
These algorithms will not be used to validate DNSSEC signatures.
Zones (only) signed with these algorithms will be considered ``Insecure``.
This is important on systems that have a default strict crypto policy, like RHEL9 derived systems.
On such systems not disabling some algorithms (or changing the security policy) will make affected zones to be considered ``Bogus`` as using these algorithms fails.
- ''',
- 'versionadded': '4.9.0'
+ """,
+ "versionadded": "4.9.0",
},
{
- 'name' : 'log_bogus',
- 'section' : 'dnssec',
- 'oldname' : 'dnssec-log-bogus',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Log DNSSEC bogus validations',
- 'doc' : '''
+ "name": "log_bogus",
+ "section": "dnssec",
+ "oldname": "dnssec-log-bogus",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Log DNSSEC bogus validations",
+ "doc": """
Log every DNSSEC validation failure.
**Note**: This is not logged per-query but every time records are validated as Bogus.
- ''',
- 'runtime': 'set-dnssec-log-bogus',
+ """,
+ "runtime": "set-dnssec-log-bogus",
},
{
- 'name' : 'dont_query',
- 'section' : 'outgoing',
- 'type' : LType.ListSubnets,
- 'default' : '127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10, 0.0.0.0/8, 192.0.0.0/24, 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 240.0.0.0/4, ::/96, ::ffff:0:0/96, 100::/64, 2001:db8::/32',
- 'help' : 'If set, do not query these netmasks for DNS data',
- 'doc' : '''
+ "name": "dont_query",
+ "section": "outgoing",
+ "type": LType.ListSubnets,
+ "default": "127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10, 0.0.0.0/8, 192.0.0.0/24, 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 240.0.0.0/4, ::/96, ::ffff:0:0/96, 100::/64, 2001:db8::/32",
+ "help": "If set, do not query these netmasks for DNS data",
+ "doc": """
The DNS is a public database, but sometimes contains delegations to private IP addresses, like for example 127.0.0.1.
This can have odd effects, depending on your network, and may even be a security risk.
Therefore, the PowerDNS Recursor by default does not query private space IP addresses.
This setting can be used to expand or reduce the limitations.
Queries for names in forward zones and to addresses as configured in any of the settings :ref:`setting-forward-zones`, :ref:`setting-forward-zones-file` or :ref:`setting-forward-zones-recurse` are performed regardless of these limitations. However, if NS records are learned from :ref:`setting-forward-zones` and the IP addresses of the nameservers learned in that way are included in :ref:`setting-dont-query`, lookups relying on these nameservers will fail with SERVFAIL.
- ''',
+ """,
},
{
- 'name' : 'add_for',
- 'section' : 'ecs',
- 'oldname' : 'ecs-add-for',
- 'type' : LType.ListSubnets,
- 'default' : '0.0.0.0/0, ::/0, !127.0.0.0/8, !10.0.0.0/8, !100.64.0.0/10, !169.254.0.0/16, !192.168.0.0/16, !172.16.0.0/12, !::1/128, !fc00::/7, !fe80::/10',
- 'help' : 'List of client netmasks for which EDNS Client Subnet will be added',
- 'doc' : '''
+ "name": "add_for",
+ "section": "ecs",
+ "oldname": "ecs-add-for",
+ "type": LType.ListSubnets,
+ "default": "0.0.0.0/0, ::/0, !127.0.0.0/8, !10.0.0.0/8, !100.64.0.0/10, !169.254.0.0/16, !192.168.0.0/16, !172.16.0.0/12, !::1/128, !fc00::/7, !fe80::/10",
+ "help": "List of client netmasks for which EDNS Client Subnet will be added",
+ "doc": """
List of requestor netmasks for which the requestor IP Address should be used as the :rfc:`EDNS Client Subnet <7871>` for outgoing queries. Outgoing queries for requestors that do not match this list will use the :ref:`setting-ecs-scope-zero-address` instead.
Valid incoming ECS values from :ref:`setting-use-incoming-edns-subnet` are not replaced.
Regardless of the value of this setting, ECS values are only sent for outgoing queries matching the conditions in the :ref:`setting-edns-subnet-allow-list` setting. This setting only controls the actual value being sent.
This defaults to not using the requestor address inside RFC1918 and similar 'private' IP address spaces.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'ipv4_bits',
- 'section' : 'ecs',
- 'oldname' : 'ecs-ipv4-bits',
- 'type' : LType.Uint64,
- 'default' : '24',
- 'help' : 'Number of bits of IPv4 address to pass for EDNS Client Subnet',
- 'doc' : '''
+ "name": "ipv4_bits",
+ "section": "ecs",
+ "oldname": "ecs-ipv4-bits",
+ "type": LType.Uint64,
+ "default": "24",
+ "help": "Number of bits of IPv4 address to pass for EDNS Client Subnet",
+ "doc": """
Number of bits of client IPv4 address to pass when sending EDNS Client Subnet address information.
- ''',
- 'versionadded': '4.1.0',
+ """,
+ "versionadded": "4.1.0",
},
{
- 'name' : 'ipv4_cache_bits',
- 'section' : 'ecs',
- 'oldname' : 'ecs-ipv4-cache-bits',
- 'type' : LType.Uint64,
- 'default' : '24',
- 'help' : 'Maximum number of bits of IPv4 mask to cache ECS response',
- 'doc' : '''
+ "name": "ipv4_cache_bits",
+ "section": "ecs",
+ "oldname": "ecs-ipv4-cache-bits",
+ "type": LType.Uint64,
+ "default": "24",
+ "help": "Maximum number of bits of IPv4 mask to cache ECS response",
+ "doc": """
Maximum number of bits of client IPv4 address used by the authoritative server (as indicated by the EDNS Client Subnet scope in the answer) for an answer to be inserted into the record cache. This condition applies in conjunction with ``ecs-cache-limit-ttl``.
That is, only if both the limits apply, the record will not be cached. This decision can be overridden by ``ecs-ipv4-never-cache`` and ``ecs-ipv6-never-cache``.
- ''',
- 'versionadded': '4.1.12'
+ """,
+ "versionadded": "4.1.12",
},
{
- 'name' : 'ipv6_bits',
- 'section' : 'ecs',
- 'oldname' : 'ecs-ipv6-bits',
- 'type' : LType.Uint64,
- 'default' : '56',
- 'help' : 'Number of bits of IPv6 address to pass for EDNS Client Subnet',
- 'doc' : '''
+ "name": "ipv6_bits",
+ "section": "ecs",
+ "oldname": "ecs-ipv6-bits",
+ "type": LType.Uint64,
+ "default": "56",
+ "help": "Number of bits of IPv6 address to pass for EDNS Client Subnet",
+ "doc": """
Number of bits of client IPv6 address to pass when sending EDNS Client Subnet address information.
- ''',
- 'versionadded': '4.1.0'
+ """,
+ "versionadded": "4.1.0",
},
{
- 'name' : 'ipv6_cache_bits',
- 'section' : 'ecs',
- 'oldname' : 'ecs-ipv6-cache-bits',
- 'type' : LType.Uint64,
- 'default' : '56',
- 'help' : 'Maximum number of bits of IPv6 mask to cache ECS response',
- 'doc' : '''
+ "name": "ipv6_cache_bits",
+ "section": "ecs",
+ "oldname": "ecs-ipv6-cache-bits",
+ "type": LType.Uint64,
+ "default": "56",
+ "help": "Maximum number of bits of IPv6 mask to cache ECS response",
+ "doc": """
Maximum number of bits of client IPv6 address used by the authoritative server (as indicated by the EDNS Client Subnet scope in the answer) for an answer to be inserted into the record cache. This condition applies in conjunction with ``ecs-cache-limit-ttl``.
That is, only if both the limits apply, the record will not be cached. This decision can be overridden by ``ecs-ipv4-never-cache`` and ``ecs-ipv6-never-cache``.
- ''',
- 'versionadded': '4.1.12'
+ """,
+ "versionadded": "4.1.12",
},
{
- 'name' : 'ipv4_never_cache',
- 'section' : 'ecs',
- 'oldname' : 'ecs-ipv4-never-cache',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'If we should never cache IPv4 ECS responses',
- 'doc' : '''
+ "name": "ipv4_never_cache",
+ "section": "ecs",
+ "oldname": "ecs-ipv4-never-cache",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "If we should never cache IPv4 ECS responses",
+ "doc": """
When set, never cache replies carrying EDNS IPv4 Client Subnet scope in the record cache.
In this case the decision made by ``ecs-ipv4-cache-bits`` and ``ecs-cache-limit-ttl`` is no longer relevant.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'ipv6_never_cache',
- 'section' : 'ecs',
- 'oldname' : 'ecs-ipv6-never-cache',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'If we should never cache IPv6 ECS responses',
- 'doc' : '''
+ "name": "ipv6_never_cache",
+ "section": "ecs",
+ "oldname": "ecs-ipv6-never-cache",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "If we should never cache IPv6 ECS responses",
+ "doc": """
When set, never cache replies carrying EDNS IPv6 Client Subnet scope in the record cache.
In this case the decision made by ``ecs-ipv6-cache-bits`` and ``ecs-cache-limit-ttl`` is no longer relevant.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'minimum_ttl_override',
- 'section' : 'ecs',
- 'oldname' : 'ecs-minimum-ttl-override',
- 'type' : LType.Uint64,
- 'default' : '1',
- 'help' : 'The minimum TTL for records in ECS-specific answers',
- 'doc' : '''
+ "name": "minimum_ttl_override",
+ "section": "ecs",
+ "oldname": "ecs-minimum-ttl-override",
+ "type": LType.Uint64,
+ "default": "1",
+ "help": "The minimum TTL for records in ECS-specific answers",
+ "doc": """
This setting artificially raises the TTLs of records in the ANSWER section of ECS-specific answers to be at least this long.
Setting this to a value greater than 1 technically is an RFC violation, but might improve performance a lot.
Using a value of 0 impacts performance of TTL 0 records greatly, since it forces the recursor to contact
authoritative servers every time a client requests them.
- ''',
- 'versionchanged': ('4.5.0', 'Old versions used default 0.'),
- 'runtime': 'set-ecs-minimum-ttl',
- },
- {
- 'name' : 'cache_limit_ttl',
- 'section' : 'ecs',
- 'oldname' : 'ecs-cache-limit-ttl',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'Minimum TTL to cache ECS response',
- 'doc' : '''
+ """,
+ "versionchanged": ("4.5.0", "Old versions used default 0."),
+ "runtime": "set-ecs-minimum-ttl",
+ },
+ {
+ "name": "cache_limit_ttl",
+ "section": "ecs",
+ "oldname": "ecs-cache-limit-ttl",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "Minimum TTL to cache ECS response",
+ "doc": """
The minimum TTL for an ECS-specific answer to be inserted into the record cache. This condition applies in conjunction with ``ecs-ipv4-cache-bits`` or ``ecs-ipv6-cache-bits``.
That is, only if both the limits apply, the record will not be cached. This decision can be overridden by ``ecs-ipv4-never-cache`` and ``ecs-ipv6-never-cache``.
- ''',
- 'versionadded': '4.1.12'
+ """,
+ "versionadded": "4.1.12",
},
{
- 'name' : 'scope_zero_address',
- 'section' : 'ecs',
- 'oldname' : 'ecs-scope-zero-address',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Address to send to allow-listed authoritative servers for incoming queries with ECS prefix-length source of 0',
- 'doc' : '''
+ "name": "scope_zero_address",
+ "section": "ecs",
+ "oldname": "ecs-scope-zero-address",
+ "type": LType.String,
+ "default": "",
+ "help": "Address to send to allow-listed authoritative servers for incoming queries with ECS prefix-length source of 0",
+ "doc": """
The IP address sent via EDNS Client Subnet to authoritative servers listed in
:ref:`setting-edns-subnet-allow-list` when :ref:`setting-use-incoming-edns-subnet` is set and the query has
an ECS source prefix-length set to 0.
The default is to look for the first usable (not an ``any`` one) address in
:ref:`setting-query-local-address` (starting with IPv4). If no suitable address is
found, the recursor fallbacks to sending 127.0.0.1.
- ''',
- 'versionadded': '4.1.0'
+ """,
+ "versionadded": "4.1.0",
},
{
- 'name' : 'edns_bufsize',
- 'section' : 'outgoing',
- 'oldname' : 'edns-outgoing-bufsize',
- 'type' : LType.Uint64,
- 'default' : '1232',
- 'help' : 'Outgoing EDNS buffer size',
- 'doc' : '''
+ "name": "edns_bufsize",
+ "section": "outgoing",
+ "oldname": "edns-outgoing-bufsize",
+ "type": LType.Uint64,
+ "default": "1232",
+ "help": "Outgoing EDNS buffer size",
+ "doc": """
.. note:: Why 1232?
1232 is the largest number of payload bytes that can fit in the smallest IPv6 packet.
This is the value set for the EDNS0 buffer size in outgoing packets.
Lower this if you experience timeouts.
- ''',
- 'versionchanged': ('4.2.0', 'Before 4.2.0, the default was 1680')
+ """,
+ "versionchanged": ("4.2.0", "Before 4.2.0, the default was 1680"),
},
{
- 'name' : 'edns_padding_from',
- 'section' : 'incoming',
- 'type' : LType.ListSubnets,
- 'default' : '',
- 'help' : 'List of netmasks (proxy IP in case of proxy-protocol presence, client IP otherwise) for which EDNS padding will be enabled in responses, provided that \'edns-padding-mode\' applies',
- 'doc' : '''
+ "name": "edns_padding_from",
+ "section": "incoming",
+ "type": LType.ListSubnets,
+ "default": "",
+ "help": "List of netmasks (proxy IP in case of proxy-protocol presence, client IP otherwise) for which EDNS padding will be enabled in responses, provided that 'edns-padding-mode' applies",
+ "doc": """
List of netmasks (proxy IP in case of proxy-protocol presence, client IP otherwise) for which EDNS padding will be enabled in responses, provided that :ref:`setting-edns-padding-mode` applies.
- ''',
- 'versionadded' : '4.5.0',
- 'versionchanged' : ('5.0.5', 'YAML settings only: previously this was defined as a string instead of a sequence')
- },
- {
- 'name' : 'edns_padding_mode',
- 'section' : 'incoming',
- 'type' : LType.String,
- 'default' : 'padded-queries-only',
- 'help' : 'Whether to add EDNS padding to all responses (\'always\') or only to responses for queries containing the EDNS padding option (\'padded-queries-only\', the default). In both modes, padding will only be added to responses for queries coming from \'setting-edns-padding-from\' sources',
- 'doc' : '''
+ """,
+ "versionadded": "4.5.0",
+ "versionchanged": (
+ "5.0.5",
+ "YAML settings only: previously this was defined as a string instead of a sequence",
+ ),
+ },
+ {
+ "name": "edns_padding_mode",
+ "section": "incoming",
+ "type": LType.String,
+ "default": "padded-queries-only",
+ "help": "Whether to add EDNS padding to all responses ('always') or only to responses for queries containing the EDNS padding option ('padded-queries-only', the default). In both modes, padding will only be added to responses for queries coming from 'setting-edns-padding-from' sources",
+ "doc": """
One of ``always``, ``padded-queries-only``.
Whether to add EDNS padding to all responses (``always``) or only to responses for queries containing the EDNS padding option (``padded-queries-only``, the default).
In both modes, padding will only be added to responses for queries coming from :ref:`setting-edns-padding-from` sources.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'edns_padding',
- 'section' : 'outgoing',
- 'oldname' : 'edns-padding-out',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Whether to add EDNS padding to outgoing DoT messages',
- 'doc' : '''
+ "name": "edns_padding",
+ "section": "outgoing",
+ "oldname": "edns-padding-out",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Whether to add EDNS padding to outgoing DoT messages",
+ "doc": """
Whether to add EDNS padding to outgoing DoT queries.
- ''',
- 'versionadded': '4.8.0'
+ """,
+ "versionadded": "4.8.0",
},
{
- 'name' : 'edns_padding_tag',
- 'section' : 'incoming',
- 'type' : LType.Uint64,
- 'default' : '7830',
- 'help' : 'Packetcache tag associated to responses sent with EDNS padding, to prevent sending these to clients for which padding is not enabled.',
- 'doc' : '''
+ "name": "edns_padding_tag",
+ "section": "incoming",
+ "type": LType.Uint64,
+ "default": "7830",
+ "help": "Packetcache tag associated to responses sent with EDNS padding, to prevent sending these to clients for which padding is not enabled.",
+ "doc": """
The packetcache tag to use for padded responses, to prevent a client not allowed by the :ref:`setting-edns-padding-from` list to be served a cached answer generated for an allowed one. This
effectively divides the packet cache in two when :ref:`setting-edns-padding-from` is used. Note that this will not override a tag set from one of the ``Lua`` hooks.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'edns_subnet_allow_list',
- 'section' : 'outgoing',
- 'type' : LType.ListStrings,
- 'default' : '',
- 'help' : 'List of netmasks and domains that we should enable EDNS subnet for',
- 'doc' : '''
+ "name": "edns_subnet_allow_list",
+ "section": "outgoing",
+ "type": LType.ListStrings,
+ "default": "",
+ "help": "List of netmasks and domains that we should enable EDNS subnet for",
+ "doc": """
List of netmasks and domains that :rfc:`EDNS Client Subnet <7871>` should be enabled for in outgoing queries.
For example, an EDNS Client Subnet option containing the address of the initial requestor (but see :ref:`setting-ecs-add-for`) will be added to an outgoing query sent to server 192.0.2.1 for domain X if 192.0.2.1 matches one of the supplied netmasks, or if X matches one of the supplied domains.
Note that this setting describes the destination of outgoing queries, not the sources of incoming queries, nor the subnets described in the EDNS Client Subnet option.
By default, this option is empty, meaning no EDNS Client Subnet information is sent.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'edns_subnet_harden',
- 'section' : 'outgoing',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Do more strict checking or EDNS Client Subnet information returned by authoritative servers',
- 'doc' : '''
+ "name": "edns_subnet_harden",
+ "section": "outgoing",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Do more strict checking or EDNS Client Subnet information returned by authoritative servers",
+ "doc": """
Do more strict checking or EDNS Client Subnet information returned by authoritative servers.
Answers missing ECS information will be ignored and followed up by an ECS-less query.
- ''',
- 'versionadded': ['5.2.4', '5.1.6', '5.0.12']
+ """,
+ "versionadded": ["5.2.4", "5.1.6", "5.0.12"],
},
{
- 'name' : 'enable_old_settings',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Enable (deprecated) parsing of old-style settings',
- 'doc' : '''
+ "name": "enable_old_settings",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Enable (deprecated) parsing of old-style settings",
+ "doc": """
Enable the deprecated parsing of old-style settings.
Only makes sense to set on the command line.
- ''',
- 'skip-yaml': True,
- 'versionadded': '5.2.0',
- },
- {
- 'name' : 'entropy_source',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '/dev/urandom',
- 'help' : '',
- 'doc' : '''
- ''',
- 'skip-yaml': True,
- 'versionchanged': ('4.9.0', 'This setting is no longer used.'),
- },
- {
- 'name' : 'etc_hosts_file',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '/etc/hosts',
- 'help' : 'Path to \'hosts\' file',
- 'doc' : '''
+ """,
+ "skip-yaml": True,
+ "versionadded": "5.2.0",
+ },
+ {
+ "name": "entropy_source",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "/dev/urandom",
+ "help": "",
+ "doc": """
+ """,
+ "skip-yaml": True,
+ "versionchanged": ("4.9.0", "This setting is no longer used."),
+ },
+ {
+ "name": "etc_hosts_file",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "/etc/hosts",
+ "help": "Path to 'hosts' file",
+ "doc": """
The path to the /etc/hosts file, or equivalent.
This file can be used to serve data authoritatively using :ref:`setting-export-etc-hosts`.
- ''',
+ """,
},
{
- 'name' : 'event_trace_enabled',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'If set, event traces are collected and send out via protobuf logging (1), logfile (2), opentelemetry trace data (4) or a combination',
- 'doc' : '''
+ "name": "event_trace_enabled",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "If set, event traces are collected and send out via protobuf logging (1), logfile (2), opentelemetry trace data (4) or a combination",
+ "doc": """
Enable the recording and logging of ref:`event traces`. This is an experimental feature and subject to change.
Possible values are 0: (disabled), 1 (add information to protobuf logging messages), 2 (write to log), 4 (output OpenTelemetry Trace data in protobuf logging messages, since version 5.3.0). Values can be added to get multiple types of logging simultaneously.
For example, 6 means: write to log and output OpenTelemetry Trace data in the protobuf stream.
- ''',
- 'versionadded': '4.6.0',
- 'versionchanged': ('5.3.0', 'A value to generate OpenTelemetry Trace data was added'),
- 'runtime': 'set-event-trace-enabled',
- },
- {
- 'name' : 'export_etc_hosts',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'If we should serve up contents from /etc/hosts',
- 'doc' : '''
+ """,
+ "versionadded": "4.6.0",
+ "versionchanged": ("5.3.0", "A value to generate OpenTelemetry Trace data was added"),
+ "runtime": "set-event-trace-enabled",
+ },
+ {
+ "name": "export_etc_hosts",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "If we should serve up contents from /etc/hosts",
+ "doc": """
If set, this flag will export the host names and IP addresses mentioned in ``/etc/hosts``.
- ''',
+ """,
},
{
- 'name' : 'export_etc_hosts_search_suffix',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Also serve up the contents of /etc/hosts with this suffix',
- 'doc' : '''
+ "name": "export_etc_hosts_search_suffix",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "Also serve up the contents of /etc/hosts with this suffix",
+ "doc": """
If set, all hostnames in the :ref:`setting-export-etc-hosts` file are loaded in canonical form, based on this suffix, unless the name contains a '.', in which case the name is unchanged.
So an entry called 'pc' with ``export-etc-hosts-search-suffix='home.com'`` will lead to the generation of 'pc.home.com' within the recursor.
An entry called 'server1.home' will be stored as 'server1.home', regardless of this setting.
- ''',
+ """,
},
{
- 'name' : 'extended_resolution_errors',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'If set, send an EDNS Extended Error extension on resolution failures, like DNSSEC validation errors',
- 'doc' : '''
+ "name": "extended_resolution_errors",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "If set, send an EDNS Extended Error extension on resolution failures, like DNSSEC validation errors",
+ "doc": """
If set, the recursor will add an EDNS Extended Error (:rfc:`8914`) to responses when resolution failed, like DNSSEC validation errors, explaining the reason it failed. This setting is not needed to allow setting custom error codes from Lua or from a RPZ hit.
- ''',
- 'versionadded': '4.5.0',
- 'versionchanged': ('5.0.0', 'Default changed to enabled, previously it was disabled.'),
+ """,
+ "versionadded": "4.5.0",
+ "versionchanged": ("5.0.0", "Default changed to enabled, previously it was disabled."),
},
{
- 'name' : 'forward_zones',
- 'section' : 'recursor',
- 'type' : LType.ListForwardZones,
- 'default' : '',
- 'help' : 'Zones for which we forward queries, comma separated domain=ip pairs',
- 'doc' : '''
+ "name": "forward_zones",
+ "section": "recursor",
+ "type": LType.ListForwardZones,
+ "default": "",
+ "help": "Zones for which we forward queries, comma separated domain=ip pairs",
+ "doc": """
Queries for zones listed here will be forwarded to the IP address listed. i.e.
.. code-block:: none
To prevent this, add a Negative Trust Anchor (NTA) for this zone in the :ref:`setting-lua-config-file` with ``addNTA('your.zone', 'A comment')``.
If this forwarded zone is signed, instead of adding NTA, add the DS record to the :ref:`setting-lua-config-file`.
See the :doc:`dnssec` information.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Queries for zones listed here will be forwarded to the IP address listed. i.e.
.. code-block:: yaml
.. note::
When an ``NS`` record for a subzone is learned and the IP address for that nameserver is included in the IP ranges in :ref:`setting-dont-query`, SERVFAIL is returned.
- ''',
- 'versionchanged' : ('5.2.0', 'Zones having ``notify_allowed`` set will be added to :ref:`setting-yaml-incoming.allow_notify_for`.'),
- 'runtime': ['reload-zones'],
- },
- {
- 'name' : 'forward_zones_file',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'File with (+)domain=ip pairs for forwarding',
- 'doc' : '''
+ """,
+ "versionchanged": (
+ "5.2.0",
+ "Zones having ``notify_allowed`` set will be added to :ref:`setting-yaml-incoming.allow_notify_for`.",
+ ),
+ "runtime": ["reload-zones"],
+ },
+ {
+ "name": "forward_zones_file",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "File with (+)domain=ip pairs for forwarding",
+ "doc": """
Same as :ref:`setting-forward-zones`, parsed from a file. Only 1 zone is allowed per line, specified as follows:
.. code-block:: none
:ref:`setting-forward-zones`.
The DNSSEC notes from :ref:`setting-forward-zones` apply here as well.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Same as :ref:`setting-forward-zones`, parsed from a file as a sequence of `Forward Zone`_.
The filename MUST end in ``.yml`` for the content to be parsed as YAML.
notify_allowed: true
The DNSSEC notes from :ref:`setting-forward-zones` apply here as well.
- ''',
- 'versionchanged': [('4.0.0', '(Old style settings only) Comments are allowed, everything behind ``#`` is ignored.'),
- ('4.6.0', '(Old style settings only) Zones prefixed with a ``^`` are added to the :ref:`setting-allow-notify-for` list. Both prefix characters can be used if desired, in any order.')],
- 'runtime': ['reload-zones'],
- },
- {
- 'name' : 'forward_zones_recurse',
- 'section' : 'recursor',
- 'type' : LType.ListForwardZones,
- 'default' : '',
- 'help' : 'Zones for which we forward queries with recursion bit, comma separated domain=ip pairs',
- 'doc' : '''
+ """,
+ "versionchanged": [
+ ("4.0.0", "(Old style settings only) Comments are allowed, everything behind ``#`` is ignored."),
+ (
+ "4.6.0",
+ "(Old style settings only) Zones prefixed with a ``^`` are added to the :ref:`setting-allow-notify-for` list. Both prefix characters can be used if desired, in any order.",
+ ),
+ ],
+ "runtime": ["reload-zones"],
+ },
+ {
+ "name": "forward_zones_recurse",
+ "section": "recursor",
+ "type": LType.ListForwardZones,
+ "default": "",
+ "help": "Zones for which we forward queries with recursion bit, comma separated domain=ip pairs",
+ "doc": """
Like regular :ref:`setting-forward-zones`, but forwarded queries have the ``recursion desired (RD)`` bit set to ``1``, meaning that this setting is intended to forward queries to other recursive resolvers.
In contrast to regular forwarding, the rule that delegations of the forwarded subzones are respected is not active.
This is because we rely on the forwarder to resolve the query fully.
See :ref:`setting-forward-zones` for additional options (such as supplying multiple recursive servers) and an important note about DNSSEC.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Like regular :ref:`setting-forward-zones`, but forwarded queries have the ``recursion desired (RD)`` bit set to ``1``, meaning that this setting is intended to forward queries to other recursive resolvers.
In contrast to regular forwarding, the rule that delegations of the forwarded subzones are respected is not active.
This is because we rely on the forwarder to resolve the query fully.
The `recurse` field of a `Forward Zone`_ is fixed to ``true`` in the context of :ref:`setting-yaml-recursor.forward_zones_recurse`.
See :ref:`setting-forward-zones` for additional options (such as supplying multiple recursive servers) and an important note about DNSSEC.
- ''',
- 'runtime': ['reload-zones'],
+ """,
+ "runtime": ["reload-zones"],
},
{
- 'name' : 'gettag_needs_edns_options',
- 'section' : 'incoming',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'If EDNS Options should be extracted before calling the gettag() hook',
- 'doc' : '''
+ "name": "gettag_needs_edns_options",
+ "section": "incoming",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "If EDNS Options should be extracted before calling the gettag() hook",
+ "doc": """
If set, EDNS options in incoming queries are extracted and passed to the :func:`gettag` hook in the ``ednsoptions`` table.
- ''',
- 'versionadded': '4.1.0'
+ """,
+ "versionadded": "4.1.0",
},
{
- 'name' : 'help',
- 'section' : 'commands',
- 'type' : LType.Command,
- 'default' : 'no',
- 'help' : 'Provide a helpful message',
- 'doc' : '''
-EMPTY? '''
+ "name": "help",
+ "section": "commands",
+ "type": LType.Command,
+ "default": "no",
+ "help": "Provide a helpful message",
+ "doc": """
+EMPTY? """,
},
{
- 'name' : 'hint_file',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'If set, load root hints from this file',
- 'doc' : '''
+ "name": "hint_file",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "If set, load root hints from this file",
+ "doc": """
If set, the root-hints are read from this file. If empty, the default built-in root hints are used.
In some special cases, processing the root hints is not needed, for example when forwarding all queries to another recursor.
For these special cases, it is possible to disable the processing of root hints by setting the value to ``no`` or ``no-refresh``.
See :ref:`handling-of-root-hints` for more information on root hints handling.
- ''',
- 'versionchanged': [('4.6.2', 'Introduced the value ``no`` to disable root-hints processing.'),
- ('4.9.0', 'Introduced the value ``no-refresh`` to disable both root-hints processing and periodic refresh of the cached root `NS` records.')]
- },
- {
- 'name' : 'ignore_unknown_settings',
- 'section' : 'recursor',
- 'type' : LType.ListStrings,
- 'default' : '',
- 'help' : 'Configuration settings to ignore if they are unknown',
- 'doc' : '''
+ """,
+ "versionchanged": [
+ ("4.6.2", "Introduced the value ``no`` to disable root-hints processing."),
+ (
+ "4.9.0",
+ "Introduced the value ``no-refresh`` to disable both root-hints processing and periodic refresh of the cached root `NS` records.",
+ ),
+ ],
+ },
+ {
+ "name": "ignore_unknown_settings",
+ "section": "recursor",
+ "type": LType.ListStrings,
+ "default": "",
+ "help": "Configuration settings to ignore if they are unknown",
+ "doc": """
Names of settings to be ignored while parsing configuration files, if the setting
name is unknown to PowerDNS.
Useful during upgrade testing.
- ''',
+ """,
},
{
- 'name' : 'include_dir',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Include settings files from this directory.',
- 'doc' : '''
+ "name": "include_dir",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "Include settings files from this directory.",
+ "doc": """
Directory to scan for additional config files. All files that end with ``.conf`` are loaded in order using ``POSIX`` as locale.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Directory to scan for additional config files. All files that end with ``.yml`` are loaded in order using ``POSIX`` as locale.
- ''',
+ """,
},
{
- 'name' : 'latency_statistic_size',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '10000',
- 'help' : 'Number of latency values to calculate the qa-latency average',
- 'doc' : '''
+ "name": "latency_statistic_size",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "10000",
+ "help": "Number of latency values to calculate the qa-latency average",
+ "doc": """
Indication of how many queries will be averaged to get the average latency reported by the 'qa-latency' metric.
- ''',
+ """,
},
{
- 'name' : 'listen',
- 'section' : 'incoming',
- 'oldname' : 'local-address',
- 'type' : LType.ListSocketAddresses,
- 'default' : '127.0.0.1, ::1',
- 'help' : 'IP addresses to listen on, separated by spaces or commas. Also accepts ports.',
- 'versionchanged': ('5.3.0', '::1 was added to the list'),
- 'doc' : '''
+ "name": "listen",
+ "section": "incoming",
+ "oldname": "local-address",
+ "type": LType.ListSocketAddresses,
+ "default": "127.0.0.1, ::1",
+ "help": "IP addresses to listen on, separated by spaces or commas. Also accepts ports.",
+ "versionchanged": ("5.3.0", "::1 was added to the list"),
+ "doc": """
Local IP addresses to which we bind. Each address specified can
include a port number; if no port is included then the
:ref:`setting-local-port` port will be used for that address. If a
local-address=0.0.0.0:5353
local-address=[::]:8053
local-address=127.0.0.1:53, [::1]:5353
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
Local IP addresses to which we bind. Each address specified can
include a port number; if no port is included then the
:ref:`setting-local-port` port will be used for that address. If a
- 127.0.0.1
- '[::1]:5353'
- '::'
- ''',
+ """,
},
{
- 'name' : 'port',
- 'section' : 'incoming',
- 'oldname' : 'local-port',
- 'type' : LType.Uint64,
- 'default' : '53',
- 'help' : 'port to listen on',
- 'doc' : '''
+ "name": "port",
+ "section": "incoming",
+ "oldname": "local-port",
+ "type": LType.Uint64,
+ "default": "53",
+ "help": "port to listen on",
+ "doc": """
Local port to bind to.
If an address in :ref:`setting-local-address` does not have an explicit port, this port is used.
- ''',
+ """,
},
{
- 'name' : 'timestamp',
- 'section' : 'logging',
- 'oldname' : 'log-timestamp',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Print timestamps in log lines, useful to disable when running with a tool that timestamps stderr already',
- 'doc' : '''
+ "name": "timestamp",
+ "section": "logging",
+ "oldname": "log-timestamp",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Print timestamps in log lines, useful to disable when running with a tool that timestamps stderr already",
+ "doc": """
- ''',
+ """,
},
{
- 'name' : 'non_local_bind',
- 'section' : 'incoming',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Enable binding to non-local addresses by using FREEBIND / BINDANY socket options',
- 'doc' : '''
+ "name": "non_local_bind",
+ "section": "incoming",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options",
+ "doc": """
Bind to addresses even if one or more of the :ref:`setting-local-address`'s do not exist on this server.
Setting this option will enable the needed socket options to allow binding to non-local addresses.
This feature is intended to facilitate ip-failover setups, but it may also mask configuration issues and for this reason it is disabled by default.
- ''',
+ """,
},
{
- 'name' : 'loglevel',
- 'section' : 'logging',
- 'type' : LType.Uint64,
- 'default' : '6',
- 'help' : 'Amount of logging. Higher is more. Do not set below 3',
- 'doc' : '''
+ "name": "loglevel",
+ "section": "logging",
+ "type": LType.Uint64,
+ "default": "6",
+ "help": "Amount of logging. Higher is more. Do not set below 3",
+ "doc": """
Amount of logging. The higher the number, the more lines logged.
Corresponds to ``syslog`` level values (e.g. 0 = ``emergency``, 1 = ``alert``, 2 = ``critical``, 3 = ``error``, 4 = ``warning``, 5 = ``notice``, 6 = ``info``, 7 = ``debug``).
Each level includes itself plus the lower levels before it.
Not recommended to set this below 3.
If :ref:`setting-quiet` is ``no/false``, :ref:`setting-loglevel` will be minimally set to ``6 (info)``.
- ''',
- 'versionchanged': ('5.0.0', 'Previous version would not allow setting a level below ``3 (error)``.')
+ """,
+ "versionchanged": ("5.0.0", "Previous version would not allow setting a level below ``3 (error)``."),
},
{
- 'name' : 'common_errors',
- 'section' : 'logging',
- 'oldname' : 'log-common-errors',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'If we should log rather common errors',
- 'doc' : '''
+ "name": "common_errors",
+ "section": "logging",
+ "oldname": "log-common-errors",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "If we should log rather common errors",
+ "doc": """
Some DNS errors occur rather frequently and are no cause for alarm.
- ''',
+ """,
},
{
- 'name' : 'rpz_changes',
- 'section' : 'logging',
- 'oldname' : 'log-rpz-changes',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Log additions and removals to RPZ zones at Info level',
- 'doc' : '''
+ "name": "rpz_changes",
+ "section": "logging",
+ "oldname": "log-rpz-changes",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Log additions and removals to RPZ zones at Info level",
+ "doc": """
Log additions and removals to RPZ zones at Info (6) level instead of Debug (7).
- ''',
- 'versionadded': '4.1.0'
+ """,
+ "versionadded": "4.1.0",
},
{
- 'name' : 'facility',
- 'section' : 'logging',
- 'oldname' : 'logging-facility',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Facility to log messages as. 0 corresponds to local0',
- 'doc' : '''
+ "name": "facility",
+ "section": "logging",
+ "oldname": "logging-facility",
+ "type": LType.String,
+ "default": "",
+ "help": "Facility to log messages as. 0 corresponds to local0",
+ "doc": """
If set to a digit, logging is performed under this LOCAL facility.
See :ref:`logging`.
Do not pass names like 'local0'!
- ''',
+ """,
},
{
- 'name' : 'lowercase',
- 'section' : 'outgoing',
- 'oldname' : 'lowercase-outgoing',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Force outgoing questions to lowercase',
- 'doc' : '''
+ "name": "lowercase",
+ "section": "outgoing",
+ "oldname": "lowercase-outgoing",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Force outgoing questions to lowercase",
+ "doc": """
Set to true to lowercase the outgoing queries.
When set to 'no' (the default) a query from a client using mixed case in the DNS labels (such as a user entering mixed-case names or `draft-vixie-dnsext-dns0x20-00 <http://tools.ietf.org/html/draft-vixie-dnsext-dns0x20-00>`_), PowerDNS preserves the case of the query.
Broken authoritative servers might give a wrong or broken answer on this encoding.
Setting ``lowercase-outgoing`` to 'yes' makes the PowerDNS Recursor lowercase all the labels in the query to the authoritative servers, but still return the proper case to the client requesting.
- ''',
+ """,
},
{
- 'name' : 'lua_config_file',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'More powerful configuration options',
- 'doc' : '''
+ "name": "lua_config_file",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "More powerful configuration options",
+ "doc": """
If set, and Lua support is compiled in, this will load an additional configuration file for newer features and more complicated setups.
See :doc:`lua-config/index` for the options that can be set in this file.
- ''',
+ """,
},
{
- 'name' : 'lua_global_include_dir',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'More powerful configuration options',
- 'doc' : '''
+ "name": "lua_global_include_dir",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "More powerful configuration options",
+ "doc": """
When creating a Lua context, all ``*.lua`` files in the directory are loaded into the Lua context.
- ''',
+ """,
},
{
- 'name' : 'lua_dns_script',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Filename containing an optional Lua script that will be used to modify dns answers',
- 'doc' : '''
+ "name": "lua_dns_script",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "Filename containing an optional Lua script that will be used to modify dns answers",
+ "doc": """
Path to a lua file to manipulate the Recursor's answers. See :doc:`lua-scripting/index` for more information.
- ''',
+ """,
},
{
- 'name' : 'lua_maintenance_interval',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '1',
- 'help' : 'Number of seconds between calls to the lua user defined maintenance() function',
- 'doc' : '''
+ "name": "lua_maintenance_interval",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "1",
+ "help": "Number of seconds between calls to the lua user defined maintenance() function",
+ "doc": """
The interval between calls to the Lua user defined `maintenance()` function in seconds.
See :ref:`hooks-maintenance-callback`
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'max_busy_dot_probes',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'Maximum number of concurrent DoT probes',
- 'doc' : '''
+ "name": "max_busy_dot_probes",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "Maximum number of concurrent DoT probes",
+ "doc": """
Limit the maximum number of simultaneous DoT probes the Recursor will schedule.
The default value 0 means no DoT probes are scheduled.
.. note::
DoT probing is an experimental feature.
Please test thoroughly to determine if it is suitable in your specific production environment before enabling.
- ''',
- 'versionadded': '4.7.0'
+ """,
+ "versionadded": "4.7.0",
},
{
- 'name' : 'max_cache_bogus_ttl',
- 'section' : 'recordcache',
- 'type' : LType.Uint64,
- 'default' : '3600',
- 'help' : 'maximum number of seconds to keep a Bogus (positive or negative) cached entry in memory',
- 'doc' : '''
+ "name": "max_cache_bogus_ttl",
+ "section": "recordcache",
+ "type": LType.Uint64,
+ "default": "3600",
+ "help": "maximum number of seconds to keep a Bogus (positive or negative) cached entry in memory",
+ "doc": """
Maximum number of seconds to cache an item in the DNS cache (negative or positive) if its DNSSEC validation failed, no matter what the original TTL specified, to reduce the impact of a broken domain.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'max_entries',
- 'section' : 'recordcache',
- 'oldname' : 'max-cache-entries',
- 'type' : LType.Uint64,
- 'default' : '1000000',
- 'help' : 'If set, maximum number of entries in the main cache',
- 'doc' : '''
+ "name": "max_entries",
+ "section": "recordcache",
+ "oldname": "max-cache-entries",
+ "type": LType.Uint64,
+ "default": "1000000",
+ "help": "If set, maximum number of entries in the main cache",
+ "doc": """
Maximum number of DNS record cache entries, shared by all threads since 4.4.0.
Each entry associates a name and type with a record set.
The size of the negative cache is 10% of this number.
- ''',
- 'runtime': 'set-max-cache-entries',
+ """,
+ "runtime": "set-max-cache-entries",
},
{
- 'name' : 'max_ttl',
- 'section' : 'recordcache',
- 'oldname' : 'max-cache-ttl',
- 'type' : LType.Uint64,
- 'default' : '86400',
- 'help' : 'maximum number of seconds to keep a cached entry in memory',
- 'doc' : '''
+ "name": "max_ttl",
+ "section": "recordcache",
+ "oldname": "max-cache-ttl",
+ "type": LType.Uint64,
+ "default": "86400",
+ "help": "maximum number of seconds to keep a cached entry in memory",
+ "doc": """
Maximum number of seconds to cache an item in the DNS cache, no matter what the original TTL specified.
This value also controls the refresh period of cached root data.
See :ref:`handling-of-root-hints` for more information on this.
- ''',
- 'versionchanged': ('4.1.0', 'The minimum value of this setting is 15. i.e. setting this to lower than 15 will make this value 15.')
- },
- {
- 'name' : 'max_entry_size',
- 'section' : 'recordcache',
- 'oldname': 'max-recordcache-entry-size',
- 'type' : LType.Uint64,
- 'default' : '8192',
- 'help' : 'maximum storage size of a recordset stored in record cache',
- 'doc' : '''
+ """,
+ "versionchanged": (
+ "4.1.0",
+ "The minimum value of this setting is 15. i.e. setting this to lower than 15 will make this value 15.",
+ ),
+ },
+ {
+ "name": "max_entry_size",
+ "section": "recordcache",
+ "oldname": "max-recordcache-entry-size",
+ "type": LType.Uint64,
+ "default": "8192",
+ "help": "maximum storage size of a recordset stored in record cache",
+ "doc": """
Maximum size of storage used by a single record cache entry. Entries larger than this number will not be stored.
Zero means no limit.
-''',
- 'versionadded': ['5.1.10', '5.2.8', '5.3.5'],
+""",
+ "versionadded": ["5.1.10", "5.2.8", "5.3.5"],
},
{
- 'name' : 'max_concurrent_requests_per_tcp_connection',
- 'section' : 'incoming',
- 'type' : LType.Uint64,
- 'default' : '10',
- 'help' : 'Maximum number of requests handled concurrently per TCP connection',
- 'doc' : '''
+ "name": "max_concurrent_requests_per_tcp_connection",
+ "section": "incoming",
+ "type": LType.Uint64,
+ "default": "10",
+ "help": "Maximum number of requests handled concurrently per TCP connection",
+ "doc": """
Maximum number of incoming requests handled concurrently per tcp
connection. This number must be larger than 0 and smaller than 65536
and also smaller than `max-mthreads`.
- ''',
- 'versionadded': '4.3.0'
+ """,
+ "versionadded": "4.3.0",
},
{
- 'name': 'max_chain_length',
- 'section': 'recursor',
- 'type': LType.Uint64,
- 'default': '0',
- 'help': 'maximum number of queries that can be chained to an outgoing request, 0 is no limit',
- 'doc': '''
+ "name": "max_chain_length",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "maximum number of queries that can be chained to an outgoing request, 0 is no limit",
+ "doc": """
The maximum number of queries that can be attached to an outgoing request chain. Attaching requests to a chain
saves on outgoing queries, but the processing of a chain when the reply to the outgoing query comes in
might result in a large outgoing traffic spike. Reducing the maximum chain length mitigates this.
If this value is zero, no maximum is enforced, though the maximum number of mthreads (:ref:`setting-max-mthreads`)
also limits the chain length.
-''',
- 'versionadded': '5.1.0'
+""",
+ "versionadded": "5.1.0",
},
{
- 'name' : 'max_include_depth',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '20',
- 'help' : 'Maximum nested $INCLUDE depth when loading a zone from a file',
- 'doc' : '''
+ "name": "max_include_depth",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "20",
+ "help": "Maximum nested $INCLUDE depth when loading a zone from a file",
+ "doc": """
Maximum number of nested ``$INCLUDE`` directives while processing a zone file.
Zero mean no ``$INCLUDE`` directives will be accepted.
- ''',
- 'versionadded': '4.6.0'
+ """,
+ "versionadded": "4.6.0",
},
{
- 'name' : 'max_generate_steps',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'Maximum number of $GENERATE steps when loading a zone from a file',
- 'doc' : '''
+ "name": "max_generate_steps",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "Maximum number of $GENERATE steps when loading a zone from a file",
+ "doc": """
Maximum number of steps for a '$GENERATE' directive when parsing a
zone file. This is a protection measure to prevent consuming a lot of
CPU and memory when untrusted zones are loaded. Default to 0 which
means unlimited.
- ''',
- 'versionadded': '4.3.0'
+ """,
+ "versionadded": "4.3.0",
},
{
- 'name' : 'max_mthreads',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '2048',
- 'help' : 'Maximum number of simultaneous Mtasker threads',
- 'doc' : '''
+ "name": "max_mthreads",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "2048",
+ "help": "Maximum number of simultaneous Mtasker threads",
+ "doc": """
Maximum number of simultaneous MTasker threads, per worker thread.
- ''',
+ """,
},
{
- 'name' : 'max_entries',
- 'section' : 'packetcache',
- 'oldname' : 'max-packetcache-entries',
- 'type' : LType.Uint64,
- 'default' : '500000',
- 'help' : 'maximum number of entries to keep in the packetcache',
- 'doc' : '''
+ "name": "max_entries",
+ "section": "packetcache",
+ "oldname": "max-packetcache-entries",
+ "type": LType.Uint64,
+ "default": "500000",
+ "help": "maximum number of entries to keep in the packetcache",
+ "doc": """
Maximum number of Packet Cache entries. Sharded and shared by all threads since 4.9.0.
-''',
- 'runtime': 'set-max-packetcache-entries',
+""",
+ "runtime": "set-max-packetcache-entries",
},
{
- 'name' : 'max_entry_size',
- 'section' : 'packetcache',
- 'oldname' : 'max-packetcache-entry-size',
- 'type' : LType.Uint64,
- 'default' : '8192',
- 'help' : 'maximum size of a packet stored in the the packet cache',
- 'doc' : '''
+ "name": "max_entry_size",
+ "section": "packetcache",
+ "oldname": "max-packetcache-entry-size",
+ "type": LType.Uint64,
+ "default": "8192",
+ "help": "maximum size of a packet stored in the the packet cache",
+ "doc": """
Maximum size of packets stored in the packet cache. Packets larger than this number will not be stored.
Zero means no limit.
-''',
- 'versionadded': ['5.1.10', '5.2.8', '5.3.5'],
+""",
+ "versionadded": ["5.1.10", "5.2.8", "5.3.5"],
},
{
- 'name' : 'max_qperq',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '50',
- 'help' : 'Maximum outgoing queries per client query',
- 'doc' : '''
+ "name": "max_qperq",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "50",
+ "help": "Maximum outgoing queries per client query",
+ "doc": """
The maximum number of outgoing queries that will be sent out during the resolution of a single client query.
This is used to avoid cycles resolving names.
- ''',
- 'versionchanged': ('5.1.0', 'The default used to be 60, with an extra allowance if qname minimization was enabled. Having better algorithms allows for a lower default limit.'),
- },
- {
- 'name' : 'max_bytesperq',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '100000',
- 'help' : 'Maximum number of received bytes per client query',
- 'doc' : '''
+ """,
+ "versionchanged": (
+ "5.1.0",
+ "The default used to be 60, with an extra allowance if qname minimization was enabled. Having better algorithms allows for a lower default limit.",
+ ),
+ },
+ {
+ "name": "max_bytesperq",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "100000",
+ "help": "Maximum number of received bytes per client query",
+ "doc": """
The maximum number of cumulative bytes that will be accepted during the resolution of a single client query.
This is useful to limit amplification attacks.
- ''',
- 'versionadded': ['5.1.10', '5.2.8', '5.3.5'],
+ """,
+ "versionadded": ["5.1.10", "5.2.8", "5.3.5"],
},
{
- 'name' : 'max_cnames_followed',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '10',
- 'help' : 'Maximum number CNAME records followed',
- 'doc' : '''
+ "name": "max_cnames_followed",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "10",
+ "help": "Maximum number CNAME records followed",
+ "doc": """
Maximum length of a CNAME chain. If a CNAME chain exceeds this length, a ``ServFail`` answer will be returned.
Previously, this limit was fixed at 10.
- ''',
- 'versionadded': '5.1.0'
+ """,
+ "versionadded": "5.1.0",
},
{
- 'name' : 'limit_qtype_any',
- 'section' : 'recordcache',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Limit answers to ANY queries in size',
- 'doc' : '''
+ "name": "limit_qtype_any",
+ "section": "recordcache",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Limit answers to ANY queries in size",
+ "doc": """
Limit answers to ANY queries constructed from the record cache in size.
Trying to retrieve more than :ref:`setting-max-rrset-size` records will result in a ``ServFail``',
- ''',
- 'versionadded': ['4.9.9', '5.0.9', '5.1.2']
+ """,
+ "versionadded": ["4.9.9", "5.0.9", "5.1.2"],
},
{
- 'name' : 'max_rrset_size',
- 'section' : 'recordcache',
- 'type' : LType.Uint64,
- 'default' : '256',
- 'help' : 'Maximum size of RRSet in cache',
- 'doc' : '''
+ "name": "max_rrset_size",
+ "section": "recordcache",
+ "type": LType.Uint64,
+ "default": "256",
+ "help": "Maximum size of RRSet in cache",
+ "doc": """
Maximum size of RRSets in cache.
Trying to retrieve larger RRSets will result in a ``ServFail``.',
- ''',
- 'versionadded': ['4.9.9', '5.0.9', '5.1.2']
+ """,
+ "versionadded": ["4.9.9", "5.0.9", "5.1.2"],
},
{
- 'name' : 'max_ns_address_qperq',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '10',
- 'help' : 'Maximum outgoing NS address queries per query',
- 'doc' : '''
+ "name": "max_ns_address_qperq",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "10",
+ "help": "Maximum outgoing NS address queries per query",
+ "doc": """
The maximum number of outgoing queries with empty replies for
resolving nameserver names to addresses we allow during the resolution
of a single client query. If IPv6 is enabled, an A and a AAAA query
it by the number of NS records found above the
:ref:`setting-max-ns-address-qperq` value. The limit will not be reduced to a
number lower than 5.
- ''',
- 'versionadded' : ['4.1.16', '4.2.2', '4.3.1']
+ """,
+ "versionadded": ["4.1.16", "4.2.2", "4.3.1"],
},
{
- 'name' : 'max_ns_per_resolve',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '13',
- 'help' : 'Maximum number of NS records to consider to resolve a name, 0 is no limit',
- 'doc' : '''
+ "name": "max_ns_per_resolve",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "13",
+ "help": "Maximum number of NS records to consider to resolve a name, 0 is no limit",
+ "doc": """
The maximum number of NS records that will be considered to select a nameserver to contact to resolve a name.
If a zone has more than :ref:`setting-max-ns-per-resolve` NS records, a random sample of this size will be used.
If :ref:`setting-max-ns-per-resolve` is zero, no limit applies.
- ''',
- 'versionadded': ['4.8.0', '4.7.3', '4.6.4', '4.5.11']
+ """,
+ "versionadded": ["4.8.0", "4.7.3", "4.6.4", "4.5.11"],
},
{
- 'name' : 'max_negative_ttl',
- 'section' : 'recordcache',
- 'type' : LType.Uint64,
- 'default' : '3600',
- 'help' : 'maximum number of seconds to keep a negative cached entry in memory',
- 'doc' : '''
+ "name": "max_negative_ttl",
+ "section": "recordcache",
+ "type": LType.Uint64,
+ "default": "3600",
+ "help": "maximum number of seconds to keep a negative cached entry in memory",
+ "doc": """
A query for which there is authoritatively no answer is cached to quickly deny a record's existence later on, without putting a heavy load on the remote server.
In practice, caches can become saturated with hundreds of thousands of hosts which are tried only once.
This setting, which defaults to 3600 seconds, puts a maximum on the amount of time negative entries are cached.
- ''',
+ """,
},
{
- 'name' : 'max_recursion_depth',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '16',
- 'help' : 'Maximum number of internal recursion calls per query, 0 for unlimited',
- 'doc' : '''
+ "name": "max_recursion_depth",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "16",
+ "help": "Maximum number of internal recursion calls per query, 0 for unlimited",
+ "doc": """
Total maximum number of internal recursion calls the server may use to answer a single query.
0 means unlimited.
The value of :ref:`setting-stack-size` should be increased together with this one to prevent the stack from overflowing.
If :ref:`setting-qname-minimization` is enabled, the fallback code in case of a failing resolve is allowed an additional `max-recursion-depth/2`.
- ''',
- 'versionchanged': [('4.1.0', 'Before 4.1.0, this settings was unlimited.'),
- ('4.9.0', "Before 4.9.0 this setting's default was 40 and the limit on ``CNAME`` chains (fixed at 16) acted as a bound on he recursion depth.")]
- },
- {
- 'name' : 'max_tcp_clients',
- 'section' : 'incoming',
- 'type' : LType.Uint64,
- 'default' : '1024',
- 'help' : 'Maximum number of simultaneous TCP clients',
- 'doc' : '''
+ """,
+ "versionchanged": [
+ ("4.1.0", "Before 4.1.0, this settings was unlimited."),
+ (
+ "4.9.0",
+ "Before 4.9.0 this setting's default was 40 and the limit on ``CNAME`` chains (fixed at 16) acted as a bound on he recursion depth.",
+ ),
+ ],
+ },
+ {
+ "name": "max_tcp_clients",
+ "section": "incoming",
+ "type": LType.Uint64,
+ "default": "1024",
+ "help": "Maximum number of simultaneous TCP clients",
+ "doc": """
Maximum number of simultaneous incoming TCP connections allowed.
- ''',
- 'versionchanged': ('5.2.0', 'Before 5.2.0 the default was 128.'),
+ """,
+ "versionchanged": ("5.2.0", "Before 5.2.0 the default was 128."),
},
{
- 'name' : 'max_tcp_per_client',
- 'section' : 'incoming',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'If set, maximum number of TCP sessions per client (IP address)',
- 'doc' : '''
+ "name": "max_tcp_per_client",
+ "section": "incoming",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "If set, maximum number of TCP sessions per client (IP address)",
+ "doc": """
Maximum number of simultaneous incoming TCP connections allowed per client (remote IP address).
0 means unlimited.
- ''',
+ """,
},
{
- 'name' : 'max_tcp_queries_per_connection',
- 'section' : 'incoming',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'If set, maximum number of TCP queries in a TCP connection',
- 'doc' : '''
+ "name": "max_tcp_queries_per_connection",
+ "section": "incoming",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "If set, maximum number of TCP queries in a TCP connection",
+ "doc": """
Maximum number of DNS queries in a TCP connection.
0 means unlimited.
- ''',
- 'versionadded': '4.1.0'
+ """,
+ "versionadded": "4.1.0",
},
{
- 'name' : 'max_total_msec',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '7000',
- 'help' : 'Maximum total wall-clock time per query in milliseconds, 0 for unlimited',
- 'doc' : '''
+ "name": "max_total_msec",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "7000",
+ "help": "Maximum total wall-clock time per query in milliseconds, 0 for unlimited",
+ "doc": """
Total maximum number of milliseconds of wallclock time the server may use to answer a single query.
0 means unlimited.
- ''',
+ """,
},
{
- 'name' : 'max_udp_queries_per_round',
- 'section' : 'incoming',
- 'type' : LType.Uint64,
- 'default' : '10000',
- 'help' : 'Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing',
- 'doc' : '''
+ "name": "max_udp_queries_per_round",
+ "section": "incoming",
+ "type": LType.Uint64,
+ "default": "10000",
+ "help": "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing",
+ "doc": """
Under heavy load the recursor might be busy processing incoming UDP queries for a long while before there is no more of these, and might therefore
neglect scheduling new ``mthreads``, handling responses from authoritative servers or responding to :doc:`rec_control <manpages/rec_control.1>`
requests.
This setting caps the maximum number of incoming UDP DNS queries processed in a single round of looping on ``recvmsg()`` after being woken up by the multiplexer, before
returning back to normal processing and handling other events.
- ''',
- 'versionadded': '4.1.4'
+ """,
+ "versionadded": "4.1.4",
},
{
- 'name' : 'minimum_ttl_override',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '1',
- 'help' : 'The minimum TTL',
- 'doc' : '''
+ "name": "minimum_ttl_override",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "1",
+ "help": "The minimum TTL",
+ "doc": """
This setting artificially raises all TTLs to be at least this long.
Setting this to a value greater than 1 technically is an RFC violation, but might improve performance a lot.
Using a value of 0 impacts performance of TTL 0 records greatly, since it forces the recursor to contact
authoritative servers each time a client requests them.
- ''',
- 'versionchanged': ('4.5.0', 'Old versions used default 0.'),
- 'runtime': 'set-minimum-ttl',
- },
- {
- 'name' : 'tracking',
- 'section' : 'nod',
- 'oldname' : 'new-domain-tracking',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Track newly observed domains (i.e. never seen before).',
- 'doc' : '''
+ """,
+ "versionchanged": ("4.5.0", "Old versions used default 0."),
+ "runtime": "set-minimum-ttl",
+ },
+ {
+ "name": "tracking",
+ "section": "nod",
+ "oldname": "new-domain-tracking",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Track newly observed domains (i.e. never seen before).",
+ "doc": """
Whether to track newly observed domains, i.e. never seen before. This
is a probabilistic algorithm, using a stable bloom filter to store
records of previously seen domains. When enabled for the first time,
thus it may not be available in all pre-built packages.
If protobuf is enabled and configured, then the newly observed domain
status will appear as a flag in Response messages.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'log',
- 'section' : 'nod',
- 'oldname' : 'new-domain-log',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Log newly observed domains.',
- 'doc' : '''
+ "name": "log",
+ "section": "nod",
+ "oldname": "new-domain-log",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Log newly observed domains.",
+ "doc": """
If a newly observed domain is detected, log that domain in the
recursor log file. The log line looks something like::
Jul 18 11:31:25 Newly observed domain nod=sdfoijdfio.com
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'lookup',
- 'section' : 'nod',
- 'oldname' : 'new-domain-lookup',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Perform a DNS lookup newly observed domains as a subdomain of the configured domain',
- 'doc' : '''
+ "name": "lookup",
+ "section": "nod",
+ "oldname": "new-domain-lookup",
+ "type": LType.String,
+ "default": "",
+ "help": "Perform a DNS lookup newly observed domains as a subdomain of the configured domain",
+ "doc": """
If a domain is specified, then each time a newly observed domain is
detected, the recursor will perform an A record lookup of '<newly
observed domain>.<lookup domain>'. For example if 'new-domain-lookup'
'example.com.nod.powerdns.com'. This feature gives a way to share the
newly observed domain with partners, vendors or security teams. The
result of the DNS lookup will be ignored by the recursor.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'db_size',
- 'section' : 'nod',
- 'oldname' : 'new-domain-db-size',
- 'type' : LType.Uint64,
- 'default' : '67108864',
- 'help' : 'Size of the DB used to track new domains in terms of number of cells. Defaults to 67108864',
- 'doc' : '''
+ "name": "db_size",
+ "section": "nod",
+ "oldname": "new-domain-db-size",
+ "type": LType.Uint64,
+ "default": "67108864",
+ "help": "Size of the DB used to track new domains in terms of number of cells. Defaults to 67108864",
+ "doc": """
The default size of the stable bloom filter used to store previously
observed domains is 67108864. To change the number of cells, use this
setting. For each cell, the SBF uses 1 bit of memory, and one byte of
disk for the persistent file.
If there are already persistent files saved to disk, this setting will
have no effect unless you remove the existing files.
- ''',
- 'versionadded': '4.2.0'
- },
- {
- 'name' : 'history_dir',
- 'section' : 'nod',
- 'oldname' : 'new-domain-history-dir',
- 'type' : LType.String,
- 'default' : 'NODCACHEDIRNOD',
- 'docdefault': 'Determined by distribution',
- 'help' : 'Persist new domain tracking data here to persist between restarts',
- 'doc' : '''
+ """,
+ "versionadded": "4.2.0",
+ },
+ {
+ "name": "history_dir",
+ "section": "nod",
+ "oldname": "new-domain-history-dir",
+ "type": LType.String,
+ "default": "NODCACHEDIRNOD",
+ "docdefault": "Determined by distribution",
+ "help": "Persist new domain tracking data here to persist between restarts",
+ "doc": """
This setting controls which directory is used to store the on-disk
cache of previously observed domains.
preserved across recursor restarts.
If you change the new-domain-db-size setting, you must remove any files
from this directory.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'db_snapshot_interval',
- 'section' : 'nod',
- 'oldname' : 'new-domain-db-snapshot-interval',
- 'type' : LType.Uint64,
- 'default' : '600',
- 'help' : 'Interval (in seconds) to write the NOD and UDR DB snapshots',
- 'doc' : '''
+ "name": "db_snapshot_interval",
+ "section": "nod",
+ "oldname": "new-domain-db-snapshot-interval",
+ "type": LType.Uint64,
+ "default": "600",
+ "help": "Interval (in seconds) to write the NOD and UDR DB snapshots",
+ "doc": """
Interval (in seconds) to write the NOD and UDR DB snapshots.
Set to zero to disable snapshot writing.',
- ''',
- 'versionadded': '5.1.0'
+ """,
+ "versionadded": "5.1.0",
},
{
- 'name' : 'ignore_list',
- 'section' : 'nod',
- 'oldname' : 'new-domain-ignore-list',
- 'type' : LType.ListStrings,
- 'default' : '',
- 'help' : 'List of domains (and implicitly all subdomains) which will never be considered a new domain',
- 'doc' : '''
+ "name": "ignore_list",
+ "section": "nod",
+ "oldname": "new-domain-ignore-list",
+ "type": LType.ListStrings,
+ "default": "",
+ "help": "List of domains (and implicitly all subdomains) which will never be considered a new domain",
+ "doc": """
This setting is a list of all domains (and implicitly all subdomains)
that will never be considered a new domain. For example, if the domain
'example.com' is in the list, then 'foo.bar.example.com' will never be
considered a new domain. One use-case for the ignore list is to never
reveal details of internal subdomains via the new-domain-lookup
feature.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'ignore_list_file',
- 'section' : 'nod',
- 'type' : LType.String,
- 'oldname' : 'new-domain-ignore-list-file',
- 'default' : '',
- 'help' : 'File with a list of domains (and implicitly all subdomains) which will never be considered a new domain',
- 'doc' : '''
+ "name": "ignore_list_file",
+ "section": "nod",
+ "type": LType.String,
+ "oldname": "new-domain-ignore-list-file",
+ "default": "",
+ "help": "File with a list of domains (and implicitly all subdomains) which will never be considered a new domain",
+ "doc": """
Path to a file with a list of domains. File should have one domain per line,
with no extra characters or comments.
See :ref:`setting-new-domain-ignore-list`.
- ''',
- 'versionadded': '5.1.0'
+ """,
+ "versionadded": "5.1.0",
},
{
- 'name' : 'pb_tag',
- 'section' : 'nod',
- 'oldname' : 'new-domain-pb-tag',
- 'type' : LType.String,
- 'default' : 'pdns-nod',
- 'help' : 'If protobuf is configured, the tag to use for messages containing newly observed domains. Defaults to \'pdns-nod\'',
- 'doc' : '''
+ "name": "pb_tag",
+ "section": "nod",
+ "oldname": "new-domain-pb-tag",
+ "type": LType.String,
+ "default": "pdns-nod",
+ "help": "If protobuf is configured, the tag to use for messages containing newly observed domains. Defaults to 'pdns-nod'",
+ "doc": """
If protobuf is configured, then this tag will be added to all protobuf response messages when
a new domain is observed.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'network_timeout',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '1500',
- 'help' : 'Wait this number of milliseconds for network i/o',
- 'doc' : '''
+ "name": "network_timeout",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "1500",
+ "help": "Wait this number of milliseconds for network i/o",
+ "doc": """
Number of milliseconds to wait for a remote authoritative server to respond.
If the number of concurrent requests is high, the :program:Recursor uses a lower value.
- ''',
+ """,
},
{
- 'name' : 'no_shuffle',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Don\'t change',
- 'doc' : 'SKIP',
- 'skip-yaml': True,
+ "name": "no_shuffle",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Don't change",
+ "doc": "SKIP",
+ "skip-yaml": True,
},
{
- 'name' : 'non_resolving_ns_max_fails',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '5',
- 'help' : 'Number of failed address resolves of a nameserver to start throttling it, 0 is disabled',
- 'doc' : '''
+ "name": "non_resolving_ns_max_fails",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "5",
+ "help": "Number of failed address resolves of a nameserver to start throttling it, 0 is disabled",
+ "doc": """
Number of failed address resolves of a nameserver name to start throttling it, 0 is disabled.
Nameservers matching :ref:`setting-dont-throttle-names` will not be throttled.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'non_resolving_ns_throttle_time',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '60',
- 'help' : 'Number of seconds to throttle a nameserver with a name failing to resolve',
- 'doc' : '''
+ "name": "non_resolving_ns_throttle_time",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "60",
+ "help": "Number of seconds to throttle a nameserver with a name failing to resolve",
+ "doc": """
Number of seconds to throttle a nameserver with a name failing to resolve.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'nothing_below_nxdomain',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : 'dnssec',
- 'help' : 'When an NXDOMAIN exists in cache for a name with fewer labels than the qname, send NXDOMAIN without doing a lookup (see RFC 8020)',
- 'doc' : '''
+ "name": "nothing_below_nxdomain",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "dnssec",
+ "help": "When an NXDOMAIN exists in cache for a name with fewer labels than the qname, send NXDOMAIN without doing a lookup (see RFC 8020)",
+ "doc": """
- One of ``no``, ``dnssec``, ``yes``.
The type of :rfc:`8020` handling using cached NXDOMAIN responses.
``yes``
:rfc:`8020` processing is done using any non-Bogus NXDOMAIN record
available in the cache.
- ''',
- 'versionadded': '4.3.0'
+ """,
+ "versionadded": "4.3.0",
},
{
- 'name' : 'nsec3_max_iterations',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '50',
- 'help' : 'Maximum number of iterations allowed for an NSEC3 record',
- 'doc' : '''
+ "name": "nsec3_max_iterations",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "50",
+ "help": "Maximum number of iterations allowed for an NSEC3 record",
+ "doc": """
Maximum number of iterations allowed for an NSEC3 record.
If an answer containing an NSEC3 record with more iterations is received, its DNSSEC validation status is treated as ``Insecure``.
- ''',
- 'versionadded': '4.1.0',
- 'versionchanged': [('4.5.2', 'Default is now 150, was 2500 before.'),
- ('5.0.0', 'Default is now 50, was 150 before.')]
- },
- {
- 'name' : 'max_rrsigs_per_record',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '2',
- 'help' : 'Maximum number of RRSIGs to consider when validating a given record',
- 'doc' : '''
+ """,
+ "versionadded": "4.1.0",
+ "versionchanged": [
+ ("4.5.2", "Default is now 150, was 2500 before."),
+ ("5.0.0", "Default is now 50, was 150 before."),
+ ],
+ },
+ {
+ "name": "max_rrsigs_per_record",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "2",
+ "help": "Maximum number of RRSIGs to consider when validating a given record",
+ "doc": """
Maximum number of RRSIGs we are willing to cryptographically check when validating a given record. Expired or not yet incepted RRSIGs do not count toward to this limit.
- ''',
- 'versionadded': ['5.0.2', '4.9.3', '4.8.6'],
+ """,
+ "versionadded": ["5.0.2", "4.9.3", "4.8.6"],
},
{
- 'name' : 'max_nsec3s_per_record',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '10',
- 'help' : 'Maximum number of NSEC3s to consider when validating a given denial of existence',
- 'doc' : '''
+ "name": "max_nsec3s_per_record",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "10",
+ "help": "Maximum number of NSEC3s to consider when validating a given denial of existence",
+ "doc": """
Maximum number of NSEC3s to consider when validating a given denial of existence.
- ''',
- 'versionadded': ['5.0.2', '4.9.3', '4.8.6'],
+ """,
+ "versionadded": ["5.0.2", "4.9.3", "4.8.6"],
},
{
- 'name' : 'max_signature_validations_per_query',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '30',
- 'help' : 'Maximum number of RRSIG signatures we are willing to validate per incoming query',
- 'doc' : '''
+ "name": "max_signature_validations_per_query",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "30",
+ "help": "Maximum number of RRSIG signatures we are willing to validate per incoming query",
+ "doc": """
Maximum number of RRSIG signatures we are willing to validate per incoming query.
- ''',
- 'versionadded': ['5.0.2', '4.9.3', '4.8.6'],
+ """,
+ "versionadded": ["5.0.2", "4.9.3", "4.8.6"],
},
{
- 'name' : 'max_nsec3_hash_computations_per_query',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '600',
- 'help' : 'Maximum number of NSEC3 hashes that we are willing to compute during DNSSEC validation, per incoming query',
- 'doc' : '''
+ "name": "max_nsec3_hash_computations_per_query",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "600",
+ "help": "Maximum number of NSEC3 hashes that we are willing to compute during DNSSEC validation, per incoming query",
+ "doc": """
Maximum number of NSEC3 hashes that we are willing to compute during DNSSEC validation, per incoming query.
- ''',
- 'versionadded': ['5.0.2', '4.9.3', '4.8.6'],
+ """,
+ "versionadded": ["5.0.2", "4.9.3", "4.8.6"],
},
{
- 'name' : 'aggressive_cache_max_nsec3_hash_cost',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '150',
- 'help' : 'Maximum estimated NSEC3 cost for a given query to consider aggressive use of the NSEC3 cache',
- 'doc' : '''
+ "name": "aggressive_cache_max_nsec3_hash_cost",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "150",
+ "help": "Maximum estimated NSEC3 cost for a given query to consider aggressive use of the NSEC3 cache",
+ "doc": """
Maximum estimated NSEC3 cost for a given query to consider aggressive use of the NSEC3 cache. The cost is estimated based on a heuristic taking the zone's NSEC3 salt and iterations parameters into account, as well at the number of labels of the requested name. For example a query for a name like a.b.c.d.e.f.example.com. in an example.com zone. secured with NSEC3 and 10 iterations (NSEC3 iterations count of 9) and an empty salt will have an estimated worst-case cost of 10 (iterations) * 6 (number of labels) = 60. The aggressive NSEC cache is an optimization to reduce the number of queries to authoritative servers, which is especially useful when a zone is under pseudo-random subdomain attack, and we want to skip it the zone parameters make it expensive.
-''',
- 'versionadded': ['5.0.2', '4.9.3', '4.8.6'],
+""",
+ "versionadded": ["5.0.2", "4.9.3", "4.8.6"],
},
{
- 'name' : 'max_ds_per_zone',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '8',
- 'help' : 'Maximum number of DS records to consider per zone',
- 'doc' : '''
+ "name": "max_ds_per_zone",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "8",
+ "help": "Maximum number of DS records to consider per zone",
+ "doc": """
Maximum number of DS records to consider when validating records inside a zone.
- ''',
- 'versionadded': ['5.0.2', '4.9.3', '4.8.6'],
+ """,
+ "versionadded": ["5.0.2", "4.9.3", "4.8.6"],
},
{
- 'name' : 'max_dnskeys',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '2',
- 'help' : 'Maximum number of DNSKEYs with the same algorithm and tag to consider when validating a given record',
- 'doc' : '''
+ "name": "max_dnskeys",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "2",
+ "help": "Maximum number of DNSKEYs with the same algorithm and tag to consider when validating a given record",
+ "doc": """
Maximum number of DNSKEYs with the same algorithm and tag to consider when validating a given record. Setting this value to 1 effectively denies DNSKEY tag collisions in a zone.
- ''',
- 'versionadded': ['5.0.2', '4.9.3', '4.8.6'],
+ """,
+ "versionadded": ["5.0.2", "4.9.3", "4.8.6"],
},
{
- 'name' : 'ttl',
- 'section' : 'packetcache',
- 'oldname' : 'packetcache-ttl',
- 'type' : LType.Uint64,
- 'default' : '86400',
- 'help' : 'maximum number of seconds to keep a cached entry in packetcache',
- 'doc' : '''
+ "name": "ttl",
+ "section": "packetcache",
+ "oldname": "packetcache-ttl",
+ "type": LType.Uint64,
+ "default": "86400",
+ "help": "maximum number of seconds to keep a cached entry in packetcache",
+ "doc": """
Maximum number of seconds to cache an item in the packet cache, no matter what the original TTL specified.
- ''',
- 'versionchanged': ('4.9.0', 'The default was changed from 3600 (1 hour) to 86400 (24 hours).')
+ """,
+ "versionchanged": ("4.9.0", "The default was changed from 3600 (1 hour) to 86400 (24 hours)."),
},
{
- 'name' : 'negative_ttl',
- 'section' : 'packetcache',
- 'oldname' : 'packetcache-negative-ttl',
- 'type' : LType.Uint64,
- 'default' : '60',
- 'help' : 'maximum number of seconds to keep a cached NxDomain or NoData entry in packetcache',
- 'doc' : '''
+ "name": "negative_ttl",
+ "section": "packetcache",
+ "oldname": "packetcache-negative-ttl",
+ "type": LType.Uint64,
+ "default": "60",
+ "help": "maximum number of seconds to keep a cached NxDomain or NoData entry in packetcache",
+ "doc": """
Maximum number of seconds to cache an ``NxDomain`` or ``NoData`` (a ``NoError`` response without answer records) answer in the packetcache.
This setting's maximum is capped to :ref:`setting-packetcache-ttl`.
i.e. setting :ref:`setting-packetcache-ttl` to 15 and keeping :ref:`setting-packetcache-negative-ttl` at the default will lower the used value of :ref:`setting-packetcache-negative-ttl` to 15.
- ''',
- 'versionadded': '4.9.0'
+ """,
+ "versionadded": "4.9.0",
},
{
- 'name' : 'servfail_ttl',
- 'section' : 'packetcache',
- 'oldname' : 'packetcache-servfail-ttl',
- 'type' : LType.Uint64,
- 'default' : '60',
- 'help' : 'maximum number of seconds to keep a cached servfail entry in packetcache',
- 'doc' : '''
+ "name": "servfail_ttl",
+ "section": "packetcache",
+ "oldname": "packetcache-servfail-ttl",
+ "type": LType.Uint64,
+ "default": "60",
+ "help": "maximum number of seconds to keep a cached servfail entry in packetcache",
+ "doc": """
Maximum number of seconds to cache an answer indicating a failure to resolve in the packet cache.
Before version 4.6.0 only ``ServFail`` answers were considered as such. All responses with a code other than ``NoError`` and ``NXDomain``, or without records in the answer and authority sections, are considered as a failure to resolve.
This setting's maximum is capped to :ref:`setting-packetcache-ttl`. Setting :ref:`setting-packetcache-ttl` to 15 and keeping :ref:`setting-packetcache-servfail-ttl` at the default will lower the used value of :ref:`setting-packetcache-servfail-ttl` to 15.
Since 4.9.0, negative answers are handled separately from resolving failures.
-''',
+""",
},
{
- 'name' : 'shards',
- 'section' : 'packetcache',
- 'oldname' : 'packetcache-shards',
- 'type' : LType.Uint64,
- 'default' : '1024',
- 'help' : 'Number of shards in the packet cache',
- 'doc' : '''
+ "name": "shards",
+ "section": "packetcache",
+ "oldname": "packetcache-shards",
+ "type": LType.Uint64,
+ "default": "1024",
+ "help": "Number of shards in the packet cache",
+ "doc": """
Sets the number of shards in the packet cache. If you have high contention as reported by ``packetcache-contented/packetcache-acquired``,
you can try to enlarge this value or run with fewer threads.
- ''',
- 'versionadded': '4.9.0'
+ """,
+ "versionadded": "4.9.0",
},
{
- 'name' : 'pdns_distributes_queries',
- 'section' : 'incoming',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'If PowerDNS itself should distribute queries over threads',
- 'doc' : '''
+ "name": "pdns_distributes_queries",
+ "section": "incoming",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "If PowerDNS itself should distribute queries over threads",
+ "doc": """
If set, PowerDNS will use distinct threads to listen to client sockets and distribute that work to worker-threads using a hash of the query.
This feature should maximize the cache hit ratio on versions before 4.9.0.
To use more than one thread set :ref:`setting-distributor-threads` in version 4.2.0 or newer.
Enabling should improve performance on systems where :ref:`setting-reuseport` does not have the effect of
balancing the queries evenly over multiple worker threads.
- ''',
- 'versionchanged': ('4.9.0', 'Default changed to ``no``, previously it was ``yes``.')
+ """,
+ "versionchanged": ("4.9.0", "Default changed to ``no``, previously it was ``yes``."),
},
{
- 'name' : 'processes',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '1',
- 'help' : 'Launch this number of processes (EXPERIMENTAL, DO NOT CHANGE)',
- 'doc' : '''SKIP''',
- 'skip-yaml': True,
+ "name": "processes",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "1",
+ "help": "Launch this number of processes (EXPERIMENTAL, DO NOT CHANGE)",
+ "doc": """SKIP""",
+ "skip-yaml": True,
},
{
- 'name' : 'protobuf_use_kernel_timestamp',
- 'section' : 'logging',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Compute the latency of queries in protobuf messages by using the timestamp set by the kernel when the query was received (when available)',
- 'doc' : '''
+ "name": "protobuf_use_kernel_timestamp",
+ "section": "logging",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Compute the latency of queries in protobuf messages by using the timestamp set by the kernel when the query was received (when available)",
+ "doc": """
Whether to compute the latency of responses in protobuf messages using the timestamp set by the kernel when the query packet was received (when available), instead of computing it based on the moment we start processing the query.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'proxy_protocol_from',
- 'section' : 'incoming',
- 'type' : LType.ListSubnets,
- 'default' : '',
- 'help' : 'A Proxy Protocol header is required from these subnets',
- 'doc' : '''
+ "name": "proxy_protocol_from",
+ "section": "incoming",
+ "type": LType.ListSubnets,
+ "default": "",
+ "help": "A Proxy Protocol header is required from these subnets",
+ "doc": """
Ranges that are required to send a Proxy Protocol version 2 header in front of UDP and TCP queries, to pass the original source and destination addresses and ports to the recursor, as well as custom values.
Queries that are not prefixed with such a header will not be accepted from clients in these ranges. Queries prefixed by headers from clients that are not listed in these ranges will be dropped.
Note that once a Proxy Protocol header has been received, the source address from the proxy header instead of the address of the proxy will be checked against the :ref:`setting-allow-from` ACL.
The dnsdist docs have `more information about the PROXY protocol <https://dnsdist.org/advanced/passing-source-address.html#proxy-protocol>`_.
- ''',
- 'versionadded' : '4.4.0',
- 'versionchanged' : [('5.0.5', 'YAML settings only: previously this was defined as a string instead of a sequence'),
- ('5.3.0', '``rec_control reload-acls`` reloads this setting')],
- 'runtime': ['reload-acls (since 5.3.0)'],
- },
- {
- 'name' : 'proxy_protocol_exceptions',
- 'section' : 'incoming',
- 'type' : LType.ListSocketAddresses,
- 'default' : '',
- 'help' : 'A Proxy Protocol header should not be used for these listen addresses.',
- 'doc' : '''
+ """,
+ "versionadded": "4.4.0",
+ "versionchanged": [
+ ("5.0.5", "YAML settings only: previously this was defined as a string instead of a sequence"),
+ ("5.3.0", "``rec_control reload-acls`` reloads this setting"),
+ ],
+ "runtime": ["reload-acls (since 5.3.0)"],
+ },
+ {
+ "name": "proxy_protocol_exceptions",
+ "section": "incoming",
+ "type": LType.ListSocketAddresses,
+ "default": "",
+ "help": "A Proxy Protocol header should not be used for these listen addresses.",
+ "doc": """
If set, clients sending from an address in :ref:`setting-proxy-protocol-from` to an address:port listed here are excluded from using the Proxy Protocol.
If no port is specified, port 53 is assumed.
This is typically used to provide an easy to use address and port to send debug queries to.
- ''',
- 'versionadded' : '5.1.0',
- 'versionchanged' : ('5.3.0', '``rec_control reload-acls`` reloads this setting'),
- 'runtime': ['reload-acls (since 5.3.0)'],
- },
- {
- 'name' : 'proxy_protocol_maximum_size',
- 'section' : 'incoming',
- 'type' : LType.Uint64,
- 'default' : '512',
- 'help' : 'The maximum size of a proxy protocol payload, including the TLV values',
- 'doc' : '''
+ """,
+ "versionadded": "5.1.0",
+ "versionchanged": ("5.3.0", "``rec_control reload-acls`` reloads this setting"),
+ "runtime": ["reload-acls (since 5.3.0)"],
+ },
+ {
+ "name": "proxy_protocol_maximum_size",
+ "section": "incoming",
+ "type": LType.Uint64,
+ "default": "512",
+ "help": "The maximum size of a proxy protocol payload, including the TLV values",
+ "doc": """
The maximum size, in bytes, of a Proxy Protocol payload (header, addresses and ports, and TLV values). Queries with a larger payload will be dropped.
- ''',
- 'versionadded': '4.4.0'
+ """,
+ "versionadded": "4.4.0",
},
{
- 'name' : 'public_suffix_list_file',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Path to the Public Suffix List file, if any',
- 'doc' : '''
+ "name": "public_suffix_list_file",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "Path to the Public Suffix List file, if any",
+ "doc": """
Path to the Public Suffix List file, if any. If set, PowerDNS will try to load the Public Suffix List from this file instead of using the built-in list. The PSL is used to group the queries by relevant domain names when displaying the top queries.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'qname_minimization',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Use Query Name Minimization',
- 'doc' : '''
+ "name": "qname_minimization",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Use Query Name Minimization",
+ "doc": """
Enable Query Name Minimization. This implements a relaxed form of Query Name Mimimization as
described in :rfc:`9156`.
- ''',
- 'versionadded': '4.3.0'
+ """,
+ "versionadded": "4.3.0",
},
{
- 'name' : 'qname_max_minimize_count',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '10',
- 'help' : 'RFC9156 max minimize count',
- 'doc' : '''
+ "name": "qname_max_minimize_count",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "10",
+ "help": "RFC9156 max minimize count",
+ "doc": """
``Max minimize count`` parameter, described in :rfc:`9156`. This is the maximum number of iterations
of the Query Name Minimization Algorithm.
- ''',
- 'versionadded': '5.0.0'
+ """,
+ "versionadded": "5.0.0",
},
{
- 'name' : 'qname_minimize_one_label',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '4',
- 'help' : 'RFC9156 minimize one label parameter',
- 'doc' : '''
+ "name": "qname_minimize_one_label",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "4",
+ "help": "RFC9156 minimize one label parameter",
+ "doc": """
``Minimize one label`` parameter, described in :rfc:`9156`.
The value for the number of iterations of the Query Name Minimization Algorithm that should only have one label appended.
This value has precedence over :ref:`setting-qname-max-minimize-count`.
- ''',
- 'versionadded': '5.0.0'
+ """,
+ "versionadded": "5.0.0",
},
{
- 'name' : 'source_address',
- 'section' : 'outgoing',
- 'oldname' : 'query-local-address',
- 'type' : LType.ListSubnets,
- 'default' : '0.0.0.0',
- 'help' : 'Source IP address for sending queries',
- 'doc' : '''
+ "name": "source_address",
+ "section": "outgoing",
+ "oldname": "query-local-address",
+ "type": LType.ListSubnets,
+ "default": "0.0.0.0",
+ "help": "Source IP address for sending queries",
+ "doc": """
.. note::
While subnets and their negations are mentioned as accepted, the handling of subnets has not been implemented yet.
Only individual IP addresses can be listed.
addresses, increased spoofing resilience is achieved. When no address of a certain
address family is configured, there are *no* queries sent with that address family.
In the default configuration this means that IPv6 is not used for outgoing queries.
- ''',
- 'versionchanged': ('4.4.0', 'IPv6 addresses can be set with this option as well.')
+ """,
+ "versionchanged": ("4.4.0", "IPv6 addresses can be set with this option as well."),
},
{
- 'name' : 'quiet',
- 'section' : 'logging',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Suppress logging of questions and answers',
- 'doc' : '''
+ "name": "quiet",
+ "section": "logging",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Suppress logging of questions and answers",
+ "doc": """
Don't log queries.
- ''',
+ """,
},
{
- 'name' : 'locked_ttl_perc',
- 'section' : 'recordcache',
- 'oldname' : 'record-cache-locked-ttl-perc',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'Replace records in record cache only after this % of original TTL has passed',
- 'doc' : '''
+ "name": "locked_ttl_perc",
+ "section": "recordcache",
+ "oldname": "record-cache-locked-ttl-perc",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "Replace records in record cache only after this % of original TTL has passed",
+ "doc": """
Replace record sets in the record cache only after this percentage of the original TTL has passed.
The PowerDNS Recursor already has several mechanisms to protect against spoofing attempts.
This adds an extra layer of protection---as it limits the window of time cache updates are accepted---at the cost of a less efficient record cache.
- Authoritative record sets will replace unauthoritative record sets unless DNSSEC validation of the new record set failed.
- If the new record set belongs to a DNSSEC-secure zone and successfully passed validation it will replace an existing entry.
- Record sets produced by :ref:`setting-refresh-on-ttl-perc` tasks will also replace existing record sets.
- ''',
- 'versionadded': '4.8.0'
+ """,
+ "versionadded": "4.8.0",
},
{
- 'name' : 'shards',
- 'section' : 'recordcache',
- 'oldname' : 'record-cache-shards',
- 'type' : LType.Uint64,
- 'default' : '1024',
- 'help' : 'Number of shards in the record cache',
- 'doc' : '''
+ "name": "shards",
+ "section": "recordcache",
+ "oldname": "record-cache-shards",
+ "type": LType.Uint64,
+ "default": "1024",
+ "help": "Number of shards in the record cache",
+ "doc": """
Sets the number of shards in the record cache. If you have high
contention as reported by
``record-cache-contented/record-cache-acquired``, you can try to
enlarge this value or run with fewer threads.
- ''',
- 'versionadded': '4.4.0'
+ """,
+ "versionadded": "4.4.0",
},
{
- 'name' : 'refresh_on_ttl_perc',
- 'section' : 'recordcache',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'If a record is requested from the cache and only this % of original TTL remains, refetch',
- 'doc' : '''
+ "name": "refresh_on_ttl_perc",
+ "section": "recordcache",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "If a record is requested from the cache and only this % of original TTL remains, refetch",
+ "doc": """
Sets the 'refresh almost expired' percentage of the record cache. Whenever a record is fetched from the packet or record cache
and only ``refresh-on-ttl-perc`` percent or less of its original TTL is left, a task is queued to refetch the name/type combination to
update the record cache. In most cases this causes future queries to always see a non-expired record cache entry.
A typical value is 10. If the value is zero, this functionality is disabled.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'reuseport',
- 'section' : 'incoming',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address',
- 'doc' : '''
+ "name": "reuseport",
+ "section": "incoming",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address",
+ "doc": """
If ``SO_REUSEPORT`` support is available, allows multiple threads and processes to open listening sockets for the same port.
Since 4.1.0, when :ref:`setting-pdns-distributes-queries` is disabled and :ref:`setting-reuseport` is enabled, every worker-thread will open a separate listening socket to let the kernel distribute the incoming queries instead of running a distributor thread (which could otherwise be a bottleneck) and avoiding thundering herd issues, thus leading to much higher performance on multi-core boxes.
- ''',
- 'versionchanged': ('4.9.0', 'The default is changed to ``yes``, previously it was ``no``. If ``SO_REUSEPORT`` support is not available, the setting defaults to ``no``.')
- },
- {
- 'name' : 'rng',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : 'auto',
- 'help' : '',
- 'doc' : '''
- ''',
- 'skip-yaml': True,
- 'versionchanged': ('4.9.0', 'This setting is no longer used.')
- },
- {
- 'name' : 'root_nx_trust',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'If set, believe that an NXDOMAIN from the root means the TLD does not exist',
- 'doc' : '''
+ """,
+ "versionchanged": (
+ "4.9.0",
+ "The default is changed to ``yes``, previously it was ``no``. If ``SO_REUSEPORT`` support is not available, the setting defaults to ``no``.",
+ ),
+ },
+ {
+ "name": "rng",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "auto",
+ "help": "",
+ "doc": """
+ """,
+ "skip-yaml": True,
+ "versionchanged": ("4.9.0", "This setting is no longer used."),
+ },
+ {
+ "name": "root_nx_trust",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "If set, believe that an NXDOMAIN from the root means the TLD does not exist",
+ "doc": """
If set, an NXDOMAIN from the root-servers will serve as a blanket NXDOMAIN for the entire TLD the query belonged to.
The effect of this is far fewer queries to the root-servers.
- ''',
- 'versionchanged': ('4.0.0', "Default is ``yes`` now, was ``no`` before 4.0.0")
+ """,
+ "versionchanged": ("4.0.0", "Default is ``yes`` now, was ``no`` before 4.0.0"),
},
{
- 'name' : 'save_parent_ns_set',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Save parent NS set to be used if child NS set fails',
- 'doc' : '''
+ "name": "save_parent_ns_set",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Save parent NS set to be used if child NS set fails",
+ "doc": """
If set, a parent (non-authoritative) ``NS`` set is saved if it contains more entries than a newly encountered child (authoritative) ``NS`` set for the same domain.
The saved parent ``NS`` set is tried if resolution using the child ``NS`` set fails.
- ''',
- 'versionadded': '4.7.0'
+ """,
+ "versionadded": "4.7.0",
},
{
- 'name' : 'security_poll_suffix',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : 'secpoll.powerdns.com.',
- 'help' : 'Domain name from which to query security update notifications',
- 'doc' : '''
+ "name": "security_poll_suffix",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "secpoll.powerdns.com.",
+ "help": "Domain name from which to query security update notifications",
+ "doc": """
Domain name from which to query security update notifications.
Setting this to an empty string disables secpoll.
- ''',
+ """,
},
{
- 'name' : 'serve_rfc1918',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'If we should be authoritative for RFC 1918 private IP space',
- 'doc' : '''
+ "name": "serve_rfc1918",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "If we should be authoritative for RFC 1918 private IP space",
+ "doc": """
This makes the server authoritatively aware of: ``10.in-addr.arpa``, ``168.192.in-addr.arpa``, ``16-31.172.in-addr.arpa``, which saves load on the AS112 servers.
Individual parts of these zones can still be loaded or forwarded.
- ''',
- 'runtime': ['reload-zones'],
+ """,
+ "runtime": ["reload-zones"],
},
{
- 'name' : 'serve_rfc6303',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'If we should be authoritative for RFC 6303 private IP space',
- 'doc' : '''
+ "name": "serve_rfc6303",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "If we should be authoritative for RFC 6303 private IP space",
+ "doc": """
This makes the server authoritatively aware of the zones in RFC 6303 not covered by RFC 1918.
Individual parts of these zones can still be loaded or forwarded.
:ref:`setting-serve-rfc1918` must be enabled for this option to take effect.
-''',
- 'versionadded': ['5.1.3', '5.2.0'],
- 'runtime': ['reload-zones'],
+""",
+ "versionadded": ["5.1.3", "5.2.0"],
+ "runtime": ["reload-zones"],
},
{
- 'name' : 'serve_stale_extensions',
- 'section' : 'recordcache',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'Number of times a record\'s ttl is extended by 30s to be served stale',
- 'doc' : '''
+ "name": "serve_stale_extensions",
+ "section": "recordcache",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "Number of times a record's ttl is extended by 30s to be served stale",
+ "doc": """
Maximum number of times an expired record's TTL is extended by 30s when serving stale.
Extension only occurs if a record cannot be refreshed.
A value of 0 means the ``Serve Stale`` mechanism is not used.
To allow records becoming stale to be served for an hour, use a value of 120.
See :ref:`serve-stale` for a description of the Serve Stale mechanism.
- ''',
- 'versionadded': '4.8.0'
+ """,
+ "versionadded": "4.8.0",
},
{
- 'name' : 'server_down_max_fails',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '64',
- 'help' : 'Maximum number of consecutive timeouts (and unreachables) to mark a server as down ( 0 => disabled )',
- 'doc' : '''
+ "name": "server_down_max_fails",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "64",
+ "help": "Maximum number of consecutive timeouts (and unreachables) to mark a server as down ( 0 => disabled )",
+ "doc": """
If a server has not responded in any way this many times in a row, no longer send it any queries for :ref:`setting-server-down-throttle-time` seconds.
Afterwards, we will try a new packet, and if that also gets no response at all, we again throttle for :ref:`setting-server-down-throttle-time` seconds.
Even a single response packet will drop the block.
- ''',
+ """,
},
{
- 'name' : 'server_down_throttle_time',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '60',
- 'help' : 'Number of seconds to throttle all queries to a server after being marked as down',
- 'doc' : '''
+ "name": "server_down_throttle_time",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "60",
+ "help": "Number of seconds to throttle all queries to a server after being marked as down",
+ "doc": """
Throttle a server that has failed to respond :ref:`setting-server-down-max-fails` times for this many seconds.
- ''',
+ """,
},
{
- 'name' : 'bypass_server_throttling_probability',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '25',
- 'help' : 'Determines the probability of a server marked down to be used anyway',
- 'doc' : '''
+ "name": "bypass_server_throttling_probability",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "25",
+ "help": "Determines the probability of a server marked down to be used anyway",
+ "doc": """
This setting determines the probability of a server marked down to be used anyway.
A value of ``n`` means that the chance of a server marked down still being used after it wins speed selection is is ``1/n``.
If this setting is zero throttled servers will never be selected to be used anyway.
- ''',
- 'versionadded': '5.0.0'
+ """,
+ "versionadded": "5.0.0",
},
{
- 'name' : 'server_id',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : RUNTIME,
- 'help' : 'Returned when queried for \'id.server\' TXT or NSID, defaults to hostname, set custom or \'disabled\'',
- 'doc' : '''
+ "name": "server_id",
+ "section": "recursor",
+ "type": LType.String,
+ "default": RUNTIME,
+ "help": "Returned when queried for 'id.server' TXT or NSID, defaults to hostname, set custom or 'disabled'",
+ "doc": """
The reply given by The PowerDNS recursor to a query for 'id.server' with its hostname, useful for in clusters.
When a query contains the :rfc:`NSID EDNS0 Option <5001>`, this value is returned in the response as the NSID value.
dig @192.0.2.14 CHAOS TXT id.server.
dig @192.0.2.14 example.com IN A +nsid
- ''',
+ """,
},
{
- 'name' : 'setgid',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'If set, change group id to this gid for more security',
- 'doc' : '''
+ "name": "setgid",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "If set, change group id to this gid for more security",
+ "doc": """
PowerDNS can change its user and group id after binding to its socket.
Can be used for better :doc:`security <security>`.
- '''
+ """,
},
{
- 'name' : 'setuid',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'If set, change user id to this uid for more security',
- 'doc' : '''
+ "name": "setuid",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "If set, change user id to this uid for more security",
+ "doc": """
PowerDNS can change its user and group id after binding to its socket.
Can be used for better :doc:`security <security>`.
- '''
+ """,
},
{
- 'name' : 'signature_inception_skew',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '60',
- 'help' : 'Allow the signature inception to be off by this number of seconds',
- 'doc' : '''
+ "name": "signature_inception_skew",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "60",
+ "help": "Allow the signature inception to be off by this number of seconds",
+ "doc": """
Allow the signature inception to be off by this number of seconds. Negative values are not allowed.
- ''',
- 'versionadded': '4.1.5',
- 'versionchanged': ('4.2.0', 'Default is now 60, was 0 before.')
+ """,
+ "versionadded": "4.1.5",
+ "versionchanged": ("4.2.0", "Default is now 60, was 0 before."),
},
{
- 'name' : 'single_socket',
- 'section' : 'outgoing',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'If set, only use a single socket for outgoing queries',
- 'doc' : '''
+ "name": "single_socket",
+ "section": "outgoing",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "If set, only use a single socket for outgoing queries",
+ "doc": """
Use only a single socket for outgoing queries.
- ''',
+ """,
},
{
- 'name' : 'agent',
- 'section' : 'snmp',
- 'oldname' : 'snmp-agent',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'If set, register as an SNMP agent',
- 'doc' : '''
+ "name": "agent",
+ "section": "snmp",
+ "oldname": "snmp-agent",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "If set, register as an SNMP agent",
+ "doc": """
If set to true and PowerDNS has been compiled with SNMP support, it will register as an SNMP agent to provide statistics and be able to send traps.
- ''',
- 'versionadded': '4.1.0'
+ """,
+ "versionadded": "4.1.0",
},
{
- 'name' : 'daemon_socket',
- 'section' : 'snmp',
- 'oldname' : 'snmp-daemon-socket',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'If set and snmp-agent is set, the socket to use to register to the SNMP daemon',
- 'doc' : '''
+ "name": "daemon_socket",
+ "section": "snmp",
+ "oldname": "snmp-daemon-socket",
+ "type": LType.String,
+ "default": "",
+ "help": "If set and snmp-agent is set, the socket to use to register to the SNMP daemon",
+ "doc": """
If not empty and ``snmp-agent`` is set to true, indicates how PowerDNS should contact the SNMP daemon to register as an SNMP agent.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'soa_minimum_ttl',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'Don\'t change',
- 'doc' : '''SKIP''',
- 'skip-yaml': True,
+ "name": "soa_minimum_ttl",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "Don't change",
+ "doc": """SKIP""",
+ "skip-yaml": True,
},
{
- 'name' : 'socket_dir',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Where the controlsocket will live, /var/run/pdns-recursor when unset and not chrooted',
- 'doc' : '''
+ "name": "socket_dir",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "Where the controlsocket will live, /var/run/pdns-recursor when unset and not chrooted",
+ "doc": """
Where to store the control socket and pidfile.
The default depends on ``LOCALSTATEDIR`` or the ``--with-socketdir`` setting when building (usually ``/var/run`` or ``/run``).
When using :ref:`setting-chroot` the default becomes ``/``.
The default value is overruled by the ``RUNTIME_DIRECTORY`` environment variable when that variable has a value (e.g. under systemd).
- ''',
+ """,
},
{
- 'name' : 'socket_group',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Group of socket',
- 'doc' : '''
+ "name": "socket_group",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "Group of socket",
+ "doc": """
Group and mode of the controlsocket.
Owner and group can be specified by name, mode is in octal.
-'''
+""",
},
{
- 'name' : 'socket_mode',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Permissions for socket',
- 'doc' : '''
+ "name": "socket_mode",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "Permissions for socket",
+ "doc": """
Mode of the controlsocket.
Owner and group can be specified by name, mode is in octal.
- '''
+ """,
},
{
- 'name' : 'socket_owner',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Owner of socket',
- 'doc' : '''
+ "name": "socket_owner",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "Owner of socket",
+ "doc": """
Owner of the controlsocket.
Owner and group can be specified by name, mode is in octal.
- '''
+ """,
},
{
- 'name' : 'spoof_nearmiss_max',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '1',
- 'help' : 'If non-zero, assume spoofing after this many near misses',
- 'doc' : '''
+ "name": "spoof_nearmiss_max",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "1",
+ "help": "If non-zero, assume spoofing after this many near misses",
+ "doc": """
If set to non-zero, PowerDNS will assume it is being subjected to a spoofing attack after seeing this many answers with the wrong id.
- ''',
- 'versionchanged': ('4.5.0', 'Older versions used 20 as the default value.')
+ """,
+ "versionchanged": ("4.5.0", "Older versions used 20 as the default value."),
},
{
- 'name' : 'stack_cache_size',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '100',
- 'help' : 'Size of the stack cache, per mthread',
- 'doc' : '''
+ "name": "stack_cache_size",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "100",
+ "help": "Size of the stack cache, per mthread",
+ "doc": """
Maximum number of mthread stacks that can be cached for later reuse, per thread. Caching these stacks reduces the CPU load at the cost of a slightly higher memory usage, each cached stack consuming `stack-size` bytes of memory.
It makes no sense to cache more stacks than the value of `max-mthreads`, since there will never be more stacks than that in use at a given time.
- ''',
- 'versionadded': '4.9.0'
+ """,
+ "versionadded": "4.9.0",
},
{
- 'name' : 'stack_size',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '200000',
- 'help' : 'stack size per mthread',
- 'doc' : '''
+ "name": "stack_size",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "200000",
+ "help": "stack size per mthread",
+ "doc": """
Size in bytes of the stack of each mthread.
- ''',
+ """,
},
{
- 'name' : 'statistics_interval',
- 'section' : 'logging',
- 'type' : LType.Uint64,
- 'default' : '1800',
- 'help' : 'Number of seconds between printing of recursor statistics, 0 to disable',
- 'doc' : '''
+ "name": "statistics_interval",
+ "section": "logging",
+ "type": LType.Uint64,
+ "default": "1800",
+ "help": "Number of seconds between printing of recursor statistics, 0 to disable",
+ "doc": """
Interval between logging statistical summary on recursor performance.
Use 0 to disable.
- ''',
- 'versionadded': '4.1.0'
+ """,
+ "versionadded": "4.1.0",
},
{
- 'name' : 'stats_api_disabled_list',
- 'section' : 'recursor',
- 'type' : LType.ListStrings,
- 'default' : 'cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-1, ecs-v4-response-bits-2, ecs-v4-response-bits-3, ecs-v4-response-bits-4, ecs-v4-response-bits-5, ecs-v4-response-bits-6, ecs-v4-response-bits-7, ecs-v4-response-bits-8, ecs-v4-response-bits-9, ecs-v4-response-bits-10, ecs-v4-response-bits-11, ecs-v4-response-bits-12, ecs-v4-response-bits-13, ecs-v4-response-bits-14, ecs-v4-response-bits-15, ecs-v4-response-bits-16, ecs-v4-response-bits-17, ecs-v4-response-bits-18, ecs-v4-response-bits-19, ecs-v4-response-bits-20, ecs-v4-response-bits-21, ecs-v4-response-bits-22, ecs-v4-response-bits-23, ecs-v4-response-bits-24, ecs-v4-response-bits-25, ecs-v4-response-bits-26, ecs-v4-response-bits-27, ecs-v4-response-bits-28, ecs-v4-response-bits-29, ecs-v4-response-bits-30, ecs-v4-response-bits-31, ecs-v4-response-bits-32, ecs-v6-response-bits-1, ecs-v6-response-bits-2, ecs-v6-response-bits-3, ecs-v6-response-bits-4, ecs-v6-response-bits-5, ecs-v6-response-bits-6, ecs-v6-response-bits-7, ecs-v6-response-bits-8, ecs-v6-response-bits-9, ecs-v6-response-bits-10, ecs-v6-response-bits-11, ecs-v6-response-bits-12, ecs-v6-response-bits-13, ecs-v6-response-bits-14, ecs-v6-response-bits-15, ecs-v6-response-bits-16, ecs-v6-response-bits-17, ecs-v6-response-bits-18, ecs-v6-response-bits-19, ecs-v6-response-bits-20, ecs-v6-response-bits-21, ecs-v6-response-bits-22, ecs-v6-response-bits-23, ecs-v6-response-bits-24, ecs-v6-response-bits-25, ecs-v6-response-bits-26, ecs-v6-response-bits-27, ecs-v6-response-bits-28, ecs-v6-response-bits-29, ecs-v6-response-bits-30, ecs-v6-response-bits-31, ecs-v6-response-bits-32, ecs-v6-response-bits-33, ecs-v6-response-bits-34, ecs-v6-response-bits-35, ecs-v6-response-bits-36, ecs-v6-response-bits-37, ecs-v6-response-bits-38, ecs-v6-response-bits-39, ecs-v6-response-bits-40, ecs-v6-response-bits-41, ecs-v6-response-bits-42, ecs-v6-response-bits-43, ecs-v6-response-bits-44, ecs-v6-response-bits-45, ecs-v6-response-bits-46, ecs-v6-response-bits-47, ecs-v6-response-bits-48, ecs-v6-response-bits-49, ecs-v6-response-bits-50, ecs-v6-response-bits-51, ecs-v6-response-bits-52, ecs-v6-response-bits-53, ecs-v6-response-bits-54, ecs-v6-response-bits-55, ecs-v6-response-bits-56, ecs-v6-response-bits-57, ecs-v6-response-bits-58, ecs-v6-response-bits-59, ecs-v6-response-bits-60, ecs-v6-response-bits-61, ecs-v6-response-bits-62, ecs-v6-response-bits-63, ecs-v6-response-bits-64, ecs-v6-response-bits-65, ecs-v6-response-bits-66, ecs-v6-response-bits-67, ecs-v6-response-bits-68, ecs-v6-response-bits-69, ecs-v6-response-bits-70, ecs-v6-response-bits-71, ecs-v6-response-bits-72, ecs-v6-response-bits-73, ecs-v6-response-bits-74, ecs-v6-response-bits-75, ecs-v6-response-bits-76, ecs-v6-response-bits-77, ecs-v6-response-bits-78, ecs-v6-response-bits-79, ecs-v6-response-bits-80, ecs-v6-response-bits-81, ecs-v6-response-bits-82, ecs-v6-response-bits-83, ecs-v6-response-bits-84, ecs-v6-response-bits-85, ecs-v6-response-bits-86, ecs-v6-response-bits-87, ecs-v6-response-bits-88, ecs-v6-response-bits-89, ecs-v6-response-bits-90, ecs-v6-response-bits-91, ecs-v6-response-bits-92, ecs-v6-response-bits-93, ecs-v6-response-bits-94, ecs-v6-response-bits-95, ecs-v6-response-bits-96, ecs-v6-response-bits-97, ecs-v6-response-bits-98, ecs-v6-response-bits-99, ecs-v6-response-bits-100, ecs-v6-response-bits-101, ecs-v6-response-bits-102, ecs-v6-response-bits-103, ecs-v6-response-bits-104, ecs-v6-response-bits-105, ecs-v6-response-bits-106, ecs-v6-response-bits-107, ecs-v6-response-bits-108, ecs-v6-response-bits-109, ecs-v6-response-bits-110, ecs-v6-response-bits-111, ecs-v6-response-bits-112, ecs-v6-response-bits-113, ecs-v6-response-bits-114, ecs-v6-response-bits-115, ecs-v6-response-bits-116, ecs-v6-response-bits-117, ecs-v6-response-bits-118, ecs-v6-response-bits-119, ecs-v6-response-bits-120, ecs-v6-response-bits-121, ecs-v6-response-bits-122, ecs-v6-response-bits-123, ecs-v6-response-bits-124, ecs-v6-response-bits-125, ecs-v6-response-bits-126, ecs-v6-response-bits-127, ecs-v6-response-bits-128',
- 'docdefault': 'cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-\\*, ecs-v6-response-bits-\\*',
- 'help' : 'List of statistics that are disabled when retrieving the complete list of statistics via the API',
- 'doc' : '''
+ "name": "stats_api_disabled_list",
+ "section": "recursor",
+ "type": LType.ListStrings,
+ "default": "cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-1, ecs-v4-response-bits-2, ecs-v4-response-bits-3, ecs-v4-response-bits-4, ecs-v4-response-bits-5, ecs-v4-response-bits-6, ecs-v4-response-bits-7, ecs-v4-response-bits-8, ecs-v4-response-bits-9, ecs-v4-response-bits-10, ecs-v4-response-bits-11, ecs-v4-response-bits-12, ecs-v4-response-bits-13, ecs-v4-response-bits-14, ecs-v4-response-bits-15, ecs-v4-response-bits-16, ecs-v4-response-bits-17, ecs-v4-response-bits-18, ecs-v4-response-bits-19, ecs-v4-response-bits-20, ecs-v4-response-bits-21, ecs-v4-response-bits-22, ecs-v4-response-bits-23, ecs-v4-response-bits-24, ecs-v4-response-bits-25, ecs-v4-response-bits-26, ecs-v4-response-bits-27, ecs-v4-response-bits-28, ecs-v4-response-bits-29, ecs-v4-response-bits-30, ecs-v4-response-bits-31, ecs-v4-response-bits-32, ecs-v6-response-bits-1, ecs-v6-response-bits-2, ecs-v6-response-bits-3, ecs-v6-response-bits-4, ecs-v6-response-bits-5, ecs-v6-response-bits-6, ecs-v6-response-bits-7, ecs-v6-response-bits-8, ecs-v6-response-bits-9, ecs-v6-response-bits-10, ecs-v6-response-bits-11, ecs-v6-response-bits-12, ecs-v6-response-bits-13, ecs-v6-response-bits-14, ecs-v6-response-bits-15, ecs-v6-response-bits-16, ecs-v6-response-bits-17, ecs-v6-response-bits-18, ecs-v6-response-bits-19, ecs-v6-response-bits-20, ecs-v6-response-bits-21, ecs-v6-response-bits-22, ecs-v6-response-bits-23, ecs-v6-response-bits-24, ecs-v6-response-bits-25, ecs-v6-response-bits-26, ecs-v6-response-bits-27, ecs-v6-response-bits-28, ecs-v6-response-bits-29, ecs-v6-response-bits-30, ecs-v6-response-bits-31, ecs-v6-response-bits-32, ecs-v6-response-bits-33, ecs-v6-response-bits-34, ecs-v6-response-bits-35, ecs-v6-response-bits-36, ecs-v6-response-bits-37, ecs-v6-response-bits-38, ecs-v6-response-bits-39, ecs-v6-response-bits-40, ecs-v6-response-bits-41, ecs-v6-response-bits-42, ecs-v6-response-bits-43, ecs-v6-response-bits-44, ecs-v6-response-bits-45, ecs-v6-response-bits-46, ecs-v6-response-bits-47, ecs-v6-response-bits-48, ecs-v6-response-bits-49, ecs-v6-response-bits-50, ecs-v6-response-bits-51, ecs-v6-response-bits-52, ecs-v6-response-bits-53, ecs-v6-response-bits-54, ecs-v6-response-bits-55, ecs-v6-response-bits-56, ecs-v6-response-bits-57, ecs-v6-response-bits-58, ecs-v6-response-bits-59, ecs-v6-response-bits-60, ecs-v6-response-bits-61, ecs-v6-response-bits-62, ecs-v6-response-bits-63, ecs-v6-response-bits-64, ecs-v6-response-bits-65, ecs-v6-response-bits-66, ecs-v6-response-bits-67, ecs-v6-response-bits-68, ecs-v6-response-bits-69, ecs-v6-response-bits-70, ecs-v6-response-bits-71, ecs-v6-response-bits-72, ecs-v6-response-bits-73, ecs-v6-response-bits-74, ecs-v6-response-bits-75, ecs-v6-response-bits-76, ecs-v6-response-bits-77, ecs-v6-response-bits-78, ecs-v6-response-bits-79, ecs-v6-response-bits-80, ecs-v6-response-bits-81, ecs-v6-response-bits-82, ecs-v6-response-bits-83, ecs-v6-response-bits-84, ecs-v6-response-bits-85, ecs-v6-response-bits-86, ecs-v6-response-bits-87, ecs-v6-response-bits-88, ecs-v6-response-bits-89, ecs-v6-response-bits-90, ecs-v6-response-bits-91, ecs-v6-response-bits-92, ecs-v6-response-bits-93, ecs-v6-response-bits-94, ecs-v6-response-bits-95, ecs-v6-response-bits-96, ecs-v6-response-bits-97, ecs-v6-response-bits-98, ecs-v6-response-bits-99, ecs-v6-response-bits-100, ecs-v6-response-bits-101, ecs-v6-response-bits-102, ecs-v6-response-bits-103, ecs-v6-response-bits-104, ecs-v6-response-bits-105, ecs-v6-response-bits-106, ecs-v6-response-bits-107, ecs-v6-response-bits-108, ecs-v6-response-bits-109, ecs-v6-response-bits-110, ecs-v6-response-bits-111, ecs-v6-response-bits-112, ecs-v6-response-bits-113, ecs-v6-response-bits-114, ecs-v6-response-bits-115, ecs-v6-response-bits-116, ecs-v6-response-bits-117, ecs-v6-response-bits-118, ecs-v6-response-bits-119, ecs-v6-response-bits-120, ecs-v6-response-bits-121, ecs-v6-response-bits-122, ecs-v6-response-bits-123, ecs-v6-response-bits-124, ecs-v6-response-bits-125, ecs-v6-response-bits-126, ecs-v6-response-bits-127, ecs-v6-response-bits-128",
+ "docdefault": "cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-\\*, ecs-v6-response-bits-\\*",
+ "help": "List of statistics that are disabled when retrieving the complete list of statistics via the API",
+ "doc": """
A list of comma-separated statistic names, that are disabled when retrieving the complete list of statistics via the API for performance reasons.
These statistics can still be retrieved individually by specifically asking for it.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
A sequence of statistic names, that are disabled when retrieving the complete list of statistics via the API for performance reasons.
These statistics can still be retrieved individually by specifically asking for it.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'stats_carbon_disabled_list',
- 'section' : 'recursor',
- 'type' : LType.ListStrings,
- 'default' : 'cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-1, ecs-v4-response-bits-2, ecs-v4-response-bits-3, ecs-v4-response-bits-4, ecs-v4-response-bits-5, ecs-v4-response-bits-6, ecs-v4-response-bits-7, ecs-v4-response-bits-8, ecs-v4-response-bits-9, ecs-v4-response-bits-10, ecs-v4-response-bits-11, ecs-v4-response-bits-12, ecs-v4-response-bits-13, ecs-v4-response-bits-14, ecs-v4-response-bits-15, ecs-v4-response-bits-16, ecs-v4-response-bits-17, ecs-v4-response-bits-18, ecs-v4-response-bits-19, ecs-v4-response-bits-20, ecs-v4-response-bits-21, ecs-v4-response-bits-22, ecs-v4-response-bits-23, ecs-v4-response-bits-24, ecs-v4-response-bits-25, ecs-v4-response-bits-26, ecs-v4-response-bits-27, ecs-v4-response-bits-28, ecs-v4-response-bits-29, ecs-v4-response-bits-30, ecs-v4-response-bits-31, ecs-v4-response-bits-32, ecs-v6-response-bits-1, ecs-v6-response-bits-2, ecs-v6-response-bits-3, ecs-v6-response-bits-4, ecs-v6-response-bits-5, ecs-v6-response-bits-6, ecs-v6-response-bits-7, ecs-v6-response-bits-8, ecs-v6-response-bits-9, ecs-v6-response-bits-10, ecs-v6-response-bits-11, ecs-v6-response-bits-12, ecs-v6-response-bits-13, ecs-v6-response-bits-14, ecs-v6-response-bits-15, ecs-v6-response-bits-16, ecs-v6-response-bits-17, ecs-v6-response-bits-18, ecs-v6-response-bits-19, ecs-v6-response-bits-20, ecs-v6-response-bits-21, ecs-v6-response-bits-22, ecs-v6-response-bits-23, ecs-v6-response-bits-24, ecs-v6-response-bits-25, ecs-v6-response-bits-26, ecs-v6-response-bits-27, ecs-v6-response-bits-28, ecs-v6-response-bits-29, ecs-v6-response-bits-30, ecs-v6-response-bits-31, ecs-v6-response-bits-32, ecs-v6-response-bits-33, ecs-v6-response-bits-34, ecs-v6-response-bits-35, ecs-v6-response-bits-36, ecs-v6-response-bits-37, ecs-v6-response-bits-38, ecs-v6-response-bits-39, ecs-v6-response-bits-40, ecs-v6-response-bits-41, ecs-v6-response-bits-42, ecs-v6-response-bits-43, ecs-v6-response-bits-44, ecs-v6-response-bits-45, ecs-v6-response-bits-46, ecs-v6-response-bits-47, ecs-v6-response-bits-48, ecs-v6-response-bits-49, ecs-v6-response-bits-50, ecs-v6-response-bits-51, ecs-v6-response-bits-52, ecs-v6-response-bits-53, ecs-v6-response-bits-54, ecs-v6-response-bits-55, ecs-v6-response-bits-56, ecs-v6-response-bits-57, ecs-v6-response-bits-58, ecs-v6-response-bits-59, ecs-v6-response-bits-60, ecs-v6-response-bits-61, ecs-v6-response-bits-62, ecs-v6-response-bits-63, ecs-v6-response-bits-64, ecs-v6-response-bits-65, ecs-v6-response-bits-66, ecs-v6-response-bits-67, ecs-v6-response-bits-68, ecs-v6-response-bits-69, ecs-v6-response-bits-70, ecs-v6-response-bits-71, ecs-v6-response-bits-72, ecs-v6-response-bits-73, ecs-v6-response-bits-74, ecs-v6-response-bits-75, ecs-v6-response-bits-76, ecs-v6-response-bits-77, ecs-v6-response-bits-78, ecs-v6-response-bits-79, ecs-v6-response-bits-80, ecs-v6-response-bits-81, ecs-v6-response-bits-82, ecs-v6-response-bits-83, ecs-v6-response-bits-84, ecs-v6-response-bits-85, ecs-v6-response-bits-86, ecs-v6-response-bits-87, ecs-v6-response-bits-88, ecs-v6-response-bits-89, ecs-v6-response-bits-90, ecs-v6-response-bits-91, ecs-v6-response-bits-92, ecs-v6-response-bits-93, ecs-v6-response-bits-94, ecs-v6-response-bits-95, ecs-v6-response-bits-96, ecs-v6-response-bits-97, ecs-v6-response-bits-98, ecs-v6-response-bits-99, ecs-v6-response-bits-100, ecs-v6-response-bits-101, ecs-v6-response-bits-102, ecs-v6-response-bits-103, ecs-v6-response-bits-104, ecs-v6-response-bits-105, ecs-v6-response-bits-106, ecs-v6-response-bits-107, ecs-v6-response-bits-108, ecs-v6-response-bits-109, ecs-v6-response-bits-110, ecs-v6-response-bits-111, ecs-v6-response-bits-112, ecs-v6-response-bits-113, ecs-v6-response-bits-114, ecs-v6-response-bits-115, ecs-v6-response-bits-116, ecs-v6-response-bits-117, ecs-v6-response-bits-118, ecs-v6-response-bits-119, ecs-v6-response-bits-120, ecs-v6-response-bits-121, ecs-v6-response-bits-122, ecs-v6-response-bits-123, ecs-v6-response-bits-124, ecs-v6-response-bits-125, ecs-v6-response-bits-126, ecs-v6-response-bits-127, ecs-v6-response-bits-128, cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count',
- 'docdefault': 'cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-\\*, ecs-v6-response-bits-\\*, cumul-answers-\\*, cumul-auth4answers-\\*, cumul-auth6answers-\\*',
- 'help' : 'List of statistics that are prevented from being exported via Carbon',
- 'doc' : '''
+ "name": "stats_carbon_disabled_list",
+ "section": "recursor",
+ "type": LType.ListStrings,
+ "default": "cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-1, ecs-v4-response-bits-2, ecs-v4-response-bits-3, ecs-v4-response-bits-4, ecs-v4-response-bits-5, ecs-v4-response-bits-6, ecs-v4-response-bits-7, ecs-v4-response-bits-8, ecs-v4-response-bits-9, ecs-v4-response-bits-10, ecs-v4-response-bits-11, ecs-v4-response-bits-12, ecs-v4-response-bits-13, ecs-v4-response-bits-14, ecs-v4-response-bits-15, ecs-v4-response-bits-16, ecs-v4-response-bits-17, ecs-v4-response-bits-18, ecs-v4-response-bits-19, ecs-v4-response-bits-20, ecs-v4-response-bits-21, ecs-v4-response-bits-22, ecs-v4-response-bits-23, ecs-v4-response-bits-24, ecs-v4-response-bits-25, ecs-v4-response-bits-26, ecs-v4-response-bits-27, ecs-v4-response-bits-28, ecs-v4-response-bits-29, ecs-v4-response-bits-30, ecs-v4-response-bits-31, ecs-v4-response-bits-32, ecs-v6-response-bits-1, ecs-v6-response-bits-2, ecs-v6-response-bits-3, ecs-v6-response-bits-4, ecs-v6-response-bits-5, ecs-v6-response-bits-6, ecs-v6-response-bits-7, ecs-v6-response-bits-8, ecs-v6-response-bits-9, ecs-v6-response-bits-10, ecs-v6-response-bits-11, ecs-v6-response-bits-12, ecs-v6-response-bits-13, ecs-v6-response-bits-14, ecs-v6-response-bits-15, ecs-v6-response-bits-16, ecs-v6-response-bits-17, ecs-v6-response-bits-18, ecs-v6-response-bits-19, ecs-v6-response-bits-20, ecs-v6-response-bits-21, ecs-v6-response-bits-22, ecs-v6-response-bits-23, ecs-v6-response-bits-24, ecs-v6-response-bits-25, ecs-v6-response-bits-26, ecs-v6-response-bits-27, ecs-v6-response-bits-28, ecs-v6-response-bits-29, ecs-v6-response-bits-30, ecs-v6-response-bits-31, ecs-v6-response-bits-32, ecs-v6-response-bits-33, ecs-v6-response-bits-34, ecs-v6-response-bits-35, ecs-v6-response-bits-36, ecs-v6-response-bits-37, ecs-v6-response-bits-38, ecs-v6-response-bits-39, ecs-v6-response-bits-40, ecs-v6-response-bits-41, ecs-v6-response-bits-42, ecs-v6-response-bits-43, ecs-v6-response-bits-44, ecs-v6-response-bits-45, ecs-v6-response-bits-46, ecs-v6-response-bits-47, ecs-v6-response-bits-48, ecs-v6-response-bits-49, ecs-v6-response-bits-50, ecs-v6-response-bits-51, ecs-v6-response-bits-52, ecs-v6-response-bits-53, ecs-v6-response-bits-54, ecs-v6-response-bits-55, ecs-v6-response-bits-56, ecs-v6-response-bits-57, ecs-v6-response-bits-58, ecs-v6-response-bits-59, ecs-v6-response-bits-60, ecs-v6-response-bits-61, ecs-v6-response-bits-62, ecs-v6-response-bits-63, ecs-v6-response-bits-64, ecs-v6-response-bits-65, ecs-v6-response-bits-66, ecs-v6-response-bits-67, ecs-v6-response-bits-68, ecs-v6-response-bits-69, ecs-v6-response-bits-70, ecs-v6-response-bits-71, ecs-v6-response-bits-72, ecs-v6-response-bits-73, ecs-v6-response-bits-74, ecs-v6-response-bits-75, ecs-v6-response-bits-76, ecs-v6-response-bits-77, ecs-v6-response-bits-78, ecs-v6-response-bits-79, ecs-v6-response-bits-80, ecs-v6-response-bits-81, ecs-v6-response-bits-82, ecs-v6-response-bits-83, ecs-v6-response-bits-84, ecs-v6-response-bits-85, ecs-v6-response-bits-86, ecs-v6-response-bits-87, ecs-v6-response-bits-88, ecs-v6-response-bits-89, ecs-v6-response-bits-90, ecs-v6-response-bits-91, ecs-v6-response-bits-92, ecs-v6-response-bits-93, ecs-v6-response-bits-94, ecs-v6-response-bits-95, ecs-v6-response-bits-96, ecs-v6-response-bits-97, ecs-v6-response-bits-98, ecs-v6-response-bits-99, ecs-v6-response-bits-100, ecs-v6-response-bits-101, ecs-v6-response-bits-102, ecs-v6-response-bits-103, ecs-v6-response-bits-104, ecs-v6-response-bits-105, ecs-v6-response-bits-106, ecs-v6-response-bits-107, ecs-v6-response-bits-108, ecs-v6-response-bits-109, ecs-v6-response-bits-110, ecs-v6-response-bits-111, ecs-v6-response-bits-112, ecs-v6-response-bits-113, ecs-v6-response-bits-114, ecs-v6-response-bits-115, ecs-v6-response-bits-116, ecs-v6-response-bits-117, ecs-v6-response-bits-118, ecs-v6-response-bits-119, ecs-v6-response-bits-120, ecs-v6-response-bits-121, ecs-v6-response-bits-122, ecs-v6-response-bits-123, ecs-v6-response-bits-124, ecs-v6-response-bits-125, ecs-v6-response-bits-126, ecs-v6-response-bits-127, ecs-v6-response-bits-128, cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count",
+ "docdefault": "cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-\\*, ecs-v6-response-bits-\\*, cumul-answers-\\*, cumul-auth4answers-\\*, cumul-auth6answers-\\*",
+ "help": "List of statistics that are prevented from being exported via Carbon",
+ "doc": """
A list of comma-separated statistic names, that are prevented from being exported via carbon for performance reasons.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
A sequence of statistic names, that are prevented from being exported via carbon for performance reasons.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'stats_rec_control_disabled_list',
- 'section' : 'recursor',
- 'type' : LType.ListStrings,
- 'default' : 'cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-1, ecs-v4-response-bits-2, ecs-v4-response-bits-3, ecs-v4-response-bits-4, ecs-v4-response-bits-5, ecs-v4-response-bits-6, ecs-v4-response-bits-7, ecs-v4-response-bits-8, ecs-v4-response-bits-9, ecs-v4-response-bits-10, ecs-v4-response-bits-11, ecs-v4-response-bits-12, ecs-v4-response-bits-13, ecs-v4-response-bits-14, ecs-v4-response-bits-15, ecs-v4-response-bits-16, ecs-v4-response-bits-17, ecs-v4-response-bits-18, ecs-v4-response-bits-19, ecs-v4-response-bits-20, ecs-v4-response-bits-21, ecs-v4-response-bits-22, ecs-v4-response-bits-23, ecs-v4-response-bits-24, ecs-v4-response-bits-25, ecs-v4-response-bits-26, ecs-v4-response-bits-27, ecs-v4-response-bits-28, ecs-v4-response-bits-29, ecs-v4-response-bits-30, ecs-v4-response-bits-31, ecs-v4-response-bits-32, ecs-v6-response-bits-1, ecs-v6-response-bits-2, ecs-v6-response-bits-3, ecs-v6-response-bits-4, ecs-v6-response-bits-5, ecs-v6-response-bits-6, ecs-v6-response-bits-7, ecs-v6-response-bits-8, ecs-v6-response-bits-9, ecs-v6-response-bits-10, ecs-v6-response-bits-11, ecs-v6-response-bits-12, ecs-v6-response-bits-13, ecs-v6-response-bits-14, ecs-v6-response-bits-15, ecs-v6-response-bits-16, ecs-v6-response-bits-17, ecs-v6-response-bits-18, ecs-v6-response-bits-19, ecs-v6-response-bits-20, ecs-v6-response-bits-21, ecs-v6-response-bits-22, ecs-v6-response-bits-23, ecs-v6-response-bits-24, ecs-v6-response-bits-25, ecs-v6-response-bits-26, ecs-v6-response-bits-27, ecs-v6-response-bits-28, ecs-v6-response-bits-29, ecs-v6-response-bits-30, ecs-v6-response-bits-31, ecs-v6-response-bits-32, ecs-v6-response-bits-33, ecs-v6-response-bits-34, ecs-v6-response-bits-35, ecs-v6-response-bits-36, ecs-v6-response-bits-37, ecs-v6-response-bits-38, ecs-v6-response-bits-39, ecs-v6-response-bits-40, ecs-v6-response-bits-41, ecs-v6-response-bits-42, ecs-v6-response-bits-43, ecs-v6-response-bits-44, ecs-v6-response-bits-45, ecs-v6-response-bits-46, ecs-v6-response-bits-47, ecs-v6-response-bits-48, ecs-v6-response-bits-49, ecs-v6-response-bits-50, ecs-v6-response-bits-51, ecs-v6-response-bits-52, ecs-v6-response-bits-53, ecs-v6-response-bits-54, ecs-v6-response-bits-55, ecs-v6-response-bits-56, ecs-v6-response-bits-57, ecs-v6-response-bits-58, ecs-v6-response-bits-59, ecs-v6-response-bits-60, ecs-v6-response-bits-61, ecs-v6-response-bits-62, ecs-v6-response-bits-63, ecs-v6-response-bits-64, ecs-v6-response-bits-65, ecs-v6-response-bits-66, ecs-v6-response-bits-67, ecs-v6-response-bits-68, ecs-v6-response-bits-69, ecs-v6-response-bits-70, ecs-v6-response-bits-71, ecs-v6-response-bits-72, ecs-v6-response-bits-73, ecs-v6-response-bits-74, ecs-v6-response-bits-75, ecs-v6-response-bits-76, ecs-v6-response-bits-77, ecs-v6-response-bits-78, ecs-v6-response-bits-79, ecs-v6-response-bits-80, ecs-v6-response-bits-81, ecs-v6-response-bits-82, ecs-v6-response-bits-83, ecs-v6-response-bits-84, ecs-v6-response-bits-85, ecs-v6-response-bits-86, ecs-v6-response-bits-87, ecs-v6-response-bits-88, ecs-v6-response-bits-89, ecs-v6-response-bits-90, ecs-v6-response-bits-91, ecs-v6-response-bits-92, ecs-v6-response-bits-93, ecs-v6-response-bits-94, ecs-v6-response-bits-95, ecs-v6-response-bits-96, ecs-v6-response-bits-97, ecs-v6-response-bits-98, ecs-v6-response-bits-99, ecs-v6-response-bits-100, ecs-v6-response-bits-101, ecs-v6-response-bits-102, ecs-v6-response-bits-103, ecs-v6-response-bits-104, ecs-v6-response-bits-105, ecs-v6-response-bits-106, ecs-v6-response-bits-107, ecs-v6-response-bits-108, ecs-v6-response-bits-109, ecs-v6-response-bits-110, ecs-v6-response-bits-111, ecs-v6-response-bits-112, ecs-v6-response-bits-113, ecs-v6-response-bits-114, ecs-v6-response-bits-115, ecs-v6-response-bits-116, ecs-v6-response-bits-117, ecs-v6-response-bits-118, ecs-v6-response-bits-119, ecs-v6-response-bits-120, ecs-v6-response-bits-121, ecs-v6-response-bits-122, ecs-v6-response-bits-123, ecs-v6-response-bits-124, ecs-v6-response-bits-125, ecs-v6-response-bits-126, ecs-v6-response-bits-127, ecs-v6-response-bits-128, cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count',
- 'docdefault': 'cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-\\*, ecs-v6-response-bits-\\*, cumul-answers-\\*, cumul-auth4answers-\\*, cumul-auth6answers-\\*',
- 'help' : 'List of statistics that are prevented from being exported via rec_control get-all',
- 'doc' : '''
+ "name": "stats_rec_control_disabled_list",
+ "section": "recursor",
+ "type": LType.ListStrings,
+ "default": "cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-1, ecs-v4-response-bits-2, ecs-v4-response-bits-3, ecs-v4-response-bits-4, ecs-v4-response-bits-5, ecs-v4-response-bits-6, ecs-v4-response-bits-7, ecs-v4-response-bits-8, ecs-v4-response-bits-9, ecs-v4-response-bits-10, ecs-v4-response-bits-11, ecs-v4-response-bits-12, ecs-v4-response-bits-13, ecs-v4-response-bits-14, ecs-v4-response-bits-15, ecs-v4-response-bits-16, ecs-v4-response-bits-17, ecs-v4-response-bits-18, ecs-v4-response-bits-19, ecs-v4-response-bits-20, ecs-v4-response-bits-21, ecs-v4-response-bits-22, ecs-v4-response-bits-23, ecs-v4-response-bits-24, ecs-v4-response-bits-25, ecs-v4-response-bits-26, ecs-v4-response-bits-27, ecs-v4-response-bits-28, ecs-v4-response-bits-29, ecs-v4-response-bits-30, ecs-v4-response-bits-31, ecs-v4-response-bits-32, ecs-v6-response-bits-1, ecs-v6-response-bits-2, ecs-v6-response-bits-3, ecs-v6-response-bits-4, ecs-v6-response-bits-5, ecs-v6-response-bits-6, ecs-v6-response-bits-7, ecs-v6-response-bits-8, ecs-v6-response-bits-9, ecs-v6-response-bits-10, ecs-v6-response-bits-11, ecs-v6-response-bits-12, ecs-v6-response-bits-13, ecs-v6-response-bits-14, ecs-v6-response-bits-15, ecs-v6-response-bits-16, ecs-v6-response-bits-17, ecs-v6-response-bits-18, ecs-v6-response-bits-19, ecs-v6-response-bits-20, ecs-v6-response-bits-21, ecs-v6-response-bits-22, ecs-v6-response-bits-23, ecs-v6-response-bits-24, ecs-v6-response-bits-25, ecs-v6-response-bits-26, ecs-v6-response-bits-27, ecs-v6-response-bits-28, ecs-v6-response-bits-29, ecs-v6-response-bits-30, ecs-v6-response-bits-31, ecs-v6-response-bits-32, ecs-v6-response-bits-33, ecs-v6-response-bits-34, ecs-v6-response-bits-35, ecs-v6-response-bits-36, ecs-v6-response-bits-37, ecs-v6-response-bits-38, ecs-v6-response-bits-39, ecs-v6-response-bits-40, ecs-v6-response-bits-41, ecs-v6-response-bits-42, ecs-v6-response-bits-43, ecs-v6-response-bits-44, ecs-v6-response-bits-45, ecs-v6-response-bits-46, ecs-v6-response-bits-47, ecs-v6-response-bits-48, ecs-v6-response-bits-49, ecs-v6-response-bits-50, ecs-v6-response-bits-51, ecs-v6-response-bits-52, ecs-v6-response-bits-53, ecs-v6-response-bits-54, ecs-v6-response-bits-55, ecs-v6-response-bits-56, ecs-v6-response-bits-57, ecs-v6-response-bits-58, ecs-v6-response-bits-59, ecs-v6-response-bits-60, ecs-v6-response-bits-61, ecs-v6-response-bits-62, ecs-v6-response-bits-63, ecs-v6-response-bits-64, ecs-v6-response-bits-65, ecs-v6-response-bits-66, ecs-v6-response-bits-67, ecs-v6-response-bits-68, ecs-v6-response-bits-69, ecs-v6-response-bits-70, ecs-v6-response-bits-71, ecs-v6-response-bits-72, ecs-v6-response-bits-73, ecs-v6-response-bits-74, ecs-v6-response-bits-75, ecs-v6-response-bits-76, ecs-v6-response-bits-77, ecs-v6-response-bits-78, ecs-v6-response-bits-79, ecs-v6-response-bits-80, ecs-v6-response-bits-81, ecs-v6-response-bits-82, ecs-v6-response-bits-83, ecs-v6-response-bits-84, ecs-v6-response-bits-85, ecs-v6-response-bits-86, ecs-v6-response-bits-87, ecs-v6-response-bits-88, ecs-v6-response-bits-89, ecs-v6-response-bits-90, ecs-v6-response-bits-91, ecs-v6-response-bits-92, ecs-v6-response-bits-93, ecs-v6-response-bits-94, ecs-v6-response-bits-95, ecs-v6-response-bits-96, ecs-v6-response-bits-97, ecs-v6-response-bits-98, ecs-v6-response-bits-99, ecs-v6-response-bits-100, ecs-v6-response-bits-101, ecs-v6-response-bits-102, ecs-v6-response-bits-103, ecs-v6-response-bits-104, ecs-v6-response-bits-105, ecs-v6-response-bits-106, ecs-v6-response-bits-107, ecs-v6-response-bits-108, ecs-v6-response-bits-109, ecs-v6-response-bits-110, ecs-v6-response-bits-111, ecs-v6-response-bits-112, ecs-v6-response-bits-113, ecs-v6-response-bits-114, ecs-v6-response-bits-115, ecs-v6-response-bits-116, ecs-v6-response-bits-117, ecs-v6-response-bits-118, ecs-v6-response-bits-119, ecs-v6-response-bits-120, ecs-v6-response-bits-121, ecs-v6-response-bits-122, ecs-v6-response-bits-123, ecs-v6-response-bits-124, ecs-v6-response-bits-125, ecs-v6-response-bits-126, ecs-v6-response-bits-127, ecs-v6-response-bits-128, cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count",
+ "docdefault": "cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-\\*, ecs-v6-response-bits-\\*, cumul-answers-\\*, cumul-auth4answers-\\*, cumul-auth6answers-\\*",
+ "help": "List of statistics that are prevented from being exported via rec_control get-all",
+ "doc": """
A list of comma-separated statistic names, that are disabled when retrieving the complete list of statistics via `rec_control get-all`, for performance reasons.
These statistics can still be retrieved individually.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
A sequence of statistic names, that are disabled when retrieving the complete list of statistics via `rec_control get-all`, for performance reasons.
These statistics can still be retrieved individually.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'stats_ringbuffer_entries',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '10000',
- 'help' : 'maximum number of packets to store statistics for',
- 'doc' : '''
+ "name": "stats_ringbuffer_entries",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "10000",
+ "help": "maximum number of packets to store statistics for",
+ "doc": """
Number of entries in the remotes ringbuffer, which keeps statistics on who is querying your server.
Can be read out using ``rec_control top-remotes``.
- ''',
+ """,
},
{
- 'name' : 'stats_snmp_disabled_list',
- 'section' : 'recursor',
- 'type' : LType.ListStrings,
- 'default' : 'cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-1, ecs-v4-response-bits-2, ecs-v4-response-bits-3, ecs-v4-response-bits-4, ecs-v4-response-bits-5, ecs-v4-response-bits-6, ecs-v4-response-bits-7, ecs-v4-response-bits-8, ecs-v4-response-bits-9, ecs-v4-response-bits-10, ecs-v4-response-bits-11, ecs-v4-response-bits-12, ecs-v4-response-bits-13, ecs-v4-response-bits-14, ecs-v4-response-bits-15, ecs-v4-response-bits-16, ecs-v4-response-bits-17, ecs-v4-response-bits-18, ecs-v4-response-bits-19, ecs-v4-response-bits-20, ecs-v4-response-bits-21, ecs-v4-response-bits-22, ecs-v4-response-bits-23, ecs-v4-response-bits-24, ecs-v4-response-bits-25, ecs-v4-response-bits-26, ecs-v4-response-bits-27, ecs-v4-response-bits-28, ecs-v4-response-bits-29, ecs-v4-response-bits-30, ecs-v4-response-bits-31, ecs-v4-response-bits-32, ecs-v6-response-bits-1, ecs-v6-response-bits-2, ecs-v6-response-bits-3, ecs-v6-response-bits-4, ecs-v6-response-bits-5, ecs-v6-response-bits-6, ecs-v6-response-bits-7, ecs-v6-response-bits-8, ecs-v6-response-bits-9, ecs-v6-response-bits-10, ecs-v6-response-bits-11, ecs-v6-response-bits-12, ecs-v6-response-bits-13, ecs-v6-response-bits-14, ecs-v6-response-bits-15, ecs-v6-response-bits-16, ecs-v6-response-bits-17, ecs-v6-response-bits-18, ecs-v6-response-bits-19, ecs-v6-response-bits-20, ecs-v6-response-bits-21, ecs-v6-response-bits-22, ecs-v6-response-bits-23, ecs-v6-response-bits-24, ecs-v6-response-bits-25, ecs-v6-response-bits-26, ecs-v6-response-bits-27, ecs-v6-response-bits-28, ecs-v6-response-bits-29, ecs-v6-response-bits-30, ecs-v6-response-bits-31, ecs-v6-response-bits-32, ecs-v6-response-bits-33, ecs-v6-response-bits-34, ecs-v6-response-bits-35, ecs-v6-response-bits-36, ecs-v6-response-bits-37, ecs-v6-response-bits-38, ecs-v6-response-bits-39, ecs-v6-response-bits-40, ecs-v6-response-bits-41, ecs-v6-response-bits-42, ecs-v6-response-bits-43, ecs-v6-response-bits-44, ecs-v6-response-bits-45, ecs-v6-response-bits-46, ecs-v6-response-bits-47, ecs-v6-response-bits-48, ecs-v6-response-bits-49, ecs-v6-response-bits-50, ecs-v6-response-bits-51, ecs-v6-response-bits-52, ecs-v6-response-bits-53, ecs-v6-response-bits-54, ecs-v6-response-bits-55, ecs-v6-response-bits-56, ecs-v6-response-bits-57, ecs-v6-response-bits-58, ecs-v6-response-bits-59, ecs-v6-response-bits-60, ecs-v6-response-bits-61, ecs-v6-response-bits-62, ecs-v6-response-bits-63, ecs-v6-response-bits-64, ecs-v6-response-bits-65, ecs-v6-response-bits-66, ecs-v6-response-bits-67, ecs-v6-response-bits-68, ecs-v6-response-bits-69, ecs-v6-response-bits-70, ecs-v6-response-bits-71, ecs-v6-response-bits-72, ecs-v6-response-bits-73, ecs-v6-response-bits-74, ecs-v6-response-bits-75, ecs-v6-response-bits-76, ecs-v6-response-bits-77, ecs-v6-response-bits-78, ecs-v6-response-bits-79, ecs-v6-response-bits-80, ecs-v6-response-bits-81, ecs-v6-response-bits-82, ecs-v6-response-bits-83, ecs-v6-response-bits-84, ecs-v6-response-bits-85, ecs-v6-response-bits-86, ecs-v6-response-bits-87, ecs-v6-response-bits-88, ecs-v6-response-bits-89, ecs-v6-response-bits-90, ecs-v6-response-bits-91, ecs-v6-response-bits-92, ecs-v6-response-bits-93, ecs-v6-response-bits-94, ecs-v6-response-bits-95, ecs-v6-response-bits-96, ecs-v6-response-bits-97, ecs-v6-response-bits-98, ecs-v6-response-bits-99, ecs-v6-response-bits-100, ecs-v6-response-bits-101, ecs-v6-response-bits-102, ecs-v6-response-bits-103, ecs-v6-response-bits-104, ecs-v6-response-bits-105, ecs-v6-response-bits-106, ecs-v6-response-bits-107, ecs-v6-response-bits-108, ecs-v6-response-bits-109, ecs-v6-response-bits-110, ecs-v6-response-bits-111, ecs-v6-response-bits-112, ecs-v6-response-bits-113, ecs-v6-response-bits-114, ecs-v6-response-bits-115, ecs-v6-response-bits-116, ecs-v6-response-bits-117, ecs-v6-response-bits-118, ecs-v6-response-bits-119, ecs-v6-response-bits-120, ecs-v6-response-bits-121, ecs-v6-response-bits-122, ecs-v6-response-bits-123, ecs-v6-response-bits-124, ecs-v6-response-bits-125, ecs-v6-response-bits-126, ecs-v6-response-bits-127, ecs-v6-response-bits-128, cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count',
- 'docdefault': 'cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-\\*, ecs-v6-response-bits-\\*',
- 'help' : 'List of statistics that are prevented from being exported via SNMP',
- 'doc' : '''
+ "name": "stats_snmp_disabled_list",
+ "section": "recursor",
+ "type": LType.ListStrings,
+ "default": "cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-1, ecs-v4-response-bits-2, ecs-v4-response-bits-3, ecs-v4-response-bits-4, ecs-v4-response-bits-5, ecs-v4-response-bits-6, ecs-v4-response-bits-7, ecs-v4-response-bits-8, ecs-v4-response-bits-9, ecs-v4-response-bits-10, ecs-v4-response-bits-11, ecs-v4-response-bits-12, ecs-v4-response-bits-13, ecs-v4-response-bits-14, ecs-v4-response-bits-15, ecs-v4-response-bits-16, ecs-v4-response-bits-17, ecs-v4-response-bits-18, ecs-v4-response-bits-19, ecs-v4-response-bits-20, ecs-v4-response-bits-21, ecs-v4-response-bits-22, ecs-v4-response-bits-23, ecs-v4-response-bits-24, ecs-v4-response-bits-25, ecs-v4-response-bits-26, ecs-v4-response-bits-27, ecs-v4-response-bits-28, ecs-v4-response-bits-29, ecs-v4-response-bits-30, ecs-v4-response-bits-31, ecs-v4-response-bits-32, ecs-v6-response-bits-1, ecs-v6-response-bits-2, ecs-v6-response-bits-3, ecs-v6-response-bits-4, ecs-v6-response-bits-5, ecs-v6-response-bits-6, ecs-v6-response-bits-7, ecs-v6-response-bits-8, ecs-v6-response-bits-9, ecs-v6-response-bits-10, ecs-v6-response-bits-11, ecs-v6-response-bits-12, ecs-v6-response-bits-13, ecs-v6-response-bits-14, ecs-v6-response-bits-15, ecs-v6-response-bits-16, ecs-v6-response-bits-17, ecs-v6-response-bits-18, ecs-v6-response-bits-19, ecs-v6-response-bits-20, ecs-v6-response-bits-21, ecs-v6-response-bits-22, ecs-v6-response-bits-23, ecs-v6-response-bits-24, ecs-v6-response-bits-25, ecs-v6-response-bits-26, ecs-v6-response-bits-27, ecs-v6-response-bits-28, ecs-v6-response-bits-29, ecs-v6-response-bits-30, ecs-v6-response-bits-31, ecs-v6-response-bits-32, ecs-v6-response-bits-33, ecs-v6-response-bits-34, ecs-v6-response-bits-35, ecs-v6-response-bits-36, ecs-v6-response-bits-37, ecs-v6-response-bits-38, ecs-v6-response-bits-39, ecs-v6-response-bits-40, ecs-v6-response-bits-41, ecs-v6-response-bits-42, ecs-v6-response-bits-43, ecs-v6-response-bits-44, ecs-v6-response-bits-45, ecs-v6-response-bits-46, ecs-v6-response-bits-47, ecs-v6-response-bits-48, ecs-v6-response-bits-49, ecs-v6-response-bits-50, ecs-v6-response-bits-51, ecs-v6-response-bits-52, ecs-v6-response-bits-53, ecs-v6-response-bits-54, ecs-v6-response-bits-55, ecs-v6-response-bits-56, ecs-v6-response-bits-57, ecs-v6-response-bits-58, ecs-v6-response-bits-59, ecs-v6-response-bits-60, ecs-v6-response-bits-61, ecs-v6-response-bits-62, ecs-v6-response-bits-63, ecs-v6-response-bits-64, ecs-v6-response-bits-65, ecs-v6-response-bits-66, ecs-v6-response-bits-67, ecs-v6-response-bits-68, ecs-v6-response-bits-69, ecs-v6-response-bits-70, ecs-v6-response-bits-71, ecs-v6-response-bits-72, ecs-v6-response-bits-73, ecs-v6-response-bits-74, ecs-v6-response-bits-75, ecs-v6-response-bits-76, ecs-v6-response-bits-77, ecs-v6-response-bits-78, ecs-v6-response-bits-79, ecs-v6-response-bits-80, ecs-v6-response-bits-81, ecs-v6-response-bits-82, ecs-v6-response-bits-83, ecs-v6-response-bits-84, ecs-v6-response-bits-85, ecs-v6-response-bits-86, ecs-v6-response-bits-87, ecs-v6-response-bits-88, ecs-v6-response-bits-89, ecs-v6-response-bits-90, ecs-v6-response-bits-91, ecs-v6-response-bits-92, ecs-v6-response-bits-93, ecs-v6-response-bits-94, ecs-v6-response-bits-95, ecs-v6-response-bits-96, ecs-v6-response-bits-97, ecs-v6-response-bits-98, ecs-v6-response-bits-99, ecs-v6-response-bits-100, ecs-v6-response-bits-101, ecs-v6-response-bits-102, ecs-v6-response-bits-103, ecs-v6-response-bits-104, ecs-v6-response-bits-105, ecs-v6-response-bits-106, ecs-v6-response-bits-107, ecs-v6-response-bits-108, ecs-v6-response-bits-109, ecs-v6-response-bits-110, ecs-v6-response-bits-111, ecs-v6-response-bits-112, ecs-v6-response-bits-113, ecs-v6-response-bits-114, ecs-v6-response-bits-115, ecs-v6-response-bits-116, ecs-v6-response-bits-117, ecs-v6-response-bits-118, ecs-v6-response-bits-119, ecs-v6-response-bits-120, ecs-v6-response-bits-121, ecs-v6-response-bits-122, ecs-v6-response-bits-123, ecs-v6-response-bits-124, ecs-v6-response-bits-125, ecs-v6-response-bits-126, ecs-v6-response-bits-127, ecs-v6-response-bits-128, cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count",
+ "docdefault": "cache-bytes, packetcache-bytes, special-memory-usage, ecs-v4-response-bits-\\*, ecs-v6-response-bits-\\*",
+ "help": "List of statistics that are prevented from being exported via SNMP",
+ "doc": """
A list of comma-separated statistic names, that are prevented from being exported via SNMP, for performance reasons.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
A sequence of statistic names, that are prevented from being exported via SNMP, for performance reasons.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'structured_logging',
- 'section' : 'logging',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Prefer structured logging',
- 'doc' : '''
+ "name": "structured_logging",
+ "section": "logging",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Prefer structured logging",
+ "doc": """
Prefer structured logging when both an old style and a structured log message are available.
- ''',
- 'versionadded': '4.6.0',
- 'versionchanged': [('5.0.0', 'Disabling structured logging is deprecated'),
- ('5.1.0', 'Disabling structured logging is not supported')]
- },
- {
- 'name' : 'structured_logging_backend',
- 'section' : 'logging',
- 'type' : LType.String,
- 'default' : 'default',
- 'help' : 'Structured logging backend',
- 'doc' : '''
+ """,
+ "versionadded": "4.6.0",
+ "versionchanged": [
+ ("5.0.0", "Disabling structured logging is deprecated"),
+ ("5.1.0", "Disabling structured logging is not supported"),
+ ],
+ },
+ {
+ "name": "structured_logging_backend",
+ "section": "logging",
+ "type": LType.String,
+ "default": "default",
+ "help": "Structured logging backend",
+ "doc": """
The backend used for structured logging output.
This setting must be set on the command line (``--structured-logging-backend=...``) to be effective.
Available backends are:
- ``json``: JSON objects are written to the standard error stream.
See :doc:`appendices/structuredlogging` for more details.
- ''',
- 'versionadded': '4.8.0',
- 'versionchanged': ('5.1.0', 'The JSON backend was added')
+ """,
+ "versionadded": "4.8.0",
+ "versionchanged": ("5.1.0", "The JSON backend was added"),
},
{
- 'name' : 'tcp_fast_open',
- 'section' : 'incoming',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size',
- 'doc' : '''
+ "name": "tcp_fast_open",
+ "section": "incoming",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size",
+ "doc": """
Enable TCP Fast Open support, if available, on the listening sockets.
The numerical value supplied is used as the queue size, 0 meaning disabled. See :ref:`tcp-fast-open-support`.
- ''',
- 'versionadded': '4.1.0'
+ """,
+ "versionadded": "4.1.0",
},
{
- 'name' : 'tcp_fast_open_connect',
- 'section' : 'outgoing',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Enable TCP Fast Open support on outgoing sockets',
- 'doc' : '''
+ "name": "tcp_fast_open_connect",
+ "section": "outgoing",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Enable TCP Fast Open support on outgoing sockets",
+ "doc": """
Enable TCP Fast Open Connect support, if available, on the outgoing connections to authoritative servers. See :ref:`tcp-fast-open-support`.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'tcp_max_idle_ms',
- 'section' : 'outgoing',
- 'oldname' : 'tcp-out-max-idle-ms',
- 'type' : LType.Uint64,
- 'default' : '10000',
- 'help' : 'Time TCP/DoT connections are left idle in milliseconds or 0 if no limit',
- 'doc' : '''
+ "name": "tcp_max_idle_ms",
+ "section": "outgoing",
+ "oldname": "tcp-out-max-idle-ms",
+ "type": LType.Uint64,
+ "default": "10000",
+ "help": "Time TCP/DoT connections are left idle in milliseconds or 0 if no limit",
+ "doc": """
Time outgoing TCP/DoT connections are left idle in milliseconds or 0 if no limit. After having been idle for this time, the connection is eligible for closing.
- ''',
- 'versionadded': '4.6.0'
+ """,
+ "versionadded": "4.6.0",
},
{
- 'name' : 'tcp_max_idle_per_auth',
- 'section' : 'outgoing',
- 'oldname' : 'tcp-out-max-idle-per-auth',
- 'type' : LType.Uint64,
- 'default' : '10',
- 'help' : 'Maximum number of idle TCP/DoT connections to a specific IP per thread, 0 means do not keep idle connections open',
- 'doc' : '''
+ "name": "tcp_max_idle_per_auth",
+ "section": "outgoing",
+ "oldname": "tcp-out-max-idle-per-auth",
+ "type": LType.Uint64,
+ "default": "10",
+ "help": "Maximum number of idle TCP/DoT connections to a specific IP per thread, 0 means do not keep idle connections open",
+ "doc": """
Maximum number of idle outgoing TCP/DoT connections to a specific IP per thread, 0 means do not keep idle connections open.
- ''',
- 'versionadded': '4.6.0'
+ """,
+ "versionadded": "4.6.0",
},
{
- 'name' : 'tcp_max_queries',
- 'section' : 'outgoing',
- 'oldname' : 'tcp-out-max-queries',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'Maximum total number of queries per TCP/DoT connection, 0 means no limit',
- 'doc' : '''
+ "name": "tcp_max_queries",
+ "section": "outgoing",
+ "oldname": "tcp-out-max-queries",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "Maximum total number of queries per TCP/DoT connection, 0 means no limit",
+ "doc": """
Maximum total number of queries per outgoing TCP/DoT connection, 0 means no limit. After this number of queries, the connection is
closed and a new one will be created if needed.
- ''',
+ """,
},
{
- 'name' : 'tcp_max_idle_per_thread',
- 'section' : 'outgoing',
- 'oldname' : 'tcp-out-max-idle-per-thread',
- 'type' : LType.Uint64,
- 'default' : '100',
- 'help' : 'Maximum number of idle TCP/DoT connections per thread',
- 'doc' : '''
+ "name": "tcp_max_idle_per_thread",
+ "section": "outgoing",
+ "oldname": "tcp-out-max-idle-per-thread",
+ "type": LType.Uint64,
+ "default": "100",
+ "help": "Maximum number of idle TCP/DoT connections per thread",
+ "doc": """
Maximum number of idle outgoing TCP/DoT connections per thread, 0 means do not keep idle connections open.
- ''',
- 'versionadded': '4.6.0'
+ """,
+ "versionadded": "4.6.0",
},
{
- 'name' : 'threads',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '2',
- 'help' : 'Launch this number of threads',
- 'doc' : '''
+ "name": "threads",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "2",
+ "help": "Launch this number of threads",
+ "doc": """
Spawn this number of threads on startup.
- ''',
+ """,
},
{
- 'name' : 'tcp_threads',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '1',
- 'help' : 'Launch this number of threads listening for and processing TCP queries',
- 'doc' : '''
+ "name": "tcp_threads",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "1",
+ "help": "Launch this number of threads listening for and processing TCP queries",
+ "doc": """
Spawn this number of TCP processing threads on startup.
- ''',
- 'versionadded': '5.0.0'
+ """,
+ "versionadded": "5.0.0",
},
{
- 'name' : 'trace',
- 'section' : 'logging',
- 'type' : LType.String,
- 'default' : 'no',
- 'help' : 'if we should output heaps of logging. set to \'fail\' to only log failing domains',
- 'doc' : '''
+ "name": "trace",
+ "section": "logging",
+ "type": LType.String,
+ "default": "no",
+ "help": "if we should output heaps of logging. set to 'fail' to only log failing domains",
+ "doc": """
One of ``no``, ``yes`` or ``fail``.
If turned on, output impressive heaps of logging.
May destroy performance under load.
To log only queries resulting in a ``ServFail`` answer from the resolving process, this value can be set to ``fail``, but note that the performance impact is still large.
Also note that queries that do produce a result but with a failing DNSSEC validation are not written to the log
- ''',
+ """,
},
{
- 'name' : 'udp_source_port_min',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '1024',
- 'help' : 'Minimum UDP port to bind on',
- 'doc' : '''
+ "name": "udp_source_port_min",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "1024",
+ "help": "Minimum UDP port to bind on",
+ "doc": """
This option sets the low limit of UDP port number to bind on.
In combination with :ref:`setting-udp-source-port-max` it configures the UDP
port range to use. Port numbers are randomized within this range on
initialization, and exceptions can be configured with :ref:`setting-udp-source-port-avoid`
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'udp_source_port_max',
- 'section' : 'outgoing',
- 'type' : LType.Uint64,
- 'default' : '65535',
- 'help' : 'Maximum UDP port to bind on',
- 'doc' : '''
+ "name": "udp_source_port_max",
+ "section": "outgoing",
+ "type": LType.Uint64,
+ "default": "65535",
+ "help": "Maximum UDP port to bind on",
+ "doc": """
This option sets the maximum limit of UDP port number to bind on.
See :ref:`setting-udp-source-port-min`.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'udp_source_port_avoid',
- 'section' : 'outgoing',
- 'type' : LType.ListStrings,
- 'default' : '4791,11211',
- 'help' : 'List of comma separated UDP port numbers to avoid',
- 'doc' : '''
+ "name": "udp_source_port_avoid",
+ "section": "outgoing",
+ "type": LType.ListStrings,
+ "default": "4791,11211",
+ "help": "List of comma separated UDP port numbers to avoid",
+ "doc": """
A list of comma-separated UDP port numbers to avoid when binding.
Ex: `4791,5300,11211`
See :ref:`setting-udp-source-port-min`.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
A sequence of UDP port numbers to avoid when binding. For example:
.. code-block:: yaml
- 11211
See :ref:`setting-udp-source-port-min`.
- ''',
- 'versionadded': '4.2.0',
- 'versionchanged': ('5.2.0', 'port 4791 was added to the default list'),
+ """,
+ "versionadded": "4.2.0",
+ "versionchanged": ("5.2.0", "port 4791 was added to the default list"),
},
{
- 'name' : 'udp_truncation_threshold',
- 'section' : 'incoming',
- 'type' : LType.Uint64,
- 'default' : '1232',
- 'help' : 'Maximum UDP response size before we truncate',
- 'doc' : '''
+ "name": "udp_truncation_threshold",
+ "section": "incoming",
+ "type": LType.Uint64,
+ "default": "1232",
+ "help": "Maximum UDP response size before we truncate",
+ "doc": """
EDNS0 allows for large UDP response datagrams, which can potentially raise performance.
Large responses however also have downsides in terms of reflection attacks.
This setting limits the accepted size.
Maximum value is 65535, but values above 4096 should probably not be attempted.
To know why 1232, see the note at :ref:`setting-edns-outgoing-bufsize`.
- ''',
- 'versionchanged': ('4.2.0', 'Before 4.2.0, the default was 1680.')
+ """,
+ "versionchanged": ("4.2.0", "Before 4.2.0, the default was 1680."),
},
{
- 'name' : 'unique_response_tracking',
- 'section' : 'nod',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Track unique responses (tuple of query name, type and RR).',
- 'doc' : '''
+ "name": "unique_response_tracking",
+ "section": "nod",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Track unique responses (tuple of query name, type and RR).",
+ "doc": """
Whether to track unique DNS responses, i.e. never seen before combinations
of the triplet (query name, query type, RR[rrname, rrtype, rrdata]).
This can be useful for tracking potentially suspicious domains and
track unique responses, which can have false positives as well as false
negatives, thus it is a best-effort feature. Increasing the number of cells
in the SBF using the unique-response-db-size setting can reduce FPs and FNs.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'unique_response_log',
- 'section' : 'nod',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Log unique responses',
- 'doc' : '''
+ "name": "unique_response_log",
+ "section": "nod",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Log unique responses",
+ "doc": """
Whether to log when a unique response is detected. The log line
looks something like:
Oct 24 12:11:27 Unique response observed: qname=foo.com qtype=A rrtype=AAAA rrname=foo.com rrcontent=1.2.3.4
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'unique_response_db_size',
- 'section' : 'nod',
- 'type' : LType.Uint64,
- 'default' : '67108864',
- 'help' : 'Size of the DB used to track unique responses in terms of number of cells. Defaults to 67108864',
- 'doc' : '''
+ "name": "unique_response_db_size",
+ "section": "nod",
+ "type": LType.Uint64,
+ "default": "67108864",
+ "help": "Size of the DB used to track unique responses in terms of number of cells. Defaults to 67108864",
+ "doc": """
The default size of the stable bloom filter used to store previously
observed responses is 67108864. To change the number of cells, use this
setting. For each cell, the SBF uses 1 bit of memory, and one byte of
disk for the persistent file.
If there are already persistent files saved to disk, this setting will
have no effect unless you remove the existing files.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'unique_response_history_dir',
- 'section' : 'nod',
- 'type' : LType.String,
- 'default' : 'NODCACHEDIRUDR',
- 'docdefault': 'Determined by distribution',
- 'help' : 'Persist unique response tracking data here to persist between restarts',
- 'doc' : '''
+ "name": "unique_response_history_dir",
+ "section": "nod",
+ "type": LType.String,
+ "default": "NODCACHEDIRUDR",
+ "docdefault": "Determined by distribution",
+ "help": "Persist unique response tracking data here to persist between restarts",
+ "doc": """
This setting controls which directory is used to store the on-disk
cache of previously observed responses.
disk on startup. This ensures that previously observed responses are
preserved across recursor restarts. If you change the
unique-response-db-size, you must remove any files from this directory.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'unique_response_pb_tag',
- 'section' : 'nod',
- 'type' : LType.String,
- 'default' : 'pdns-udr',
- 'help' : 'If protobuf is configured, the tag to use for messages containing unique DNS responses. Defaults to \'pdns-udr\'',
- 'doc' : '''
+ "name": "unique_response_pb_tag",
+ "section": "nod",
+ "type": LType.String,
+ "default": "pdns-udr",
+ "help": "If protobuf is configured, the tag to use for messages containing unique DNS responses. Defaults to 'pdns-udr'",
+ "doc": """
If protobuf is configured, then this tag will be added to all protobuf response messages when
a unique DNS response is observed.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'unique_response_ignore_list',
- 'section' : 'nod',
- 'type' : LType.ListStrings,
- 'default' : '',
- 'help' : 'List of domains (and implicitly all subdomains) which will never be considered for UDR',
- 'doc' : '''
+ "name": "unique_response_ignore_list",
+ "section": "nod",
+ "type": LType.ListStrings,
+ "default": "",
+ "help": "List of domains (and implicitly all subdomains) which will never be considered for UDR",
+ "doc": """
This setting is a list of all domains (and implicitly all subdomains)
that will never be considered for new unique domain responses.
For example, if the domain 'example.com' is in the list, then 'foo.bar.example.com'
will never be considered for a new unique domain response.
-''',
- 'versionadded': '5.1.0'
+""",
+ "versionadded": "5.1.0",
},
{
- 'name' : 'unique_response_ignore_list_file',
- 'section' : 'nod',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'File with list of domains (and implicitly all subdomains) which will never be considered for UDR',
- 'doc' : '''
+ "name": "unique_response_ignore_list_file",
+ "section": "nod",
+ "type": LType.String,
+ "default": "",
+ "help": "File with list of domains (and implicitly all subdomains) which will never be considered for UDR",
+ "doc": """
Path to a file with a list of domains. File should have one domain per line,
with no extra characters or comments.
See :ref:`setting-unique-response-ignore-list`.
-''',
- 'versionadded': '5.1.0'
+""",
+ "versionadded": "5.1.0",
},
{
- 'name' : 'use_incoming_edns_subnet',
- 'section' : 'incoming',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Pass along received EDNS Client Subnet information',
- 'doc' : '''
+ "name": "use_incoming_edns_subnet",
+ "section": "incoming",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Pass along received EDNS Client Subnet information",
+ "doc": """
Whether to process and pass along a received EDNS Client Subnet to authoritative servers.
The ECS information will only be sent for netmasks and domains listed in :ref:`setting-edns-subnet-allow-list` and will be truncated if the received scope exceeds :ref:`setting-ecs-ipv4-bits` for IPv4 or :ref:`setting-ecs-ipv6-bits` for IPv6.
- ''',
+ """,
},
{
- 'name' : 'version',
- 'section' : 'commands',
- 'type' : LType.Command,
- 'default' : 'no',
- 'help' : 'Print version string',
- 'doc' : '''
+ "name": "version",
+ "section": "commands",
+ "type": LType.Command,
+ "default": "no",
+ "help": "Print version string",
+ "doc": """
Print version of this binary. Useful for checking which version of the PowerDNS recursor is installed on a system.
- ''',
+ """,
},
{
- 'name' : 'version_string',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : RUNTIME,
- 'help' : 'string reported on version.pdns or version.bind',
- 'doc' : '''
+ "name": "version_string",
+ "section": "recursor",
+ "type": LType.String,
+ "default": RUNTIME,
+ "help": "string reported on version.pdns or version.bind",
+ "doc": """
By default, PowerDNS replies to the 'version.bind' query with its version number.
Security conscious users may wish to override the reply PowerDNS issues.
- ''',
+ """,
},
{
- 'name' : 'webserver',
- 'section' : 'webservice',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Start a webserver (for REST API)',
- 'doc' : '''
+ "name": "webserver",
+ "section": "webservice",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Start a webserver (for REST API)",
+ "doc": """
Start the webserver (for REST API).
- ''',
+ """,
},
{
- 'name' : 'address',
- 'section' : 'webservice',
- 'oldname' : 'webserver-address',
- 'type' : LType.String,
- 'default' : '127.0.0.1',
- 'help' : 'IP Address of webserver to listen on',
- 'doc' : '''
+ "name": "address",
+ "section": "webservice",
+ "oldname": "webserver-address",
+ "type": LType.String,
+ "default": "127.0.0.1",
+ "help": "IP Address of webserver to listen on",
+ "doc": """
IP address for the webserver to listen on.
-''',
- 'doc-new' : '''
+""",
+ "doc-new": """
IP address for the webserver to listen on.
This field is ignored if :ref:`setting-yaml-webservice.listen` is set.
-''',
+""",
},
{
- 'name' : 'listen',
- 'section' : 'webservice',
- 'type' : LType.ListIncomingWSConfigs,
- 'default' : '',
- 'help' : 'IP addresses and associated attributes for the webserver to listen on',
- 'doc' : '''
+ "name": "listen",
+ "section": "webservice",
+ "type": LType.ListIncomingWSConfigs,
+ "default": "",
+ "help": "IP addresses and associated attributes for the webserver to listen on",
+ "doc": """
IP addresses and associated attributes for the webserver to listen on.
If this setting has a non-default value, :ref:`setting-yaml-webservice.address` and :ref:`setting-yaml-webservice.port` will be ignored. Note multiple listen addresses can be configured and https is supported as well, in contrast to earlier (pre 5.3.0) versions.
- ''',
- 'skip-old': 'No equivalent old-style setting',
- 'versionadded': '5.3.0',
- },
- {
- 'name' : 'allow_from',
- 'section' : 'webservice',
- 'oldname' : 'webserver-allow-from',
- 'type' : LType.ListSubnets,
- 'default' : '127.0.0.1, ::1',
- 'help' : 'Webserver access is only allowed from these subnets',
- 'doc' : '''
+ """,
+ "skip-old": "No equivalent old-style setting",
+ "versionadded": "5.3.0",
+ },
+ {
+ "name": "allow_from",
+ "section": "webservice",
+ "oldname": "webserver-allow-from",
+ "type": LType.ListSubnets,
+ "default": "127.0.0.1, ::1",
+ "help": "Webserver access is only allowed from these subnets",
+ "doc": """
These IPs and subnets are allowed to access the webserver. Note that
specifying an IP address without a netmask uses an implicit netmask
of /32 or /128.
- ''',
- 'versionchanged': ('4.1.0', 'Default is now 127.0.0.1,::1, was 0.0.0.0/0,::/0 before.')
+ """,
+ "versionchanged": ("4.1.0", "Default is now 127.0.0.1,::1, was 0.0.0.0/0,::/0 before."),
},
{
- 'name' : 'hash_plaintext_credentials',
- 'section' : 'webservice',
- 'oldname': 'webserver-hash-plaintext-credentials',
- 'type' : LType.Bool,
- 'default' : 'false',
- 'help' : 'Whether to hash passwords and api keys supplied in plaintext, to prevent keeping the plaintext version in memory at runtime',
- 'doc' : '''
+ "name": "hash_plaintext_credentials",
+ "section": "webservice",
+ "oldname": "webserver-hash-plaintext-credentials",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Whether to hash passwords and api keys supplied in plaintext, to prevent keeping the plaintext version in memory at runtime",
+ "doc": """
Whether passwords and API keys supplied in the configuration as plaintext should be hashed during startup, to prevent the plaintext versions from staying in memory. Doing so increases significantly the cost of verifying credentials and is thus disabled by default.
Note that this option only applies to credentials stored in the configuration as plaintext, but hashed credentials are supported without enabling this option.
- ''',
- 'versionadded': '4.6.0'
+ """,
+ "versionadded": "4.6.0",
},
{
- 'name' : 'loglevel',
- 'section' : 'webservice',
- 'oldname' : 'webserver-loglevel',
- 'type' : LType.String,
- 'default' : 'normal',
- 'help' : 'Amount of logging in the webserver (none, normal, detailed)',
- 'doc' : '''
+ "name": "loglevel",
+ "section": "webservice",
+ "oldname": "webserver-loglevel",
+ "type": LType.String,
+ "default": "normal",
+ "help": "Amount of logging in the webserver (none, normal, detailed)",
+ "doc": """
One of ``none``, ``normal``, ``detailed``.
The amount of logging the webserver must do. ``none`` means no useful webserver information will be logged.
When set to ``normal``, the webserver will log a line per request::
.. note::
The webserver logs these line on the NOTICE level. The :ref:`setting-loglevel` setting must be 5 or higher for these lines to end up in the log.
- ''',
- 'versionadded': '4.2.0'
+ """,
+ "versionadded": "4.2.0",
},
{
- 'name' : 'password',
- 'section' : 'webservice',
- 'oldname' : 'webserver-password',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Password required for accessing the webserver',
- 'doc' : '''
+ "name": "password",
+ "section": "webservice",
+ "oldname": "webserver-password",
+ "type": LType.String,
+ "default": "",
+ "help": "Password required for accessing the webserver",
+ "doc": """
Password required to access the webserver. Since 4.6.0 the password can be hashed and salted using ``rec_control hash-password`` instead of being present in the configuration in plaintext, but the plaintext version is still supported.
- ''',
- 'versionchanged': ('4.6.0', 'This setting now accepts a hashed and salted version.')
+ """,
+ "versionchanged": ("4.6.0", "This setting now accepts a hashed and salted version."),
},
{
- 'name' : 'port',
- 'section' : 'webservice',
- 'type' : LType.Uint64,
- 'oldname': 'webserver-port',
- 'default' : '8082',
- 'help' : 'Port of webserver to listen on',
- 'doc' : '''
+ "name": "port",
+ "section": "webservice",
+ "type": LType.Uint64,
+ "oldname": "webserver-port",
+ "default": "8082",
+ "help": "Port of webserver to listen on",
+ "doc": """
TCP port where the webserver should listen on.
- ''',
- 'doc-new' : '''
+ """,
+ "doc-new": """
TCP port where the webserver should listen on.
This field is ignored if :ref:`setting-yaml-webservice.listen` is set.
- ''',
+ """,
},
{
- 'name' : 'write_pid',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Write a PID file',
- 'doc' : '''
+ "name": "write_pid",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Write a PID file",
+ "doc": """
If a PID file should be written to :ref:`setting-socket-dir`
- ''',
+ """,
},
{
- 'name' : 'x_dnssec_names',
- 'section' : 'dnssec',
- 'type' : LType.ListStrings,
- 'default' : '',
- 'help' : 'Collect DNSSEC statistics for names or suffixes in this list in separate x-dnssec counters',
- 'doc' : '''
+ "name": "x_dnssec_names",
+ "section": "dnssec",
+ "type": LType.ListStrings,
+ "default": "",
+ "help": "Collect DNSSEC statistics for names or suffixes in this list in separate x-dnssec counters",
+ "doc": """
List of names whose DNSSEC validation metrics will be counted in a separate set of metrics that start
with ``x-dnssec-result-``.
The names are suffix-matched.
This can be used to not count known failing (test) name validations in the ordinary DNSSEC metrics.
- ''',
- 'versionadded': '4.5.0'
+ """,
+ "versionadded": "4.5.0",
},
{
- 'name' : 'system_resolver_ttl',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'Set TTL of system resolver feature, 0 (default) is disabled',
- 'doc' : '''
+ "name": "system_resolver_ttl",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "Set TTL of system resolver feature, 0 (default) is disabled",
+ "doc": """
Sets TTL in seconds of the system resolver feature.
If not equal to zero names can be used for forwarding targets.
The names will be resolved by the system resolver configured in the OS.
Make sure the recursor itself is not used by the system resolver! Default is 0 (not enabled).
A suggested value is 60.
-''',
- 'versionadded': '5.1.0'
+""",
+ "versionadded": "5.1.0",
},
{
- 'name' : 'system_resolver_interval',
- 'section' : 'recursor',
- 'type' : LType.Uint64,
- 'default' : '0',
- 'help' : 'Set interval (in seconds) of the re-resolve checks of system resolver subsystem.',
- 'doc' : '''
+ "name": "system_resolver_interval",
+ "section": "recursor",
+ "type": LType.Uint64,
+ "default": "0",
+ "help": "Set interval (in seconds) of the re-resolve checks of system resolver subsystem.",
+ "doc": """
Sets the check interval (in seconds) of the system resolver feature.
All names known by the system resolver subsystem are periodically checked for changing values.
This settings sets the interval between the checks.
If set to zero (the default), the value :ref:`setting-system-resolver-ttl` is used.
-''',
- 'versionadded': '5.1.0'
+""",
+ "versionadded": "5.1.0",
},
{
- 'name' : 'system_resolver_self_resolve_check',
- 'section' : 'recursor',
- 'type' : LType.Bool,
- 'default' : 'true',
- 'help' : 'Check for potential self-resolve, default enabled.',
- 'doc' : '''
+ "name": "system_resolver_self_resolve_check",
+ "section": "recursor",
+ "type": LType.Bool,
+ "default": "true",
+ "help": "Check for potential self-resolve, default enabled.",
+ "doc": """
Warn on potential self-resolve.
If this check draws the wrong conclusion, you can disable it.
-''',
- 'versionadded': '5.1.0'
+""",
+ "versionadded": "5.1.0",
},
{
- 'name' : 'trustanchors',
- 'section' : 'dnssec',
- 'type' : LType.ListTrustAnchors,
- 'default' : '[{name: ., dsrecords: [\'20326 8 2 e06d44b80b8f1d39a95c0b0d7c65d08458e880409bbc683457104237c7f8ec8d\', \'38696 8 2 683d2d0acb8c9b712a1948b27f741219298d0a450d612c483af444a4c0fb2b16\']}]',
- 'docdefault' : '''
+ "name": "trustanchors",
+ "section": "dnssec",
+ "type": LType.ListTrustAnchors,
+ "default": "[{name: ., dsrecords: ['20326 8 2 e06d44b80b8f1d39a95c0b0d7c65d08458e880409bbc683457104237c7f8ec8d', '38696 8 2 683d2d0acb8c9b712a1948b27f741219298d0a450d612c483af444a4c0fb2b16']}]",
+ "docdefault": """
.. code-block:: yaml
- 20326 8 2 e06d44b80b8f1d39a95c0b0d7c65d08458e880409bbc683457104237c7f8ec8d
- 38696 8 2 683d2d0acb8c9b712a1948b27f741219298d0a450d612c483af444a4c0fb2b16
-''',
- 'help' : 'Sequence of trust anchors',
- 'doc' : '''
+""",
+ "help": "Sequence of trust anchors",
+ "doc": """
Sequence of trust anchors. If the sequence contains an entry for the root zone, the default root zone trust anchor is not included.
If a zone appears multiple times, the entries in ``dsrecords`` are merged.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/dnssec`',
- 'versionadded': '5.1.0',
- 'runtime': ['add-ta', 'clear-ta', 'reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'negative_trustanchors',
- 'section' : 'dnssec',
- 'type' : LType.ListNegativeTrustAnchors,
- 'default' : '',
- 'help' : 'A sequence of negative trust anchors',
- 'doc' : '''
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/dnssec`",
+ "versionadded": "5.1.0",
+ "runtime": ["add-ta", "clear-ta", "reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "negative_trustanchors",
+ "section": "dnssec",
+ "type": LType.ListNegativeTrustAnchors,
+ "default": "",
+ "help": "A sequence of negative trust anchors",
+ "doc": """
Sequence of negative trust anchors.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/dnssec`',
- 'versionadded': '5.1.0',
- 'runtime': ['add-nta', 'clear-nta', 'reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'trustanchorfile',
- 'section' : 'dnssec',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'A path to a zone file containing trust anchors',
- 'doc' : '''
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/dnssec`",
+ "versionadded": "5.1.0",
+ "runtime": ["add-nta", "clear-nta", "reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "trustanchorfile",
+ "section": "dnssec",
+ "type": LType.String,
+ "default": "",
+ "help": "A path to a zone file containing trust anchors",
+ "doc": """
A path to a zone file to read trust anchors from.
This can be used to read distribution provided trust anchors, as for instance ``/usr/share/dns/root.key`` from Debian's ``dns-root-data`` package.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/dnssec`',
- 'versionadded': '5.1.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'trustanchorfile_interval',
- 'section' : 'dnssec',
- 'type' : LType.Uint64,
- 'default' : '24',
- 'help' : 'Interval (in hours) to read the trust anchors file',
- 'doc' : '''
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/dnssec`",
+ "versionadded": "5.1.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "trustanchorfile_interval",
+ "section": "dnssec",
+ "type": LType.Uint64,
+ "default": "24",
+ "help": "Interval (in hours) to read the trust anchors file",
+ "doc": """
Interval (in hours) to re-read the ``trustanchorfile``. Zero disables periodic re-reads.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/dnssec`',
- 'versionadded': '5.1.0',
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/dnssec`",
+ "versionadded": "5.1.0",
},
{
- 'name' : 'protobuf_servers',
- 'section' : 'logging',
- 'type' : LType.ListProtobufServers,
- 'default' : '',
- 'help' : 'Sequence of protobuf servers',
- 'doc' : '''
+ "name": "protobuf_servers",
+ "section": "logging",
+ "type": LType.ListProtobufServers,
+ "default": "",
+ "help": "Sequence of protobuf servers",
+ "doc": """
Sequence of outgoing protobuf servers. Currently the maximum size of this list is one.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/protobuf`',
- 'versionadded': '5.1.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'outgoing_protobuf_servers',
- 'section' : 'logging',
- 'type' : LType.ListProtobufServers,
- 'default' : '',
- 'help' : 'List of outgoing protobuf servers',
- 'doc' : '''
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/protobuf`",
+ "versionadded": "5.1.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "outgoing_protobuf_servers",
+ "section": "logging",
+ "type": LType.ListProtobufServers,
+ "default": "",
+ "help": "List of outgoing protobuf servers",
+ "doc": """
Sequence of outgoing protobuf servers. Currently the maximum size of this list is one.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/protobuf`',
- 'versionadded': '5.1.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'protobuf_mask_v4',
- 'section' : 'logging',
- 'type' : LType.Uint64,
- 'default' : '32',
- 'help' : 'Network mask to apply for client IPv4 addresses in protobuf messages',
- 'doc' : '''
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/protobuf`",
+ "versionadded": "5.1.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "protobuf_mask_v4",
+ "section": "logging",
+ "type": LType.Uint64,
+ "default": "32",
+ "help": "Network mask to apply for client IPv4 addresses in protobuf messages",
+ "doc": """
Network mask to apply to the client IPv4 addresses, for anonymization purposes. The default of 32 means no anonymization.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/protobuf`',
- 'versionadded': '5.1.0',
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/protobuf`",
+ "versionadded": "5.1.0",
},
{
- 'name' : 'protobuf_mask_v6',
- 'section' : 'logging',
- 'type' : LType.Uint64,
- 'default' : '128',
- 'help' : 'Network mask to apply for client IPv6 addresses in protobuf messages',
- 'doc' : '''
+ "name": "protobuf_mask_v6",
+ "section": "logging",
+ "type": LType.Uint64,
+ "default": "128",
+ "help": "Network mask to apply for client IPv6 addresses in protobuf messages",
+ "doc": """
Network mask to apply to the client IPv6 addresses, for anonymization purposes. The default of 128 means no anonymization.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/protobuf`',
- 'versionadded': '5.1.0',
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/protobuf`",
+ "versionadded": "5.1.0",
},
{
- 'name' : 'dnstap_framestream_servers',
- 'section' : 'logging',
- 'type' : LType.ListDNSTapFrameStreamServers,
- 'default' : '',
- 'help' : 'Sequence of dnstap servers',
- 'doc' : '''
+ "name": "dnstap_framestream_servers",
+ "section": "logging",
+ "type": LType.ListDNSTapFrameStreamServers,
+ "default": "",
+ "help": "Sequence of dnstap servers",
+ "doc": """
Sequence of dnstap servers. Currently the maximum size of this list is one.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/protobuf`',
- 'versionadded': '5.1.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'dnstap_nod_framestream_servers',
- 'section' : 'logging',
- 'type' : LType.ListDNSTapNODFrameStreamServers,
- 'default' : '',
- 'help' : 'Sequence of NOD dnstap servers',
- 'doc' : '''
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/protobuf`",
+ "versionadded": "5.1.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "dnstap_nod_framestream_servers",
+ "section": "logging",
+ "type": LType.ListDNSTapNODFrameStreamServers,
+ "default": "",
+ "help": "Sequence of NOD dnstap servers",
+ "doc": """
Sequence of NOD dnstap servers. Currently the maximum size of this list is one.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/protobuf`',
- 'versionadded': '5.1.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'sortlists',
- 'section' : 'recursor',
- 'type' : LType.ListSortLists,
- 'default' : '',
- 'help' : 'Sequence of sort lists',
- 'doc' : '''
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/protobuf`",
+ "versionadded": "5.1.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "sortlists",
+ "section": "recursor",
+ "type": LType.ListSortLists,
+ "default": "",
+ "help": "Sequence of sort lists",
+ "doc": """
Sequence of sort lists.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/sortlist`',
- 'versionadded': '5.1.0',
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/sortlist`",
+ "versionadded": "5.1.0",
},
{
- 'name' : 'rpzs',
- 'section' : 'recursor',
- 'type' : LType.ListRPZs,
- 'default' : '',
- 'help' : 'Sequence of RPZ entries',
- 'doc' : '''
+ "name": "rpzs",
+ "section": "recursor",
+ "type": LType.ListRPZs,
+ "default": "",
+ "help": "Sequence of RPZ entries",
+ "doc": """
Sequence of RPZ entries.
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/rpz`',
- 'versionadded': '5.1.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'zonetocaches',
- 'section' : 'recordcache',
- 'type' : LType.ListZoneToCaches,
- 'default' : '',
- 'help' : 'Sequence of ZoneToCache entries ',
- 'doc' : '''
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/rpz`",
+ "versionadded": "5.1.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "zonetocaches",
+ "section": "recordcache",
+ "type": LType.ListZoneToCaches,
+ "default": "",
+ "help": "Sequence of ZoneToCache entries ",
+ "doc": """
Sequence of ZoneToCache entries
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/ztc`',
- 'versionadded': '5.1.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'allowed_additional_qtypes',
- 'section' : 'recursor',
- 'type' : LType.ListAllowedAdditionalQTypes,
- 'default' : '',
- 'help' : 'Sequence of AllowedAdditionalQType',
- 'doc' : '''
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/ztc`",
+ "versionadded": "5.1.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "allowed_additional_qtypes",
+ "section": "recursor",
+ "type": LType.ListAllowedAdditionalQTypes,
+ "default": "",
+ "help": "Sequence of AllowedAdditionalQType",
+ "doc": """
Sequence of AllowedAdditionalQType
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/additionals`',
- 'versionadded': '5.1.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'proxymappings',
- 'section' : 'incoming',
- 'type' : LType.ListProxyMappings,
- 'default' : '',
- 'help' : 'Sequence of ProxyMapping',
- 'doc' : '''
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/additionals`",
+ "versionadded": "5.1.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "proxymappings",
+ "section": "incoming",
+ "type": LType.ListProxyMappings,
+ "default": "",
+ "help": "Sequence of ProxyMapping",
+ "doc": """
Sequence of ProxyMapping
- ''',
- 'skip-old' : 'Equivalent Lua config in :doc:`lua-config/proxymapping`',
- 'versionadded': '5.1.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'lua_start_stop_script',
- 'section' : 'recursor',
- 'type' : LType.String,
- 'default' : '',
- 'help' : 'Lua script containing functions to run on startup and shutdown',
- 'doc' : '''
+ """,
+ "skip-old": "Equivalent Lua config in :doc:`lua-config/proxymapping`",
+ "versionadded": "5.1.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "lua_start_stop_script",
+ "section": "recursor",
+ "type": LType.String,
+ "default": "",
+ "help": "Lua script containing functions to run on startup and shutdown",
+ "doc": """
Load this Lua script on startup and shutdown and run the Lua function ``on_recursor_start`` on startup and the Lua function ``on_recursor_stop`` on a ``nice`` shutdown (using ``rec_control quit-nicely`` of the :program:`Recursor` process.
- ''',
- 'skip-old' : 'No equivalent old-style setting',
- 'versionadded': '5.2.0',
+ """,
+ "skip-old": "No equivalent old-style setting",
+ "versionadded": "5.2.0",
},
{
- 'name' : 'forwarding_catalog_zones',
- 'section' : 'recursor',
- 'type' : LType.ListForwardingCatalogZones,
- 'default' : '',
- 'help' : 'Sequence of ForwardingCatalogZone',
- 'doc' : '''
+ "name": "forwarding_catalog_zones",
+ "section": "recursor",
+ "type": LType.ListForwardingCatalogZones,
+ "default": "",
+ "help": "Sequence of ForwardingCatalogZone",
+ "doc": """
Sequence of ForwardingCatalogZone. This setting cannot be combined with :ref:`setting-lua-config-file`.
- ''',
- 'skip-old' : 'No equivalent old style setting',
- 'versionadded': '5.2.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'cookies',
- 'section' : 'outgoing',
- 'oldname': 'outgoing-cookies',
- 'type': LType.Bool,
- 'default': 'false',
- 'help': 'Enable DNS cookies when contacting authoritative servers or forwarders',
- 'doc': '''
+ """,
+ "skip-old": "No equivalent old style setting",
+ "versionadded": "5.2.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "cookies",
+ "section": "outgoing",
+ "oldname": "outgoing-cookies",
+ "type": LType.Bool,
+ "default": "false",
+ "help": "Enable DNS cookies when contacting authoritative servers or forwarders",
+ "doc": """
Enable DNS cookies (:rfc:`7873`, :rfc:`9018`) when contacting authoritative servers or forwarders.
-''',
- 'versionadded': '5.4.0',
+""",
+ "versionadded": "5.4.0",
},
{
- 'name' : 'cookies_unsupported',
- 'section' : 'outgoing',
- 'oldname': 'outgoing-cookies-unsupported',
- 'type': LType.ListSocketAddresses,
- 'default': '',
- 'help': 'Addresses (with optional port) of authoritative servers that do not support cookies',
- 'doc': '''
+ "name": "cookies_unsupported",
+ "section": "outgoing",
+ "oldname": "outgoing-cookies-unsupported",
+ "type": LType.ListSocketAddresses,
+ "default": "",
+ "help": "Addresses (with optional port) of authoritative servers that do not support cookies",
+ "doc": """
Addresses of servers that do not properly support DNS cookies (:rfc:`7873`, :rfc:`9018`). Recursor will not even try to probe these servers for cookie support. If no port is specified port 53 is used.
-''',
- 'versionadded': '5.4.0',
+""",
+ "versionadded": "5.4.0",
},
{
- 'name' : 'tls_configurations',
- 'section' : 'outgoing',
- 'type' : LType.ListOutgoingTLSConfigurations,
- 'default' : '',
- 'help' : 'Sequence of OutgoingTLSConfiguration',
- 'doc' : '''
+ "name": "tls_configurations",
+ "section": "outgoing",
+ "type": LType.ListOutgoingTLSConfigurations,
+ "default": "",
+ "help": "Sequence of OutgoingTLSConfiguration",
+ "doc": """
Configurations used for outgoing DoT connections.
A DoT connection is matched against the subnets lists (using the remote IP) and if that does not provide a match, the nameserver name is matched against the suffixes lists. When a match is found, the corresponding DoT configuration is used.
- ''',
- 'skip-old' : 'No equivalent old style setting',
- 'versionadded': '5.4.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
- },
- {
- 'name' : 'opentelemetry_trace_conditions',
- 'section' : 'logging',
- 'type' : LType.ListOpenTelemetryTraceConditions,
- 'default' : '',
- 'help' : 'Sequence of OpenTelemetryTraceCondition',
- 'doc' : '''
+ """,
+ "skip-old": "No equivalent old style setting",
+ "versionadded": "5.4.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
+ },
+ {
+ "name": "opentelemetry_trace_conditions",
+ "section": "logging",
+ "type": LType.ListOpenTelemetryTraceConditions,
+ "default": "",
+ "help": "Sequence of OpenTelemetryTraceCondition",
+ "doc": """
List of conditions specifying when to generate :ref:`opentelemetry_tracing`.
- ''',
- 'skip-old' : 'No equivalent old style setting',
- 'versionadded': '5.4.0',
- 'runtime': ['reload-lua-config', 'reload-yaml'],
+ """,
+ "skip-old": "No equivalent old style setting",
+ "versionadded": "5.4.0",
+ "runtime": ["reload-lua-config", "reload-yaml"],
},
]
import time
try:
- raw_input
+ raw_input
except NameError:
- raw_input = input
+ raw_input = input
-MYSQL_DB='pdnsapi'
-MYSQL_USER='root'
-MYSQL_HOST=os.environ.get('MYSQL_HOST', 'localhost')
-MYSQL_PASSWD=''
+MYSQL_DB = "pdnsapi"
+MYSQL_USER = "root"
+MYSQL_HOST = os.environ.get("MYSQL_HOST", "localhost")
+MYSQL_PASSWD = ""
-PGSQL_DB='pdnsapi'
+PGSQL_DB = "pdnsapi"
-SQLITE_DB = 'pdns.sqlite3'
+SQLITE_DB = "pdns.sqlite3"
-LMDB_DB = 'pdns.lmdb'
+LMDB_DB = "pdns.lmdb"
WEBPORT = 5556
DNSPORT = 5300
-APIKEY = '1234567890abcdefghijklmnopq-key'
-WEBPASSWORD = 'something'
+APIKEY = "1234567890abcdefghijklmnopq-key"
+WEBPASSWORD = "something"
PDNSUTIL_CMD = [os.environ.get("PDNSUTIL", "../pdns/pdnsutil"), "--config-dir=."]
ZONES = ["example.com", "powerdnssec.org", "cryptokeys.org"]
ZONE_DIR = "../regression-tests/zones/"
-AUTH_MYSQL_TPL = """
+AUTH_MYSQL_TPL = (
+ """
# Generated by runtests.py
launch=gmysql
gmysql-dnssec=on
-gmysql-dbname="""+MYSQL_DB+"""
-gmysql-user="""+MYSQL_USER+"""
-gmysql-host="""+MYSQL_HOST+"""
-gmysql-password="""+MYSQL_PASSWD+"""
+gmysql-dbname="""
+ + MYSQL_DB
+ + """
+gmysql-user="""
+ + MYSQL_USER
+ + """
+gmysql-host="""
+ + MYSQL_HOST
+ + """
+gmysql-password="""
+ + MYSQL_PASSWD
+ + """
"""
+)
-AUTH_PGSQL_TPL = """
+AUTH_PGSQL_TPL = (
+ """
# Generated by runtests.py
launch=gpgsql
gpgsql-dnssec=on
-gpgsql-dbname="""+PGSQL_DB+"""
+gpgsql-dbname="""
+ + PGSQL_DB
+ + """
"""
+)
-AUTH_SQLITE_TPL = """
+AUTH_SQLITE_TPL = (
+ """
# Generated by runtests.py
launch=gsqlite3
gsqlite3-dnssec=on
-gsqlite3-database="""+SQLITE_DB+"""
+gsqlite3-database="""
+ + SQLITE_DB
+ + """
"""
+)
-AUTH_LMDB_TPL = """
+AUTH_LMDB_TPL = (
+ """
# Generated by runtests.py
launch=lmdb
-lmdb-filename="""+LMDB_DB+"""
+lmdb-filename="""
+ + LMDB_DB
+ + """
views
"""
+)
AUTH_COMMON_TPL = """
module-dir=../regression-tests/modules
file: ../regression-tests/zones/example.com.rec
"""
-REC_CONF_TPL = """
+REC_CONF_TPL = (
+ """
# Generated by runtests.py
incoming:
allow_from_file: acl.list.yml
webserver: true
api_dir: %(api_dir)s
listen:
- - addresses: [ 127.0.0.1:"""+str(WEBPORT)+""" ]
+ - addresses: [ 127.0.0.1:"""
+ + str(WEBPORT)
+ + """ ]
tls:
certificate: server.chain
key: server.key
- api_key: """+APIKEY+"""
- password: """+WEBPASSWORD+"""
+ api_key: """
+ + APIKEY
+ + """
+ password: """
+ + WEBPASSWORD
+ + """
recursor:
include_dir: %(conf_dir)s
devonly_regression_test_mode: true
"""
+)
def ensure_empty_dir(name):
subprocess.check_call(cmd, *args, **kwargs)
-wait = ('--wait' in sys.argv)
+wait = "--wait" in sys.argv
if wait:
- sys.argv.remove('--wait')
+ sys.argv.remove("--wait")
-tests = [opt for opt in sys.argv if opt.startswith('--tests=')]
+tests = [opt for opt in sys.argv if opt.startswith("--tests=")]
if tests:
for opt in tests:
sys.argv.remove(opt)
-tests = [opt.split('=', 1)[1] for opt in tests]
+tests = [opt.split("=", 1)[1] for opt in tests]
daemon = (len(sys.argv) >= 2) and sys.argv[1] or None
-backend = (len(sys.argv) == 3) and sys.argv[2] or 'gsqlite3'
+backend = (len(sys.argv) == 3) and sys.argv[2] or "gsqlite3"
-if daemon not in ('authoritative', 'recursor') or backend not in ('gmysql', 'gpgsql', 'gsqlite3', 'lmdb'):
+if daemon not in ("authoritative", "recursor") or backend not in ("gmysql", "gpgsql", "gsqlite3", "lmdb"):
print("Usage: ./runtests (authoritative|recursor) [gmysql|gpgsql|gsqlite3|lmdb]")
sys.exit(2)
pdns_server = os.environ.get("PDNSSERVER", "../pdns/pdns_server")
pdns_recursor = os.environ.get("PDNSRECURSOR", "../pdns/recursordist/build/pdns_recursor")
common_args = [
- "--daemon=no", "--socket-dir=.", "--config-dir=.",
- "--local-address=127.0.0.1", "--local-port="+str(DNSPORT),
- "--webserver=yes", "--webserver-port="+str(WEBPORT), "--webserver-address=127.0.0.1",
- "--webserver-password="+WEBPASSWORD,
- "--api-key="+APIKEY
+ "--daemon=no",
+ "--socket-dir=.",
+ "--config-dir=.",
+ "--local-address=127.0.0.1",
+ "--local-port=" + str(DNSPORT),
+ "--webserver=yes",
+ "--webserver-port=" + str(WEBPORT),
+ "--webserver-address=127.0.0.1",
+ "--webserver-password=" + WEBPASSWORD,
+ "--api-key=" + APIKEY,
]
rec_args = [
- "--daemon=no", "--socket-dir=.", "--config-dir=.",
- "--local-address=127.0.0.1", "--local-port="+str(DNSPORT),
+ "--daemon=no",
+ "--socket-dir=.",
+ "--config-dir=.",
+ "--local-address=127.0.0.1",
+ "--local-port=" + str(DNSPORT),
]
# Take sdig if it exists (recursor in travis), otherwise build it from Authoritative source.
sdig = "../pdns/sdig"
-if daemon == 'authoritative':
+if daemon == "authoritative":
# Prepare mysql DB with some zones.
- if backend == 'gmysql':
- subprocess.call(["mysqladmin", "--user=" + MYSQL_USER, "--password=" + MYSQL_PASSWD, "--host=" + MYSQL_HOST, "--force", "drop", MYSQL_DB])
-
- run_check_call(["mysqladmin", "--user=" + MYSQL_USER, "--password=" + MYSQL_PASSWD, "--host=" + MYSQL_HOST, "create", MYSQL_DB])
-
- with open('../modules/gmysqlbackend/schema.mysql.sql', 'r') as schema_file:
- run_check_call(["mysql", "--user=" + MYSQL_USER, "--password=" + MYSQL_PASSWD, "--host=" + MYSQL_HOST, MYSQL_DB], stdin=schema_file)
-
- with open('pdns.conf', 'w') as pdns_conf:
+ if backend == "gmysql":
+ subprocess.call(
+ [
+ "mysqladmin",
+ "--user=" + MYSQL_USER,
+ "--password=" + MYSQL_PASSWD,
+ "--host=" + MYSQL_HOST,
+ "--force",
+ "drop",
+ MYSQL_DB,
+ ]
+ )
+
+ run_check_call(
+ [
+ "mysqladmin",
+ "--user=" + MYSQL_USER,
+ "--password=" + MYSQL_PASSWD,
+ "--host=" + MYSQL_HOST,
+ "create",
+ MYSQL_DB,
+ ]
+ )
+
+ with open("../modules/gmysqlbackend/schema.mysql.sql", "r") as schema_file:
+ run_check_call(
+ ["mysql", "--user=" + MYSQL_USER, "--password=" + MYSQL_PASSWD, "--host=" + MYSQL_HOST, MYSQL_DB],
+ stdin=schema_file,
+ )
+
+ with open("pdns.conf", "w") as pdns_conf:
pdns_conf.write(AUTH_MYSQL_TPL + AUTH_COMMON_TPL)
# Prepare pgsql DB with some zones.
- elif backend == 'gpgsql':
+ elif backend == "gpgsql":
subprocess.call(["dropdb", PGSQL_DB])
subprocess.check_call(["createdb", PGSQL_DB])
- with open('../modules/gpgsqlbackend/schema.pgsql.sql', 'r') as schema_file:
+ with open("../modules/gpgsqlbackend/schema.pgsql.sql", "r") as schema_file:
subprocess.check_call(["psql", PGSQL_DB], stdin=schema_file)
- with open('pdns.conf', 'w') as pdns_conf:
+ with open("pdns.conf", "w") as pdns_conf:
pdns_conf.write(AUTH_PGSQL_TPL + AUTH_COMMON_TPL)
# Prepare sqlite DB with some zones.
- elif backend == 'gsqlite3':
+ elif backend == "gsqlite3":
subprocess.call("rm -f " + SQLITE_DB + "*", shell=True)
- with open('../modules/gsqlite3backend/schema.sqlite3.sql', 'r') as schema_file:
+ with open("../modules/gsqlite3backend/schema.sqlite3.sql", "r") as schema_file:
run_check_call(["sqlite3", SQLITE_DB], stdin=schema_file)
- with open('pdns.conf', 'w') as pdns_conf:
+ with open("pdns.conf", "w") as pdns_conf:
pdns_conf.write(AUTH_SQLITE_TPL + AUTH_COMMON_TPL)
# Prepare lmdb DB with some zones.
- elif backend == 'lmdb':
+ elif backend == "lmdb":
subprocess.call("rm -f " + LMDB_DB + "*", shell=True)
- with open('pdns.conf', 'w') as pdns_conf:
+ with open("pdns.conf", "w") as pdns_conf:
pdns_conf.write(AUTH_LMDB_TPL + AUTH_COMMON_TPL)
- with open('bindbackend.conf', 'w') as bindbackend_conf:
+ with open("bindbackend.conf", "w") as bindbackend_conf:
bindbackend_conf.write(BINDBACKEND_CONF_TPL)
for zone in ZONES:
- run_check_call(PDNSUTIL_CMD + ["load-zone", zone, ZONE_DIR+zone])
+ run_check_call(PDNSUTIL_CMD + ["load-zone", zone, ZONE_DIR + zone])
run_check_call(PDNSUTIL_CMD + ["secure-zone", "powerdnssec.org"])
servercmd = [pdns_server] + common_args + ["--no-shuffle", "--dnsupdate=yes", "--cache-ttl=0", "--api=yes"]
else:
- conf_dir = 'rec-conf.d'
+ conf_dir = "rec-conf.d"
ensure_empty_dir(conf_dir)
- api_dir = 'rec-api.d'
+ api_dir = "rec-api.d"
ensure_empty_dir(api_dir)
- with open('acl.list.yml', 'w') as acl_list:
+ with open("acl.list.yml", "w") as acl_list:
acl_list.write(ACL_LIST_TPL)
- with open('acl-notify.list.yml', 'w') as acl_notify_list:
+ with open("acl-notify.list.yml", "w") as acl_notify_list:
acl_notify_list.write(ACL_NOTIFY_LIST_TPL)
- with open('recursor.yml', 'w') as recursor_conf:
+ with open("recursor.yml", "w") as recursor_conf:
recursor_conf.write(REC_CONF_TPL % locals())
- with open(conf_dir+'/example.com.yml', 'w') as conf_file:
+ with open(conf_dir + "/example.com.yml", "w") as conf_file:
conf_file.write(REC_EXAMPLE_COM_CONF_TPL)
servercmd = [pdns_recursor] + rec_args
server_stderr = tempfile.TemporaryFile()
serverproc = subprocess.Popen(servercmd, close_fds=True, text=True, stdout=server_stdout, stderr=server_stderr)
+
def finalize_server():
serverproc.terminate()
serverproc.wait()
time.sleep(1)
for _ in range(0, 10):
try:
- if daemon == 'authoritative':
- requests.get('http://127.0.0.1:%s/' % WEBPORT)
+ if daemon == "authoritative":
+ requests.get("http://127.0.0.1:%s/" % WEBPORT)
else:
- requests.get('https://127.0.0.1:%s/' % WEBPORT, verify='ca.pem')
+ requests.get("https://127.0.0.1:%s/" % WEBPORT, verify="ca.pem")
available = True
break
except HTTPError as http_err:
- print(f'HTTP error occurred: {http_err}')
+ print(f"HTTP error occurred: {http_err}")
except Exception as err:
- print(f'Other error occurred: {err}')
+ print(f"Other error occurred: {err}")
time.sleep(1)
if not available:
sys.exit(2)
print("Query for example.com/A to create statistic data...")
-if daemon == 'authoritative':
- run_check_call([sdig, "127.0.0.1", str(DNSPORT), "example.com", "A"])
+if daemon == "authoritative":
+ run_check_call([sdig, "127.0.0.1", str(DNSPORT), "example.com", "A"])
else:
- run_check_call([sdig, "127.0.0.1", str(DNSPORT), "example.com", "A", "recurse"])
+ run_check_call([sdig, "127.0.0.1", str(DNSPORT), "example.com", "A", "recurse"])
print("Running tests...")
returncode = 0
test_env = {}
test_env.update(os.environ)
-test_env.update({
- 'WEBPASSWORD': WEBPASSWORD,
- 'WEBPORT': str(WEBPORT),
- 'APIKEY': APIKEY,
- 'DAEMON': daemon,
- 'BACKEND': backend,
- 'MYSQL_DB': MYSQL_DB,
- 'MYSQL_USER': MYSQL_USER,
- 'MYSQL_HOST': MYSQL_HOST,
- 'MYSQL_PASSWD': MYSQL_PASSWD,
- 'PGSQL_DB': PGSQL_DB,
- 'SQLITE_DB': SQLITE_DB,
- 'LMDB_DB': LMDB_DB,
- 'PDNSUTIL_CMD': ' '.join(PDNSUTIL_CMD),
- 'SDIG': sdig,
- 'DNSPORT': str(DNSPORT)
-})
+test_env.update(
+ {
+ "WEBPASSWORD": WEBPASSWORD,
+ "WEBPORT": str(WEBPORT),
+ "APIKEY": APIKEY,
+ "DAEMON": daemon,
+ "BACKEND": backend,
+ "MYSQL_DB": MYSQL_DB,
+ "MYSQL_USER": MYSQL_USER,
+ "MYSQL_HOST": MYSQL_HOST,
+ "MYSQL_PASSWD": MYSQL_PASSWD,
+ "PGSQL_DB": PGSQL_DB,
+ "SQLITE_DB": SQLITE_DB,
+ "LMDB_DB": LMDB_DB,
+ "PDNSUTIL_CMD": " ".join(PDNSUTIL_CMD),
+ "SDIG": sdig,
+ "DNSPORT": str(DNSPORT),
+ }
+)
try:
print("")
class TestBasics(ApiTestCase):
-
def test_unauth(self):
r = requests.get(self.url("/api/v1/servers/localhost"), verify=False)
self.assertEqual(r.status_code, requests.codes.unauthorized)
def test_index_html(self):
- r = requests.get(self.url("/"), auth=('admin', self.server_web_password), verify=False)
+ r = requests.get(self.url("/"), auth=("admin", self.server_web_password), verify=False)
self.assertEqual(r.status_code, requests.codes.ok)
def test_split_request(self):
print("Sending request")
for part in parts:
print("Sending %s" % part)
- s.sendall(part.encode('ascii'))
+ s.sendall(part.encode("ascii"))
time.sleep(0.5)
resp = s.recv(4096, socket.MSG_WAITALL)
print("response", repr(resp))
status = resp.splitlines(0)[0]
- if b'400' in status:
- raise Exception('Got unwanted response: %s' % status)
+ if b"400" in status:
+ raise Exception("Got unwanted response: %s" % status)
def test_cors(self):
r = self.session.options(self.url("/api/v1/servers/localhost"))
# look for CORS headers
self.assertEqual(r.status_code, requests.codes.ok)
- self.assertEqual(r.headers['access-control-allow-origin'], "*")
- self.assertEqual(r.headers['access-control-allow-headers'], 'Content-Type, X-API-Key')
- self.assertEqual(r.headers['access-control-allow-methods'], 'GET, OPTIONS')
+ self.assertEqual(r.headers["access-control-allow-origin"], "*")
+ self.assertEqual(r.headers["access-control-allow-headers"], "Content-Type, X-API-Key")
+ self.assertEqual(r.headers["access-control-allow-methods"], "GET, OPTIONS")
print("response", repr(r.headers))
r = self.session.options(self.url("/api/v1/servers/localhost/zones/test"))
self.assertEqual(r.status_code, requests.codes.ok)
- self.assertEqual(r.headers['access-control-allow-origin'], "*")
- self.assertEqual(r.headers['access-control-allow-headers'], 'Content-Type, X-API-Key')
+ self.assertEqual(r.headers["access-control-allow-origin"], "*")
+ self.assertEqual(r.headers["access-control-allow-headers"], "Content-Type, X-API-Key")
if is_auth():
- self.assertEqual(r.headers['access-control-allow-methods'], 'GET, PATCH, PUT, DELETE, OPTIONS')
+ self.assertEqual(r.headers["access-control-allow-methods"], "GET, PATCH, PUT, DELETE, OPTIONS")
else:
- self.assertEqual(r.headers['access-control-allow-methods'], 'GET, PUT, DELETE, OPTIONS')
+ self.assertEqual(r.headers["access-control-allow-methods"], "GET, PUT, DELETE, OPTIONS")
print("response", repr(r.headers))
class Servers(ApiTestCase):
-
def test_flush(self):
r = self.session.put(self.url("/api/v1/servers/localhost/cache/flush?domain=example.org."))
self.assert_success_json(r)
data = r.json()
- self.assertIn('count', data)
+ self.assertIn("count", data)
@unittest.skipIf(not is_recursor(), "Not applicable")
def test_flush_count(self):
- sdig("ns1.example.com", 'A')
+ sdig("ns1.example.com", "A")
r = self.session.put(self.url("/api/v1/servers/localhost/cache/flush?domain=ns1.example.com."))
self.assert_success_json(r)
data = r.json()
- self.assertIn('count', data)
- self.assertEqual(2, data['count'])
+ self.assertIn("count", data)
+ self.assertEqual(2, data["count"])
@unittest.skipIf(not is_recursor(), "Not applicable")
def test_flush_subtree(self):
- sdig("ns1.example.com", 'A')
- sdig("ns2.example.com", 'A')
+ sdig("ns1.example.com", "A")
+ sdig("ns2.example.com", "A")
r = self.session.put(self.url("/api/v1/servers/localhost/cache/flush?domain=example.com.&subtree=false"))
self.assert_success_json(r)
data = r.json()
- self.assertIn('count', data)
- self.assertEqual(3, data['count'])
+ self.assertIn("count", data)
+ self.assertEqual(3, data["count"])
r = self.session.put(self.url("/api/v1/servers/localhost/cache/flush?domain=example.com.&subtree=true"))
self.assert_success_json(r)
data = r.json()
- self.assertIn('count', data)
- self.assertEqual(4, data['count'])
+ self.assertIn("count", data)
+ self.assertEqual(4, data["count"])
def test_flush_root(self):
r = self.session.put(self.url("/api/v1/servers/localhost/cache/flush?domain=."))
self.assert_success_json(r)
data = r.json()
- self.assertIn('count', data)
- self.assertEqual(data['result'], 'Flushed cache.')
+ self.assertIn("count", data)
+ self.assertEqual(data["result"], "Flushed cache.")
def test_flush_no_domain(self):
- r = self.session.put(
- self.url("/api/v1/servers/localhost/cache/flush"))
+ r = self.session.put(self.url("/api/v1/servers/localhost/cache/flush"))
self.assertEqual(r.status_code, 422)
def test_flush_unqualified(self):
- r = self.session.put(
- self.url("/api/v1/servers/localhost/cache/flush?domain=bar"))
+ r = self.session.put(self.url("/api/v1/servers/localhost/cache/flush?domain=bar"))
self.assertEqual(r.status_code, 422)
class DiscoveryTest(ApiTestCase):
-
def test_discovery(self):
r = self.session.get(self.url("/api"))
self.assert_success_json(r)
lst = r.json()
- self.assertEqual(lst, [{'version': 1, 'url': '/api/v1'}])
+ self.assertEqual(lst, [{"version": 1, "url": "/api/v1"}])
r = self.session.get(self.url("/api/v1"))
self.assert_success_json(r)
lst = r.json()
- self.assertEqual(lst, [{'api_features': [], 'server_url': '/api/v1/servers{/server}'}])
+ self.assertEqual(lst, [{"api_features": [], "server_url": "/api/v1/servers{/server}"}])
@unittest.skipIf(not is_recursor(), "Only applicable to recursors")
class RecursorAllowFromConfig(ApiTestCase):
-
def test_config_allow_from_get(self):
r = self.session.get(self.url("/api/v1/servers/localhost/config/allow-from"))
self.assert_success_json(r)
def test_config_allow_from_replace(self):
- payload = {'value': ["127.0.0.1"]}
+ payload = {"value": ["127.0.0.1"]}
r = self.session.put(
self.url("/api/v1/servers/localhost/config/allow-from"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success_json(r)
data = r.json()
self.assertIn("value", data)
self.assertEqual("127.0.0.1/32", data["value"][0])
def test_config_allow_from_replace_empty(self):
- payload = {'value': []}
+ payload = {"value": []}
r = self.session.put(
self.url("/api/v1/servers/localhost/config/allow-from"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success_json(r)
data = r.json()
self.assertIn("value", data)
def test_config_allow_from_replace_error(self):
"""Test the error case, should return 422."""
- payload = {'value': ["abcdefgh"]}
+ payload = {"value": ["abcdefgh"]}
r = self.session.put(
self.url("/api/v1/servers/localhost/config/allow-from"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
data = r.json()
- self.assertIn('Unable to convert', data['error'])
+ self.assertIn("Unable to convert", data["error"])
@unittest.skipIf(not is_recursor(), "Only applicable to recursors")
class RecursorAllowNotifyFromConfig(ApiTestCase):
-
def test_config_allow_notify_from_get(self):
r = self.session.get(self.url("/api/v1/servers/localhost/config/allow-notify-from"))
self.assert_success_json(r)
def test_config_allow_notify_from_replace(self):
- payload = {'value': ["127.0.0.1"]}
+ payload = {"value": ["127.0.0.1"]}
r = self.session.put(
self.url("/api/v1/servers/localhost/config/allow-notify-from"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success_json(r)
data = r.json()
self.assertIn("value", data)
self.assertEqual("127.0.0.1/32", data["value"][0])
def test_config_allow_notify_from_replace_empty(self):
- payload = {'value': []}
+ payload = {"value": []}
r = self.session.put(
self.url("/api/v1/servers/localhost/config/allow-notify-from"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success_json(r)
data = r.json()
self.assertIn("value", data)
def test_config_allow_notify_from_replace_error(self):
"""Test the error case, should return 422."""
- payload = {'value': ["abcdefgh"]}
+ payload = {"value": ["abcdefgh"]}
r = self.session.put(
self.url("/api/v1/servers/localhost/config/allow-notify-from"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
data = r.json()
- self.assertIn('Unable to convert', data['error'])
+ self.assertIn("Unable to convert", data["error"])
@unittest.skipIf(not is_recursor(), "Only applicable to recursors")
class RecursorOT(ApiTestCase):
-
def assert_in_json_error(self, expected, json):
- error = json['error']
+ error = json["error"]
if expected not in error:
found = False
- if 'errors' in json:
- errors = json['errors']
+ if "errors" in json:
+ errors = json["errors"]
for item in errors:
if expected in item:
found = True
def test_basic_ot_conditions(self):
# initial list is empty
r = self.session.get(
- self.url("/api/v1/servers/localhost/ottraceconditions"),
- headers={'content-type': 'application/json'})
+ self.url("/api/v1/servers/localhost/ottraceconditions"), headers={"content-type": "application/json"}
+ )
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json(), [])
# nonexistent condition
r = self.session.get(
self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4/32"),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('Could not find otcondition', r.json())
+ self.assert_in_json_error("Could not find otcondition", r.json())
# malformed netmask
r = self.session.get(
self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3/32"),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('Could not parse netmask', r.json())
+ self.assert_in_json_error("Could not parse netmask", r.json())
# deleting non-existent netmask
r = self.session.delete(
self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4/32"),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('Could not find otcondition', r.json())
+ self.assert_in_json_error("Could not find otcondition", r.json())
# creating, most simple case
- payload = {
- "acl": "1.2.3.4"
- }
+ payload = {"acl": "1.2.3.4"}
r = self.session.post(
self.url("/api/v1/servers/localhost/ottraceconditions"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 201)
data = r.json()
- self.assertIn('acl', data)
- self.assertIn('edns_option_required', data)
- self.assertIn('traceid_only', data)
- self.assertIn('type', data)
- self.assertEqual(data['acl'], '1.2.3.4/32')
- self.assertEqual(data['type'], 'OpenTelemetryTraceCondition')
- self.assertFalse(data['edns_option_required'])
- self.assertFalse(data['traceid_only'])
+ self.assertIn("acl", data)
+ self.assertIn("edns_option_required", data)
+ self.assertIn("traceid_only", data)
+ self.assertIn("type", data)
+ self.assertEqual(data["acl"], "1.2.3.4/32")
+ self.assertEqual(data["type"], "OpenTelemetryTraceCondition")
+ self.assertFalse(data["edns_option_required"])
+ self.assertFalse(data["traceid_only"])
# creating, error because duplicate
- payload = {
- "acl": "1.2.3.4"
- }
+ payload = {"acl": "1.2.3.4"}
r = self.session.post(
self.url("/api/v1/servers/localhost/ottraceconditions"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('OTCondition already exists', r.json())
+ self.assert_in_json_error("OTCondition already exists", r.json())
# list has one element
r = self.session.get(
- self.url("/api/v1/servers/localhost/ottraceconditions"),
- headers={'content-type': 'application/json'})
+ self.url("/api/v1/servers/localhost/ottraceconditions"), headers={"content-type": "application/json"}
+ )
self.assertEqual(r.status_code, 200)
self.assertEqual(len(r.json()), 1)
# creating, more general case
- payload = {
- "acl": "1.2.3.0/24"
- }
+ payload = {"acl": "1.2.3.0/24"}
r = self.session.post(
self.url("/api/v1/servers/localhost/ottraceconditions"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 201)
data = r.json()
- self.assertIn('acl', data)
- self.assertIn('edns_option_required', data)
- self.assertIn('traceid_only', data)
- self.assertEqual(data['acl'], '1.2.3.0/24')
- self.assertFalse(data['edns_option_required'])
- self.assertFalse(data['traceid_only'])
+ self.assertIn("acl", data)
+ self.assertIn("edns_option_required", data)
+ self.assertIn("traceid_only", data)
+ self.assertEqual(data["acl"], "1.2.3.0/24")
+ self.assertFalse(data["edns_option_required"])
+ self.assertFalse(data["traceid_only"])
# list has two elements
r = self.session.get(
- self.url("/api/v1/servers/localhost/ottraceconditions"),
- headers={'content-type': 'application/json'})
+ self.url("/api/v1/servers/localhost/ottraceconditions"), headers={"content-type": "application/json"}
+ )
self.assertEqual(r.status_code, 200)
self.assertEqual(len(r.json()), 2)
# querying by more specific key than /24
r = self.session.get(
self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4/31"),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('Could not find otcondition', r.json())
+ self.assert_in_json_error("Could not find otcondition", r.json())
# deleting specific netmask
r = self.session.delete(
self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4/32"),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
# list has one elements
r = self.session.get(
- self.url("/api/v1/servers/localhost/ottraceconditions"),
- headers={'content-type': 'application/json'})
+ self.url("/api/v1/servers/localhost/ottraceconditions"), headers={"content-type": "application/json"}
+ )
self.assertEqual(r.status_code, 200)
self.assertEqual(len(r.json()), 1)
"qnames": ["foo.bar", "nl", "com"],
"qtypes": ["AAAA", "TXT"],
"traceid_only": True,
- "edns_option_required": True
+ "edns_option_required": True,
}
r = self.session.post(
self.url("/api/v1/servers/localhost/ottraceconditions"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 201)
data = r.json()
- self.assertIn('acl', data)
- self.assertIn('qid', data)
- self.assertIn('qnames', data)
- self.assertIn('qtypes', data)
- self.assertIn('traceid_only', data)
- self.assertIn('edns_option_required', data)
- self.assertIn('type', data)
- self.assertEqual(data['acl'], '::/0')
- self.assertEqual(data['qid'], 99)
- self.assertEqual(len(data['qnames']), 3)
- self.assertEqual(len(data['qtypes']), 2)
- self.assertEqual(data['type'], 'OpenTelemetryTraceCondition')
- self.assertTrue(data['edns_option_required'])
- self.assertTrue(data['traceid_only'])
+ self.assertIn("acl", data)
+ self.assertIn("qid", data)
+ self.assertIn("qnames", data)
+ self.assertIn("qtypes", data)
+ self.assertIn("traceid_only", data)
+ self.assertIn("edns_option_required", data)
+ self.assertIn("type", data)
+ self.assertEqual(data["acl"], "::/0")
+ self.assertEqual(data["qid"], 99)
+ self.assertEqual(len(data["qnames"]), 3)
+ self.assertEqual(len(data["qtypes"]), 2)
+ self.assertEqual(data["type"], "OpenTelemetryTraceCondition")
+ self.assertTrue(data["edns_option_required"])
+ self.assertTrue(data["traceid_only"])
# and GET the newly created one in a separate call
r = self.session.get(
- self.url("/api/v1/servers/localhost/ottraceconditions/::/0"),
- headers={'content-type': 'application/json'})
+ self.url("/api/v1/servers/localhost/ottraceconditions/::/0"), headers={"content-type": "application/json"}
+ )
self.assertEqual(r.status_code, 200)
data = r.json()
- self.assertIn('acl', data)
- self.assertIn('qid', data)
- self.assertIn('qnames', data)
- self.assertIn('qtypes', data)
- self.assertIn('traceid_only', data)
- self.assertIn('edns_option_required', data)
- self.assertIn('type', data)
- self.assertEqual(data['acl'], '::/0')
- self.assertEqual(data['qid'], 99)
- self.assertEqual(len(data['qnames']), 3)
- self.assertEqual(len(data['qtypes']), 2)
- self.assertEqual(data['type'], 'OpenTelemetryTraceCondition')
- self.assertTrue(data['edns_option_required'])
- self.assertTrue(data['traceid_only'])
+ self.assertIn("acl", data)
+ self.assertIn("qid", data)
+ self.assertIn("qnames", data)
+ self.assertIn("qtypes", data)
+ self.assertIn("traceid_only", data)
+ self.assertIn("edns_option_required", data)
+ self.assertIn("type", data)
+ self.assertEqual(data["acl"], "::/0")
+ self.assertEqual(data["qid"], 99)
+ self.assertEqual(len(data["qnames"]), 3)
+ self.assertEqual(len(data["qtypes"]), 2)
+ self.assertEqual(data["type"], "OpenTelemetryTraceCondition")
+ self.assertTrue(data["edns_option_required"])
+ self.assertTrue(data["traceid_only"])
class Servers(ApiTestCase):
-
def test_list_servers(self):
r = self.session.get(self.url("/api/v1/servers"))
self.assert_success_json(r)
lst = r.json()
self.assertEqual(len(lst), 1) # only localhost allowed in there
data = lst[0]
- for k in ('id', 'daemon_type', 'url'):
+ for k in ("id", "daemon_type", "url"):
self.assertIn(k, data)
- self.assertEqual(data['id'], 'localhost')
+ self.assertEqual(data["id"], "localhost")
def test_servers_localhost(self):
r = self.session.get(self.url("/api/v1/servers/localhost"))
self.assert_success_json(r)
data = r.json()
- for k in ('id', 'type', 'version', 'daemon_type', 'url', 'zones_url', 'config_url'):
+ for k in ("id", "type", "version", "daemon_type", "url", "zones_url", "config_url"):
self.assertIn(k, data)
- self.assertEqual(data['id'], 'localhost')
- self.assertEqual(data['type'], 'Server')
+ self.assertEqual(data["id"], "localhost")
+ self.assertEqual(data["type"], "Server")
# or 'recursor' for recursors
if is_auth():
- daemon_type = 'authoritative'
+ daemon_type = "authoritative"
elif is_recursor():
- daemon_type = 'recursor'
+ daemon_type = "recursor"
else:
- raise RuntimeError('Unknown daemon type')
- self.assertEqual(data['daemon_type'], daemon_type)
+ raise RuntimeError("Unknown daemon type")
+ self.assertEqual(data["daemon_type"], daemon_type)
def test_read_config(self):
r = self.session.get(self.url("/api/v1/servers/localhost/config"))
self.assert_success_json(r)
- data = dict([(r['name'], r['value']) for r in r.json()])
- self.assertIn('daemon', data)
+ data = dict([(r["name"], r["value"]) for r in r.json()])
+ self.assertIn("daemon", data)
def test_read_statistics(self):
r = self.session.get(self.url("/api/v1/servers/localhost/statistics"))
self.assert_success_json(r)
data = r.json()
- self.assertIn('uptime', [e['name'] for e in data])
+ self.assertIn("uptime", [e["name"] for e in data])
print(data)
if is_auth():
qtype_stats, respsize_stats, queries_stats, rcode_stats = None, None, None, None
for elem in data:
- if elem['type'] == 'MapStatisticItem' and elem['name'] == 'response-by-qtype':
- qtype_stats = elem['value']
- elif elem['type'] == 'MapStatisticItem' and elem['name'] == 'response-sizes':
- respsize_stats = elem['value']
- elif elem['type'] == 'RingStatisticItem' and elem['name'] == 'queries':
- queries_stats = elem['value']
- elif elem['type'] == 'MapStatisticItem' and elem['name'] == 'response-by-rcode':
- rcode_stats = elem['value']
- self.assertIn('A', [e['name'] for e in qtype_stats])
- self.assertIn('80', [e['name'] for e in respsize_stats])
- self.assertIn('example.com/A', [e['name'] for e in queries_stats])
- self.assertIn('No Error', [e['name'] for e in rcode_stats])
+ if elem["type"] == "MapStatisticItem" and elem["name"] == "response-by-qtype":
+ qtype_stats = elem["value"]
+ elif elem["type"] == "MapStatisticItem" and elem["name"] == "response-sizes":
+ respsize_stats = elem["value"]
+ elif elem["type"] == "RingStatisticItem" and elem["name"] == "queries":
+ queries_stats = elem["value"]
+ elif elem["type"] == "MapStatisticItem" and elem["name"] == "response-by-rcode":
+ rcode_stats = elem["value"]
+ self.assertIn("A", [e["name"] for e in qtype_stats])
+ self.assertIn("80", [e["name"] for e in respsize_stats])
+ self.assertIn("example.com/A", [e["name"] for e in queries_stats])
+ self.assertIn("No Error", [e["name"] for e in rcode_stats])
else:
qtype_stats, respsize_stats, rcode_stats = None, None, None
for elem in data:
- if elem['type'] == 'MapStatisticItem' and elem['name'] == 'response-by-qtype':
- qtype_stats = elem['value']
- elif elem['type'] == 'MapStatisticItem' and elem['name'] == 'response-sizes':
- respsize_stats = elem['value']
- elif elem['type'] == 'MapStatisticItem' and elem['name'] == 'response-by-rcode':
- rcode_stats = elem['value']
- self.assertIn('A', [e['name'] for e in qtype_stats])
- self.assertIn('60', [e['name'] for e in respsize_stats])
- self.assertIn('80', [e['name'] for e in respsize_stats])
- self.assertIn('No Error', [e['name'] for e in rcode_stats])
+ if elem["type"] == "MapStatisticItem" and elem["name"] == "response-by-qtype":
+ qtype_stats = elem["value"]
+ elif elem["type"] == "MapStatisticItem" and elem["name"] == "response-sizes":
+ respsize_stats = elem["value"]
+ elif elem["type"] == "MapStatisticItem" and elem["name"] == "response-by-rcode":
+ rcode_stats = elem["value"]
+ self.assertIn("A", [e["name"] for e in qtype_stats])
+ self.assertIn("60", [e["name"] for e in respsize_stats])
+ self.assertIn("80", [e["name"] for e in respsize_stats])
+ self.assertIn("No Error", [e["name"] for e in rcode_stats])
def test_read_one_statistic(self):
r = self.session.get(self.url("/api/v1/servers/localhost/statistics?statistic=uptime"))
self.assert_success_json(r)
data = r.json()
- self.assertIn('uptime', [e['name'] for e in data])
+ self.assertIn("uptime", [e["name"] for e in data])
def test_read_one_non_existent_statistic(self):
r = self.session.get(self.url("/api/v1/servers/localhost/statistics?statistic=uptimeAAAA"))
self.assertEqual(r.status_code, 422)
- self.assertIn("Unknown statistic name", r.json()['error'])
+ self.assertIn("Unknown statistic name", r.json()["error"])
def test_read_metrics(self):
if is_recursor():
- res = self.session.get(self.url("/metrics"), auth=('whatever', self.webServerBasicAuthPassword), timeout=2.0)
+ res = self.session.get(
+ self.url("/metrics"), auth=("whatever", self.webServerBasicAuthPassword), timeout=2.0
+ )
self.assertEqual(res.status_code, 200)
# print(res.text)
found = False
continue
if line.split(" ")[0] == "pdns_recursor_uptime":
found = True
- self.assertTrue(found,"pdns_recursor_uptime is missing")
+ self.assertTrue(found, "pdns_recursor_uptime is missing")
@unittest.skipIf(is_auth(), "Not applicable")
def test_read_statistics_using_password(self):
- r = requests.get(self.url("/api/v1/servers/localhost/statistics"), auth=('admin', self.server_web_password), verify=False)
+ r = requests.get(
+ self.url("/api/v1/servers/localhost/statistics"), auth=("admin", self.server_web_password), verify=False
+ )
self.assertEqual(r.status_code, requests.codes.ok)
self.assert_success_json(r)
@unittest.skipIf(is_auth_lmdb(), "No autoprimary management in LMDB yet")
def test_autoprimaries(self):
# verify that we have zero autoprimaries
- res = self.session.get(self.url("/api/v1/servers/localhost/autoprimaries"), auth=('whatever', self.webServerBasicAuthPassword), timeout=2.0)
+ res = self.session.get(
+ self.url("/api/v1/servers/localhost/autoprimaries"),
+ auth=("whatever", self.webServerBasicAuthPassword),
+ timeout=2.0,
+ )
self.assertEqual(res.status_code, requests.codes.ok)
self.assertEqual(res.json(), [])
# add one
- payload = {
- 'ip': '192.0.2.1',
- 'nameserver': 'ns.example.com'
- }
+ payload = {"ip": "192.0.2.1", "nameserver": "ns.example.com"}
res = self.session.post(
self.url("/api/v1/servers/localhost/autoprimaries"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(res.status_code, 201)
# check that it's there
- res = self.session.get(self.url("/api/v1/servers/localhost/autoprimaries"), auth=('whatever', self.webServerBasicAuthPassword), timeout=2.0)
+ res = self.session.get(
+ self.url("/api/v1/servers/localhost/autoprimaries"),
+ auth=("whatever", self.webServerBasicAuthPassword),
+ timeout=2.0,
+ )
self.assertEqual(res.status_code, requests.codes.ok)
- self.assertEqual(res.json(), [{'account': '', 'ip': '192.0.2.1', 'nameserver': 'ns.example.com'}])
+ self.assertEqual(res.json(), [{"account": "", "ip": "192.0.2.1", "nameserver": "ns.example.com"}])
# add another one, this time with an account field
- payload = {
- 'ip': '192.0.2.2',
- 'nameserver': 'ns.example.org',
- 'account': 'test'
- }
+ payload = {"ip": "192.0.2.2", "nameserver": "ns.example.org", "account": "test"}
res = self.session.post(
self.url("/api/v1/servers/localhost/autoprimaries"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(res.status_code, 201)
# check that both are there
- res = self.session.get(self.url("/api/v1/servers/localhost/autoprimaries"), auth=('whatever', self.webServerBasicAuthPassword), timeout=2.0)
+ res = self.session.get(
+ self.url("/api/v1/servers/localhost/autoprimaries"),
+ auth=("whatever", self.webServerBasicAuthPassword),
+ timeout=2.0,
+ )
self.assertEqual(res.status_code, requests.codes.ok)
self.assertEqual(len(res.json()), 2)
- self.assertEqual(sorted(res.json(), key=operator.itemgetter('ip')), [
- {'account': '', 'ip': '192.0.2.1', 'nameserver': 'ns.example.com'},
- {'account': 'test', 'ip': '192.0.2.2', 'nameserver': 'ns.example.org'}
- ])
+ self.assertEqual(
+ sorted(res.json(), key=operator.itemgetter("ip")),
+ [
+ {"account": "", "ip": "192.0.2.1", "nameserver": "ns.example.com"},
+ {"account": "test", "ip": "192.0.2.2", "nameserver": "ns.example.org"},
+ ],
+ )
# remove one
res = self.session.delete(
self.url("/api/v1/servers/localhost/autoprimaries/192.0.2.2/ns.example.org"),
- headers={'content-type': 'application/json"'})
+ headers={"content-type": 'application/json"'},
+ )
self.assertEqual(res.status_code, 204)
# check that we are back to just one
- res = self.session.get(self.url("/api/v1/servers/localhost/autoprimaries"), auth=('whatever', self.webServerBasicAuthPassword), timeout=2.0)
+ res = self.session.get(
+ self.url("/api/v1/servers/localhost/autoprimaries"),
+ auth=("whatever", self.webServerBasicAuthPassword),
+ timeout=2.0,
+ )
self.assertEqual(res.status_code, requests.codes.ok)
- self.assertEqual(res.json(), [{'account': '', 'ip': '192.0.2.1', 'nameserver': 'ns.example.com'}])
+ self.assertEqual(res.json(), [{"account": "", "ip": "192.0.2.1", "nameserver": "ns.example.com"}])
import unittest
from test_helper import ApiTestCase, unique_tsigkey_name, is_auth
+
class AuthTSIGHelperMixin(object):
- def create_tsig_key(self, name=None, algorithm='hmac-md5', key=None):
+ def create_tsig_key(self, name=None, algorithm="hmac-md5", key=None):
if name is None:
name = unique_tsigkey_name()
payload = {
- 'name': name,
- 'algorithm': algorithm,
+ "name": name,
+ "algorithm": algorithm,
}
if key is not None:
- payload.update({'key': key})
+ payload.update({"key": key})
print("sending", payload)
r = self.session.post(
self.url("/api/v1/servers/localhost/tsigkeys"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success_json(r)
self.assertEqual(r.status_code, 201)
reply = r.json()
Create a TSIG key that is generated by the server
"""
name, payload, data = self.create_tsig_key()
- for k in ('id', 'name', 'algorithm', 'key', 'type'):
+ for k in ("id", "name", "algorithm", "key", "type"):
self.assertIn(k, data)
if k in payload:
self.assertEqual(data[k], payload[k])
"""
Create a new key with the key data provided
"""
- key = 'fn+BREHMDq0uWA1WbDwaoc2ne3rD973ySJ33ToJTfWY='
+ key = "fn+BREHMDq0uWA1WbDwaoc2ne3rD973ySJ33ToJTfWY="
name, payload, data = self.create_tsig_key(key=key)
- self.assertEqual(data['key'], key)
+ self.assertEqual(data["key"], key)
def test_create_key_with_hmacsha512(self):
"""
Have the server generate a key with the provided algorithm
"""
- algorithm = 'hmac-sha512'
+ algorithm = "hmac-sha512"
name, payload, data = self.create_tsig_key(algorithm=algorithm)
- self.assertEqual(data['algorithm'], algorithm)
+ self.assertEqual(data["algorithm"], algorithm)
def test_get_non_existing_key(self):
"""
Try to get a key that does not exist
"""
name = "idonotexist"
- r = self.session.get(self.url(
- "/api/v1/servers/localhost/tsigkeys/" + name + '.'),
- headers={'accept': 'application/json'})
+ r = self.session.get(
+ self.url("/api/v1/servers/localhost/tsigkeys/" + name + "."), headers={"accept": "application/json"}
+ )
self.assert_error_json(r)
self.assertEqual(r.status_code, 404)
newdata = r.json()
- self.assertIn('TSIG key with name \'' + name + '\' not found', newdata['error'])
+ self.assertIn("TSIG key with name '" + name + "' not found", newdata["error"])
def test_remove_key(self):
"""
Create a key and attempt to delete it
"""
name, payload, data = self.create_tsig_key()
- r = self.session.delete(self.url("/api/v1/servers/localhost/tsigkeys/" + data['id']))
+ r = self.session.delete(self.url("/api/v1/servers/localhost/tsigkeys/" + data["id"]))
self.assertEqual(r.status_code, 204)
- r = self.session.get(self.url(
- "/api/v1/servers/localhost/tsigkeys"),
- headers={'accept': 'application/json'})
+ r = self.session.get(self.url("/api/v1/servers/localhost/tsigkeys"), headers={"accept": "application/json"})
self.assertEqual(r.status_code, 200)
keys = r.json()
- self.assertEqual(len([key for key in keys if key['name'] == name]), 0)
+ self.assertEqual(len([key for key in keys if key["name"] == name]), 0)
def test_put_key_change_name(self):
"""
Rename a key by PUTing a json with "name" set
"""
name, payload, data = self.create_tsig_key()
- payload = {
- 'name': 'mynewkey'
- }
- r = self.session.put(self.url("/api/v1/servers/localhost/tsigkeys/" + data['id']),
- data=json.dumps(payload))
+ payload = {"name": "mynewkey"}
+ r = self.session.put(self.url("/api/v1/servers/localhost/tsigkeys/" + data["id"]), data=json.dumps(payload))
self.assertEqual(r.status_code, 200)
newdata = r.json()
- self.assertEqual(newdata['name'], 'mynewkey')
+ self.assertEqual(newdata["name"], "mynewkey")
# Check if the old key is removed
- r = self.session.get(self.url("/api/v1/servers/localhost/tsigkeys/" + data['id']))
+ r = self.session.get(self.url("/api/v1/servers/localhost/tsigkeys/" + data["id"]))
self.assertEqual(r.status_code, 404, "Old key was not removed!")
def test_put_key_change_key(self):
Change the key by PUTing it
"""
name, payload, data = self.create_tsig_key()
- newkey = 'l36TAJalAys0HeEfSM1rFzSmz9kSwfiBo3HNkL62COs='
- payload = {
- 'key': newkey
- }
- r = self.session.put(self.url("/api/v1/servers/localhost/tsigkeys/" + data['id']),
- data=json.dumps(payload))
+ newkey = "l36TAJalAys0HeEfSM1rFzSmz9kSwfiBo3HNkL62COs="
+ payload = {"key": newkey}
+ r = self.session.put(self.url("/api/v1/servers/localhost/tsigkeys/" + data["id"]), data=json.dumps(payload))
self.assertEqual(r.status_code, 200)
data = r.json()
- self.assertEqual(data['key'], newkey)
+ self.assertEqual(data["key"], newkey)
def test_put_key_change_algo(self):
name, payload, data = self.create_tsig_key()
- newalgo = 'hmac-sha256'
- payload = {
- 'algorithm': newalgo
- }
- r = self.session.put(self.url("/api/v1/servers/localhost/tsigkeys/" + data['id']),
- data=json.dumps(payload))
+ newalgo = "hmac-sha256"
+ payload = {"algorithm": newalgo}
+ r = self.session.put(self.url("/api/v1/servers/localhost/tsigkeys/" + data["id"]), data=json.dumps(payload))
self.assertEqual(r.status_code, 200)
data = r.json()
- self.assertEqual(data['algorithm'], newalgo)
+ self.assertEqual(data["algorithm"], newalgo)
def test_put_non_existing_algo(self):
name, payload, data = self.create_tsig_key()
- payload = {
- 'algorithm': 'foobar'
- }
- r = self.session.put(self.url("/api/v1/servers/localhost/tsigkeys/" + data['id']),
- data=json.dumps(payload))
+ payload = {"algorithm": "foobar"}
+ r = self.session.put(self.url("/api/v1/servers/localhost/tsigkeys/" + data["id"]), data=json.dumps(payload))
self.assertEqual(r.status_code, 422)
data = r.json()
- self.assertIn('Unknown TSIG algorithm: ', data['error'])
+ self.assertIn("Unknown TSIG algorithm: ", data["error"])
def test_put_broken_key(self):
name, payload, data = self.create_tsig_key()
- payload = {
- 'key': 'f\\u0333oobar1======'
- }
- r = self.session.put(self.url("/api/v1/servers/localhost/tsigkeys/" + data['id']),
- data=json.dumps(payload))
+ payload = {"key": "f\\u0333oobar1======"}
+ r = self.session.put(self.url("/api/v1/servers/localhost/tsigkeys/" + data["id"]), data=json.dumps(payload))
data = r.json()
self.assertEqual(r.status_code, 422)
- self.assertIn('Can not base64 decode key content ', data['error'])
+ self.assertIn("Can not base64 decode key content ", data["error"])
def test_put_to_non_existing_key(self):
name = unique_tsigkey_name()
- payload = {
- 'algorithm': 'hmac-sha512'
- }
- r = self.session.put(self.url("/api/v1/servers/localhost/tsigkeys/" + name + '.'),
- data=json.dumps(payload),
- headers={'accept': 'application/json'})
+ payload = {"algorithm": "hmac-sha512"}
+ r = self.session.put(
+ self.url("/api/v1/servers/localhost/tsigkeys/" + name + "."),
+ data=json.dumps(payload),
+ headers={"accept": "application/json"},
+ )
self.assertEqual(r.status_code, 404)
data = r.json()
- self.assertIn('TSIG key with name \'' + name + '\' not found', data['error'])
+ self.assertIn("TSIG key with name '" + name + "' not found", data["error"])
def test_post_existing_key_name(self):
name, payload, data = self.create_tsig_key()
- r = self.session.post(self.url("/api/v1/servers/localhost/tsigkeys"),
- headers={'accept': 'application/json'},
- data=json.dumps(payload))
+ r = self.session.post(
+ self.url("/api/v1/servers/localhost/tsigkeys"),
+ headers={"accept": "application/json"},
+ data=json.dumps(payload),
+ )
self.assertEqual(r.status_code, 409)
data = r.json()
- self.assertIn('A TSIG key with the name ', data['error'])
+ self.assertIn("A TSIG key with the name ", data["error"])
def test_post_broken_key_name(self):
- payload = {
- 'name': unique_tsigkey_name(),
- 'key': 'f\\u0333oobar1======',
- 'algorithm': 'hmac-md5'
- }
- r = self.session.post(self.url("/api/v1/servers/localhost/tsigkeys"),
- headers={'accept': 'application/json'},
- data=json.dumps(payload))
+ payload = {"name": unique_tsigkey_name(), "key": "f\\u0333oobar1======", "algorithm": "hmac-md5"}
+ r = self.session.post(
+ self.url("/api/v1/servers/localhost/tsigkeys"),
+ headers={"accept": "application/json"},
+ data=json.dumps(payload),
+ )
self.assertEqual(r.status_code, 422)
data = r.json()
- self.assertIn(' cannot be base64-decoded', data['error'])
+ self.assertIn(" cannot be base64-decoded", data["error"])
def test_post_wrong_algo(self):
- payload = {
- 'name': unique_tsigkey_name(),
- 'algorithm': 'foobar'
- }
- r = self.session.post(self.url("/api/v1/servers/localhost/tsigkeys"),
- headers={'accept': 'application/json'},
- data=json.dumps(payload))
+ payload = {"name": unique_tsigkey_name(), "algorithm": "foobar"}
+ r = self.session.post(
+ self.url("/api/v1/servers/localhost/tsigkeys"),
+ headers={"accept": "application/json"},
+ data=json.dumps(payload),
+ )
self.assertEqual(r.status_code, 400)
data = r.json()
- self.assertIn('Invalid TSIG algorithm: ', data['error'])
+ self.assertIn("Invalid TSIG algorithm: ", data["error"])
from test_helper import ApiTestCase, is_auth, is_auth_lmdb
from test_Zones import AuthZonesHelperMixin
+
@unittest.skipIf(not is_auth(), "Not applicable")
@unittest.skipIf(not is_auth_lmdb(), "Views require the LMDB backend")
class Networks(ApiTestCase):
super(Networks, self).tearDown()
def test_networks(self):
- r = self.set_network('192.0.2.0/24', view='view1')
+ r = self.set_network("192.0.2.0/24", view="view1")
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
# Check network presence
nets = self.get_networks()
- self.assertEqual(nets['192.0.2.0/24'], 'view1')
+ self.assertEqual(nets["192.0.2.0/24"], "view1")
# Check individual fetch
- r = self.get_network('192.0.2.0/24')
+ r = self.get_network("192.0.2.0/24")
print(r.content)
self.assertEqual(r.status_code, 200)
- self.assertEqual(r.json(), dict(network='192.0.2.0/24', view='view1'))
+ self.assertEqual(r.json(), dict(network="192.0.2.0/24", view="view1"))
# empty view name is equivalent to delete
- r = self.set_network('192.0.2.0/24', view='')
+ r = self.set_network("192.0.2.0/24", view="")
print(r.content)
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
# Check network absence
nets = self.get_networks()
- self.assertNotIn('192.0.2.0/24', nets)
+ self.assertNotIn("192.0.2.0/24", nets)
# Check individual fetch
- r = self.get_network('192.0.2.0/24')
+ r = self.get_network("192.0.2.0/24")
print(r.content)
self.assertEqual(r.status_code, 404)
def set_network(self, prefix, **content):
r = self.session.put(
- self.url("/api/v1/servers/localhost/networks/"+prefix),
+ self.url("/api/v1/servers/localhost/networks/" + prefix),
data=json.dumps(content),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
return r
def get_network(self, prefix):
r = self.session.get(
- self.url("/api/v1/servers/localhost/networks/"+prefix),
- headers={'content-type': 'application/json'})
+ self.url("/api/v1/servers/localhost/networks/" + prefix), headers={"content-type": "application/json"}
+ )
return r
def get_networks(self):
r = self.session.get(
- self.url("/api/v1/servers/localhost/networks"),
- headers={'content-type': 'application/json'})
+ self.url("/api/v1/servers/localhost/networks"), headers={"content-type": "application/json"}
+ )
ret = {}
- for netview in r.json()['networks']:
- net = netview['network']
- view = netview['view']
+ for netview in r.json()["networks"]:
+ net = netview["network"]
+ view = netview["view"]
self.assertNotIn(net, ret)
ret[net] = view
return ret
+
@unittest.skipIf(not is_auth(), "Not applicable")
@unittest.skipIf(not is_auth_lmdb(), "Views require the LMDB backend")
class Views(ApiTestCase, AuthZonesHelperMixin):
def setUp(self):
super(Views, self).setUp()
- self.create_zone('example.com..spiceoflife')
+ self.create_zone("example.com..spiceoflife")
def tearDown(self):
super(Views, self).tearDown()
self.session.delete(self.url("/api/v1/servers/localhost/zones/example.com..spiceoflife"))
- def _test_views(self, variant=''):
- zone = 'example.com.' + variant
- r = self.set_view_zone('view1', zone)
+ def _test_views(self, variant=""):
+ zone = "example.com." + variant
+ r = self.set_view_zone("view1", zone)
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
# Check view presence
r = self.get_views()
self.assertEqual(r.status_code, 200)
- self.assertIn('view1', r.json()["views"])
+ self.assertIn("view1", r.json()["views"])
# Check individual fetch
- r = self.get_view('view1')
+ r = self.get_view("view1")
print(r)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json()["zones"], [zone])
- r = self.del_view_zone('view1', zone)
+ r = self.del_view_zone("view1", zone)
print(r.content)
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
# Check view absence
r = self.get_views()
self.assertEqual(r.status_code, 200)
- self.assertNotIn('view1', r.json()["views"])
+ self.assertNotIn("view1", r.json()["views"])
# Check individual fetch
- r = self.get_view('view1')
+ r = self.get_view("view1")
print(r.content)
self.assertEqual(r.status_code, 404)
def test_zonelist_variant(self):
- r = self.session.get(
- self.url("/api/v1/servers/localhost/zones"),
- headers={'content-type': 'application/json'})
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones"), headers={"content-type": "application/json"})
self.assertEqual(r.status_code, 200)
self.assertIn("example.com..spiceoflife", [obj["name"] for obj in r.json()])
return self._test_views()
def test_views_variant(self):
- return self._test_views('.spiceoflife')
+ return self._test_views(".spiceoflife")
def set_view_zone(self, view, zone):
r = self.session.post(
- self.url("/api/v1/servers/localhost/views/"+view),
+ self.url("/api/v1/servers/localhost/views/" + view),
data=json.dumps(dict(name=zone)),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
def del_view_zone(self, view, zone):
r = self.session.delete(
- self.url("/api/v1/servers/localhost/views/"+view+"/"+zone),
+ self.url("/api/v1/servers/localhost/views/" + view + "/" + zone),
data=json.dumps(dict(name=zone)),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
def get_view(self, view):
r = self.session.get(
- self.url("/api/v1/servers/localhost/views/"+view),
- headers={'content-type': 'application/json'})
+ self.url("/api/v1/servers/localhost/views/" + view), headers={"content-type": "application/json"}
+ )
return r
def get_views(self):
- r = self.session.get(
- self.url("/api/v1/servers/localhost/views"),
- headers={'content-type': 'application/json'})
+ r = self.session.get(self.url("/api/v1/servers/localhost/views"), headers={"content-type": "application/json"})
self.assertEqual(r.status_code, 200)
from copy import deepcopy
from parameterized import parameterized
from pprint import pprint
-from test_helper import ApiTestCase, unique_zone_name, is_auth, is_auth_lmdb, is_recursor, get_auth_db, get_db_records, pdnsutil_rectify, sdig
+from test_helper import (
+ ApiTestCase,
+ unique_zone_name,
+ is_auth,
+ is_auth_lmdb,
+ is_recursor,
+ get_auth_db,
+ get_db_records,
+ pdnsutil_rectify,
+ sdig,
+)
def remove_timestamp(json):
for item in json:
- if 'modified_at' in item:
- del item['modified_at']
+ if "modified_at" in item:
+ del item["modified_at"]
-def get_rrset(data, qname, qtype = None):
- for rrset in data['rrsets']:
- if rrset['name'] == qname and (qtype is None or rrset['type'] == qtype):
- remove_timestamp(rrset['records'])
+
+def get_rrset(data, qname, qtype=None):
+ for rrset in data["rrsets"]:
+ if rrset["name"] == qname and (qtype is None or rrset["type"] == qtype):
+ remove_timestamp(rrset["records"])
return rrset
return None
+
def get_first_rec(data, qname, qtype):
rrset = get_rrset(data, qname, qtype)
if rrset:
- return rrset['records'][0]
+ return rrset["records"][0]
return None
type_ = str(type_)
data_got[type_] = set()
data_expected[type_] = set()
- uses_name = any(['name' in expected_record for expected_record in expected_records])
+ uses_name = any(["name" in expected_record for expected_record in expected_records])
# minify + convert received data
- for rrset in [rrset for rrset in rrsets if rrset['type'] == type_]:
+ for rrset in [rrset for rrset in rrsets if rrset["type"] == type_]:
print(rrset)
- for r in rrset['records']:
- data_got[type_].add((rrset['name'] if uses_name else '@', rrset['type'], r['content']))
+ for r in rrset["records"]:
+ data_got[type_].add((rrset["name"] if uses_name else "@", rrset["type"], r["content"]))
# minify expected data
for r in expected_records:
- data_expected[type_].add((r['name'] if uses_name else '@', type_, r['content']))
+ data_expected[type_].add((r["name"] if uses_name else "@", type_, r["content"]))
print("eq_zone_rrsets: got:")
pprint(data_got)
assert data_got == data_expected, "%r != %r" % (data_got, data_expected)
+
def assert_eq_rrsets(rrsets, expected):
"""Assert rrsets sets are equal, ignoring sort order."""
- key = lambda rrset: (rrset['name'], rrset['type'])
+ key = lambda rrset: (rrset["name"], rrset["type"])
assert sorted(rrsets, key=key) == sorted(expected, key=key)
+
def templated_rrsets(rrsets: list, zonename: str):
"""
Replace $NAME$ in `name` and `content` of given rrsets with `zonename`.
"""
new_rrsets = []
for rrset in rrsets:
- new_rrset = rrset | {"name": rrset["name"].replace('$NAME$', zonename)}
+ new_rrset = rrset | {"name": rrset["name"].replace("$NAME$", zonename)}
if "records" in rrset:
records = []
for record in rrset["records"]:
- records.append(record | {"content": record["content"].replace('$NAME$', zonename)})
+ records.append(record | {"content": record["content"].replace("$NAME$", zonename)})
new_rrset["records"] = records
new_rrsets.append(new_rrset)
return new_rrsets
-class ZonesApiTestCase(ApiTestCase):
+class ZonesApiTestCase(ApiTestCase):
def assert_in_json_error(self, expected, json):
- error = json['error']
+ error = json["error"]
if expected not in error:
found = False
- if 'errors' in json:
- errors = json['errors']
+ if "errors" in json:
+ errors = json["errors"]
for item in errors:
if expected in item:
found = True
assert found, "%r not found in %r" % (expected, errors)
assert found, "%r not found in %r" % (expected, error)
-class Zones(ZonesApiTestCase):
+class Zones(ZonesApiTestCase):
def _test_list_zones(self, dnssec=True):
path = "/api/v1/servers/localhost/zones"
if not dnssec:
r = self.session.get(self.url(path))
self.assert_success_json(r)
domains = r.json()
- example_com = [domain for domain in domains if domain['name'] in ('example.com', 'example.com.')]
+ example_com = [domain for domain in domains if domain["name"] in ("example.com", "example.com.")]
self.assertEqual(len(example_com), 1)
example_com = example_com[0]
print(example_com)
- required_fields = ['id', 'url', 'name', 'kind']
+ required_fields = ["id", "url", "name", "kind"]
if is_auth():
- required_fields = required_fields + ['masters', 'last_check', 'notified_serial', 'serial', 'account', 'catalog']
+ required_fields = required_fields + [
+ "masters",
+ "last_check",
+ "notified_serial",
+ "serial",
+ "account",
+ "catalog",
+ ]
if dnssec:
- required_fields = required_fields + ['dnssec', 'edited_serial']
- self.assertNotEqual(example_com['serial'], 0)
+ required_fields = required_fields + ["dnssec", "edited_serial"]
+ self.assertNotEqual(example_com["serial"], 0)
if not dnssec:
- self.assertNotIn('dnssec', example_com)
+ self.assertNotIn("dnssec", example_com)
elif is_recursor():
- required_fields = required_fields + ['recursion_desired', 'servers']
+ required_fields = required_fields + ["recursion_desired", "servers"]
for field in required_fields:
self.assertIn(field, example_com)
def create_zone(self, name=None, expect_error=None, **kwargs):
if name is None:
name = unique_zone_name()
- payload = {
- "name": name,
- "kind": "Native",
- "nameservers": ["ns1.example.com.", "ns2.example.com."]
- }
+ payload = {"name": name, "kind": "Native", "nameservers": ["ns1.example.com.", "ns2.example.com."]}
for k, v in kwargs.items():
if v is None:
del payload[k]
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={"content-type": "application/json"})
+ headers={"content-type": "application/json"},
+ )
if expect_error:
self.assertEqual(r.status_code, 422, r.content)
def get_zone(self, api_zone_id, expect_error=None, **kwargs):
print("GET zone", api_zone_id, "args:", kwargs)
- r = self.session.get(
- self.url("/api/v1/servers/localhost/zones/" + api_zone_id),
- params=kwargs
- )
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + api_zone_id), params=kwargs)
reply = r.json()
print("reply", reply)
r = self.session.put(
self.url("/api/v1/servers/localhost/zones/" + api_zone_id),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
print("reply status code:", r.status_code)
if expect_error:
# expect success (no content)
self.assertEqual(r.status_code, 204, r.content)
+
@unittest.skipIf(not is_auth(), "Not applicable")
class AuthZones(ZonesApiTestCase, AuthZonesHelperMixin):
-
def test_create_zone(self):
# soa_edit_api has a default, override with empty for this test
- name, payload, data = self.create_zone(serial=22, soa_edit_api='')
- for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'edited_serial', 'soa_edit_api', 'soa_edit', 'account'):
+ name, payload, data = self.create_zone(serial=22, soa_edit_api="")
+ for k in (
+ "id",
+ "url",
+ "name",
+ "masters",
+ "kind",
+ "last_check",
+ "notified_serial",
+ "serial",
+ "edited_serial",
+ "soa_edit_api",
+ "soa_edit",
+ "account",
+ ):
self.assertIn(k, data)
if k in payload:
self.assertEqual(data[k], payload[k])
# validate generated SOA
- expected_soa = "a.misconfigured.dns.server.invalid. hostmaster." + name + " " + \
- str(payload['serial']) + " 10800 3600 604800 3600"
- self.assertEqual(
- get_first_rec(data, name, 'SOA')['content'],
- expected_soa
+ expected_soa = (
+ "a.misconfigured.dns.server.invalid. hostmaster."
+ + name
+ + " "
+ + str(payload["serial"])
+ + " 10800 3600 604800 3600"
)
+ self.assertEqual(get_first_rec(data, name, "SOA")["content"], expected_soa)
if not is_auth_lmdb():
# Because we had confusion about dots, check that the DB is without dots.
- dbrecs = get_db_records(name, 'SOA')
- self.assertEqual(dbrecs[0]['content'], expected_soa.replace('. ', ' '))
- self.assertNotEqual(data['serial'], data['edited_serial'])
+ dbrecs = get_db_records(name, "SOA")
+ self.assertEqual(dbrecs[0]["content"], expected_soa.replace(". ", " "))
+ self.assertNotEqual(data["serial"], data["edited_serial"])
def test_create_zone_with_soa_edit_api(self):
# soa_edit_api wins over serial
- name, payload, data = self.create_zone(soa_edit_api='EPOCH', serial=10)
- for k in ('soa_edit_api', ):
+ name, payload, data = self.create_zone(soa_edit_api="EPOCH", serial=10)
+ for k in ("soa_edit_api",):
self.assertIn(k, data)
if k in payload:
self.assertEqual(data[k], payload[k])
# generated EPOCH serial surely is > fixed serial we passed in
print(data)
- self.assertGreater(data['serial'], payload['serial'])
- soa_serial = int(get_first_rec(data, name, 'SOA')['content'].split(' ')[2])
- self.assertGreater(soa_serial, payload['serial'])
- self.assertEqual(soa_serial, data['serial'])
+ self.assertGreater(data["serial"], payload["serial"])
+ soa_serial = int(get_first_rec(data, name, "SOA")["content"].split(" ")[2])
+ self.assertGreater(soa_serial, payload["serial"])
+ self.assertEqual(soa_serial, data["serial"])
def test_create_zone_with_catalog(self):
# soa_edit_api wins over serial
- name, payload, data = self.create_zone(catalog='catalog.invalid.', serial=10)
+ name, payload, data = self.create_zone(catalog="catalog.invalid.", serial=10)
print(data)
- for k in ('catalog', ):
+ for k in ("catalog",):
self.assertIn(k, data)
if k in payload:
self.assertEqual(data[k], payload[k])
r = self.session.get(self.url("/api/v1/servers/localhost/zones"))
self.assert_success_json(r)
domains = r.json()
- domain = [domain for domain in domains if domain['name'] == name]
+ domain = [domain for domain in domains if domain["name"] == name]
self.assertEqual(len(domain), 1)
domain = domain[0]
self.assertEqual(domain["catalog"], "catalog.invalid.")
def test_create_zone_with_account(self):
# soa_edit_api wins over serial
- name, payload, data = self.create_zone(account='anaccount', serial=10, kind='Master')
+ name, payload, data = self.create_zone(account="anaccount", serial=10, kind="Master")
print(data)
- for k in ('account', ):
+ for k in ("account",):
self.assertIn(k, data)
if k in payload:
self.assertEqual(data[k], payload[k])
# as we did not set a catalog in our request, check that the default catalog was applied
- self.assertEqual(data['catalog'], "default-catalog.example.com.")
+ self.assertEqual(data["catalog"], "default-catalog.example.com.")
def test_create_zone_default_soa_edit_api(self):
"""Test that zones created without soa_edit_api get the configured default-soa-edit-api value."""
name, payload, data = self.create_zone()
print(data)
# default-soa-edit-api is set to EPOCH in the test config
- self.assertEqual(data['soa_edit_api'], 'EPOCH')
+ self.assertEqual(data["soa_edit_api"], "EPOCH")
def test_create_zone_override_soa_edit_api(self):
"""Test that explicitly setting soa_edit_api overrides the default."""
- name, payload, data = self.create_zone(soa_edit_api='SOA-EDIT-INCREASE')
+ name, payload, data = self.create_zone(soa_edit_api="SOA-EDIT-INCREASE")
print(data)
- self.assertEqual(data['soa_edit_api'], 'SOA-EDIT-INCREASE')
+ self.assertEqual(data["soa_edit_api"], "SOA-EDIT-INCREASE")
def test_create_zone_empty_soa_edit_api(self):
"""Test that explicitly setting soa_edit_api to empty does not use default."""
- name, payload, data = self.create_zone(soa_edit_api='')
+ name, payload, data = self.create_zone(soa_edit_api="")
print(data)
- self.assertEqual(data['soa_edit_api'], '')
+ self.assertEqual(data["soa_edit_api"], "")
def test_update_zone_does_not_override_soa_edit_api(self):
"""Test that updating a zone does not change existing soa_edit_api to default."""
# Create zone with empty soa_edit_api
- name, payload, data = self.create_zone(soa_edit_api='')
- self.assertEqual(data['soa_edit_api'], '')
+ name, payload, data = self.create_zone(soa_edit_api="")
+ self.assertEqual(data["soa_edit_api"], "")
# Update zone without specifying soa_edit_api - it should remain empty
- update_payload = {
- 'kind': 'Master',
- 'masters': ['192.0.2.1']
- }
+ update_payload = {"kind": "Master", "masters": ["192.0.2.1"]}
self.put_zone(name, update_payload)
data = self.get_zone(name)
# Verify soa_edit_api was NOT changed to the default value
- self.assertEqual(data['soa_edit_api'], '')
+ self.assertEqual(data["soa_edit_api"], "")
def test_create_zone_exists(self):
name, payload, data = self.create_zone()
print(data)
- payload = {
- 'name': name,
- 'kind': 'Native'
- }
+ payload = {"name": name, "kind": "Native"}
print(payload)
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 409) # Conflict - already exists
- self.assertIn("Conflict", r.json()['error'])
+ self.assertIn("Conflict", r.json()["error"])
def test_create_zone_with_soa_edit(self):
- name, payload, data = self.create_zone(soa_edit='INCEPTION-INCREMENT', soa_edit_api='SOA-EDIT-INCREASE')
+ name, payload, data = self.create_zone(soa_edit="INCEPTION-INCREMENT", soa_edit_api="SOA-EDIT-INCREASE")
print(data)
- self.assertEqual(data['soa_edit'], 'INCEPTION-INCREMENT')
- self.assertEqual(data['soa_edit_api'], 'SOA-EDIT-INCREASE')
- soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2]
+ self.assertEqual(data["soa_edit"], "INCEPTION-INCREMENT")
+ self.assertEqual(data["soa_edit_api"], "SOA-EDIT-INCREASE")
+ soa_serial = get_first_rec(data, name, "SOA")["content"].split(" ")[2]
# These particular settings lead to the first serial set to YYYYMMDD01.
- self.assertEqual(soa_serial[-2:], '01')
+ self.assertEqual(soa_serial[-2:], "01")
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [
- {
- "content": "127.0.0.1",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "A",
+ "ttl": 3600,
+ "records": [{"content": "127.0.0.1", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
- self.url("/api/v1/servers/localhost/zones/" + data['id']),
+ self.url("/api/v1/servers/localhost/zones/" + data["id"]),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
- data = self.get_zone(data['id'])
- soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2]
- self.assertEqual(soa_serial[-2:], '02')
- self.assertEqual(r.headers['X-PDNS-Old-Serial'][-2:], '01')
- self.assertEqual(r.headers['X-PDNS-New-Serial'][-2:], '02')
+ headers={"content-type": "application/json"},
+ )
+ data = self.get_zone(data["id"])
+ soa_serial = get_first_rec(data, name, "SOA")["content"].split(" ")[2]
+ self.assertEqual(soa_serial[-2:], "02")
+ self.assertEqual(r.headers["X-PDNS-Old-Serial"][-2:], "01")
+ self.assertEqual(r.headers["X-PDNS-New-Serial"][-2:], "02")
def test_create_zone_with_records(self):
name = unique_zone_name()
"name": name,
"type": "A",
"ttl": 3600,
- "records": [{
- "content": "4.3.2.1",
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": "4.3.2.1",
+ "disabled": False,
+ }
+ ],
}
name, payload, data = self.create_zone(name=name, rrsets=[rrset])
# check our record has appeared
- self.assertEqual(get_rrset(data, name, 'A')['records'], rrset['records'])
+ self.assertEqual(get_rrset(data, name, "A")["records"], rrset["records"])
def test_create_zone_with_records_ordered(self):
name = unique_zone_name()
{
"content": "ns1.example.net.",
"disabled": False,
- }
+ },
],
}
name, payload, data = self.create_zone(name=name, rrsets=[rrset_a, rrset_ns])
# check our record has appeared
- self.assertEqual(get_rrset(data, name, 'A')['records'], [
- # The content should be lexographically ordered when retrieving
- {
- "content": "127.0.0.1",
- "disabled": False,
- },
- {
- "content": "4.3.2.1",
- "disabled": False,
- },
- ])
+ self.assertEqual(
+ get_rrset(data, name, "A")["records"],
+ [
+ # The content should be lexographically ordered when retrieving
+ {
+ "content": "127.0.0.1",
+ "disabled": False,
+ },
+ {
+ "content": "4.3.2.1",
+ "disabled": False,
+ },
+ ],
+ )
- self.assertEqual(get_rrset(data, rrset_ns['name'], 'NS')['records'], [
- {
- "content": "ns1.example.net.",
- "disabled": False,
- },
- {
- "content": "ns2.example.com.",
- "disabled": False,
- }
- ])
+ self.assertEqual(
+ get_rrset(data, rrset_ns["name"], "NS")["records"],
+ [
+ {
+ "content": "ns1.example.net.",
+ "disabled": False,
+ },
+ {
+ "content": "ns2.example.com.",
+ "disabled": False,
+ },
+ ],
+ )
def test_create_zone_with_wildcard_records(self):
name = unique_zone_name()
rrset = {
- "name": "*."+name,
+ "name": "*." + name,
"type": "A",
"ttl": 3600,
- "records": [{
- "content": "4.3.2.1",
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": "4.3.2.1",
+ "disabled": False,
+ }
+ ],
}
name, payload, data = self.create_zone(name=name, rrsets=[rrset])
# check our record has appeared
- self.assertEqual(get_rrset(data, rrset['name'], 'A')['records'], rrset['records'])
+ self.assertEqual(get_rrset(data, rrset["name"], "A")["records"], rrset["records"])
def test_create_zone_with_comments(self):
name = unique_zone_name()
rrsets = [
- {
- "name": name,
- "type": "soa", # test uppercasing of type, too.
- "comments": [{
- "account": "test1",
- "content": "blah blah and test a few non-ASCII chars: ö, €",
- "modified_at": 11112,
- }],
- },
- {
- "name": name,
- "type": "AAAA",
- "ttl": 3600,
- "records": [{
- "content": "2001:DB8::1",
- "disabled": False,
- }],
- "comments": [{
- "account": "test AAAA",
- "content": "blah blah AAAA",
- "modified_at": 11112,
- }],
- },
- {
- "name": name,
- "type": "TXT",
- "ttl": 3600,
- "records": [{
- "content": "\"test TXT\"",
- "disabled": False,
- }],
- },
- {
- "name": name,
- "type": "A",
- "ttl": 3600,
- "records": [{
- "content": "192.0.2.1",
- "disabled": False,
- }],
- },
- ]
+ {
+ "name": name,
+ "type": "soa", # test uppercasing of type, too.
+ "comments": [
+ {
+ "account": "test1",
+ "content": "blah blah and test a few non-ASCII chars: ö, €",
+ "modified_at": 11112,
+ }
+ ],
+ },
+ {
+ "name": name,
+ "type": "AAAA",
+ "ttl": 3600,
+ "records": [
+ {
+ "content": "2001:DB8::1",
+ "disabled": False,
+ }
+ ],
+ "comments": [
+ {
+ "account": "test AAAA",
+ "content": "blah blah AAAA",
+ "modified_at": 11112,
+ }
+ ],
+ },
+ {
+ "name": name,
+ "type": "TXT",
+ "ttl": 3600,
+ "records": [
+ {
+ "content": '"test TXT"',
+ "disabled": False,
+ }
+ ],
+ },
+ {
+ "name": name,
+ "type": "A",
+ "ttl": 3600,
+ "records": [
+ {
+ "content": "192.0.2.1",
+ "disabled": False,
+ }
+ ],
+ },
+ ]
if is_auth_lmdb():
# No comments in LMDB
- self.create_zone(name=name, rrsets=rrsets, expect_error="Hosting backend does not support editing comments.")
+ self.create_zone(
+ name=name, rrsets=rrsets, expect_error="Hosting backend does not support editing comments."
+ )
return
name, _, data = self.create_zone(name=name, rrsets=rrsets)
# NS records have been created
- self.assertEqual(len(data['rrsets']), len(rrsets) + 1)
+ self.assertEqual(len(data["rrsets"]), len(rrsets) + 1)
# check our comment has appeared
- self.assertEqual(get_rrset(data, name, 'SOA')['comments'], rrsets[0]['comments'])
- self.assertEqual(get_rrset(data, name, 'A')['comments'], [])
- self.assertEqual(get_rrset(data, name, 'TXT')['comments'], [])
- self.assertEqual(get_rrset(data, name, 'AAAA')['comments'], rrsets[1]['comments'])
+ self.assertEqual(get_rrset(data, name, "SOA")["comments"], rrsets[0]["comments"])
+ self.assertEqual(get_rrset(data, name, "A")["comments"], [])
+ self.assertEqual(get_rrset(data, name, "TXT")["comments"], [])
+ self.assertEqual(get_rrset(data, name, "AAAA")["comments"], rrsets[1]["comments"])
def test_create_zone_uncanonical_nameservers(self):
name = unique_zone_name()
- payload = {
- 'name': name,
- 'kind': 'Native',
- 'nameservers': ['uncanon.example.com']
- }
+ payload = {"name": name, "kind": "Native", "nameservers": ["uncanon.example.com"]}
print(payload)
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('Nameserver is not canonical', r.json())
+ self.assert_in_json_error("Nameserver is not canonical", r.json())
def test_create_auth_zone_no_name(self):
payload = {
- 'name': '',
- 'kind': 'Native',
+ "name": "",
+ "kind": "Native",
}
print(payload)
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('is not canonical', r.json())
+ self.assert_in_json_error("is not canonical", r.json())
def test_create_zone_with_custom_soa(self):
name = unique_zone_name()
- content = u"ns1.example.net. testmaster@example.net. 10 10800 3600 604800 3600"
+ content = "ns1.example.net. testmaster@example.net. 10 10800 3600 604800 3600"
rrset = {
"name": name,
"type": "soa", # test uppercasing of type, too.
"ttl": 3600,
- "records": [{
- "content": content,
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": content,
+ "disabled": False,
+ }
+ ],
}
- name, payload, data = self.create_zone(name=name, rrsets=[rrset], soa_edit_api='')
- self.assertEqual(get_rrset(data, name, 'SOA')['records'], rrset['records'])
+ name, payload, data = self.create_zone(name=name, rrsets=[rrset], soa_edit_api="")
+ self.assertEqual(get_rrset(data, name, "SOA")["records"], rrset["records"])
if not is_auth_lmdb():
- dbrecs = get_db_records(name, 'SOA')
- self.assertEqual(dbrecs[0]['content'], content.replace('. ', ' '))
+ dbrecs = get_db_records(name, "SOA")
+ self.assertEqual(dbrecs[0]["content"], content.replace(". ", " "))
def test_create_zone_double_dot(self):
- name = 'test..' + unique_zone_name()
- payload = {
- 'name': name,
- 'kind': 'Native',
- 'nameservers': ['ns1.example.com.']
- }
+ name = "test.." + unique_zone_name()
+ payload = {"name": name, "kind": "Native", "nameservers": ["ns1.example.com."]}
print(payload)
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('Unable to parse Zone Name', r.json())
+ self.assert_in_json_error("Unable to parse Zone Name", r.json())
def test_create_zone_restricted_chars(self):
- name = 'test:' + unique_zone_name() # : isn't good as a name.
- payload = {
- 'name': name,
- 'kind': 'Native',
- 'nameservers': ['ns1.example.com']
- }
+ name = "test:" + unique_zone_name() # : isn't good as a name.
+ payload = {"name": name, "kind": "Native", "nameservers": ["ns1.example.com"]}
print(payload)
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('contains unsupported characters', r.json())
+ self.assert_in_json_error("contains unsupported characters", r.json())
def test_create_zone_mixed_nameservers_ns_rrset_zonelevel(self):
name = unique_zone_name()
"name": name,
"type": "NS",
"ttl": 3600,
- "records": [{
- "content": "ns2.example.com.",
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": "ns2.example.com.",
+ "disabled": False,
+ }
+ ],
}
payload = {
- 'name': name,
- 'kind': 'Native',
- 'nameservers': ['ns1.example.com.'],
- 'rrsets': [rrset],
+ "name": name,
+ "kind": "Native",
+ "nameservers": ["ns1.example.com."],
+ "rrsets": [rrset],
}
print(payload)
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('Nameservers list MUST NOT be mixed with zone-level NS in rrsets', r.json())
+ self.assert_in_json_error("Nameservers list MUST NOT be mixed with zone-level NS in rrsets", r.json())
def test_create_zone_mixed_nameservers_ns_rrset_below_zonelevel(self):
name = unique_zone_name()
rrset = {
- "name": 'subzone.'+name,
+ "name": "subzone." + name,
"type": "NS",
"ttl": 3600,
- "records": [{
- "content": "ns2.example.com.",
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": "ns2.example.com.",
+ "disabled": False,
+ }
+ ],
}
payload = {
- 'name': name,
- 'kind': 'Native',
- 'nameservers': ['ns1.example.com.'],
- 'rrsets': [rrset],
+ "name": name,
+ "kind": "Native",
+ "nameservers": ["ns1.example.com."],
+ "rrsets": [rrset],
}
print(payload)
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success_json(r)
def test_create_zone_with_symbols(self):
- name, payload, data = self.create_zone(name='foo/bar.'+unique_zone_name())
- name = payload['name']
- expected_id = name.replace('/', '=2F')
- for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial'):
+ name, payload, data = self.create_zone(name="foo/bar." + unique_zone_name())
+ name = payload["name"]
+ expected_id = name.replace("/", "=2F")
+ for k in ("id", "url", "name", "masters", "kind", "last_check", "notified_serial", "serial"):
self.assertIn(k, data)
if k in payload:
self.assertEqual(data[k], payload[k])
- self.assertEqual(data['id'], expected_id)
+ self.assertEqual(data["id"], expected_id)
if not is_auth_lmdb():
- dbrecs = get_db_records(name, 'SOA')
- self.assertEqual(dbrecs[0]['name'], name.rstrip('.'))
+ dbrecs = get_db_records(name, "SOA")
+ self.assertEqual(dbrecs[0]["name"], name.rstrip("."))
def test_create_zone_with_nameservers_non_string(self):
# ensure we don't crash
name = unique_zone_name()
payload = {
- 'name': name,
- 'kind': 'Native',
- 'nameservers': [{'a': 'ns1.example.com'}] # invalid
+ "name": name,
+ "kind": "Native",
+ "nameservers": [{"a": "ns1.example.com"}], # invalid
}
print(payload)
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
def test_create_zone_with_dnssec(self):
self.get_zone(name)
- for k in ('dnssec', ):
+ for k in ("dnssec",):
self.assertIn(k, data)
if k in payload:
self.assertEqual(data[k], payload[k])
- r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + '/cryptokeys'))
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + "/cryptokeys"))
keys = r.json()
self.assertEqual(r.status_code, 200)
self.assertEqual(len(keys), 1)
- self.assertEqual(keys[0]['type'], 'Cryptokey')
- self.assertEqual(keys[0]['active'], True)
- self.assertEqual(keys[0]['keytype'], 'csk')
+ self.assertEqual(keys[0]["type"], "Cryptokey")
+ self.assertEqual(keys[0]["active"], True)
+ self.assertEqual(keys[0]["keytype"], "csk")
def test_create_zone_with_dnssec_disable_dnssec(self):
"""
"""
name, payload, data = self.create_zone(dnssec=True)
- self.put_zone(name, {'dnssec': False})
+ self.put_zone(name, {"dnssec": False})
zoneinfo = self.get_zone(name)
- self.assertEqual(zoneinfo['dnssec'], False)
+ self.assertEqual(zoneinfo["dnssec"], False)
- r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + '/cryptokeys'))
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + "/cryptokeys"))
keys = r.json()
"""
Create a zone with "nsec3param" set and see if the metadata was added.
"""
- nsec3param = '1 0 100 aabbccddeeff'
+ nsec3param = "1 0 100 aabbccddeeff"
name, payload, data = self.create_zone(dnssec=True, nsec3param=nsec3param)
self.get_zone(name)
- for k in ('dnssec', 'nsec3param'):
+ for k in ("dnssec", "nsec3param"):
self.assertIn(k, data)
if k in payload:
self.assertEqual(data[k], payload[k])
- r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + '/metadata/NSEC3PARAM'))
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + "/metadata/NSEC3PARAM"))
data = r.json()
print(data)
self.assertEqual(r.status_code, 200)
- self.assertEqual(len(data['metadata']), 1)
- self.assertEqual(data['kind'], 'NSEC3PARAM')
- self.assertEqual(data['metadata'][0], nsec3param)
+ self.assertEqual(len(data["metadata"]), 1)
+ self.assertEqual(data["kind"], "NSEC3PARAM")
+ self.assertEqual(data["metadata"][0], nsec3param)
def test_create_zone_with_nsec3narrow(self):
"""
Create a zone with "nsec3narrow" set and see if the metadata was added.
"""
- nsec3param = '1 0 100 aabbccddeeff'
- name, payload, data = self.create_zone(dnssec=True, nsec3param=nsec3param,
- nsec3narrow=True)
+ nsec3param = "1 0 100 aabbccddeeff"
+ name, payload, data = self.create_zone(dnssec=True, nsec3param=nsec3param, nsec3narrow=True)
self.get_zone(name)
- for k in ('dnssec', 'nsec3param', 'nsec3narrow'):
+ for k in ("dnssec", "nsec3param", "nsec3narrow"):
self.assertIn(k, data)
if k in payload:
self.assertEqual(data[k], payload[k])
- r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + '/metadata/NSEC3NARROW'))
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + "/metadata/NSEC3NARROW"))
data = r.json()
print(data)
self.assertEqual(r.status_code, 200)
- self.assertEqual(len(data['metadata']), 1)
- self.assertEqual(data['kind'], 'NSEC3NARROW')
- self.assertEqual(data['metadata'][0], '1')
+ self.assertEqual(len(data["metadata"]), 1)
+ self.assertEqual(data["kind"], "NSEC3NARROW")
+ self.assertEqual(data["metadata"][0], "1")
def test_create_zone_with_nsec3param_switch_to_nsec(self):
"""
Create a zone with "nsec3param", then remove the params
"""
- name, payload, data = self.create_zone(dnssec=True,
- nsec3param='1 0 1 ab')
- self.put_zone(name, {'nsec3param': ''})
+ name, payload, data = self.create_zone(dnssec=True, nsec3param="1 0 1 ab")
+ self.put_zone(name, {"nsec3param": ""})
data = self.get_zone(name)
- self.assertEqual(data['nsec3param'], '')
+ self.assertEqual(data["nsec3param"], "")
def test_create_zone_without_dnssec_unset_nsec3parm(self):
"""
Create a non dnssec zone and set an empty "nsec3param"
"""
name, payload, data = self.create_zone(dnssec=False)
- self.put_zone(name, {'nsec3param': ''})
+ self.put_zone(name, {"nsec3param": ""})
def test_create_zone_without_dnssec_set_nsec3parm(self):
"""
Create a non dnssec zone and set "nsec3param"
"""
name, payload, data = self.create_zone(dnssec=False)
- self.put_zone(name, {'nsec3param': '1 0 1 ab'}, expect_error=True)
+ self.put_zone(name, {"nsec3param": "1 0 1 ab"}, expect_error=True)
def test_create_zone_dnssec_serial(self):
"""
after every step
"""
# Use soa_edit_api='DEFAULT' to get predictable INCEPTION-INCREMENT serials
- name, payload, data = self.create_zone(soa_edit_api='DEFAULT')
+ name, payload, data = self.create_zone(soa_edit_api="DEFAULT")
- soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2]
- self.assertEqual(soa_serial[-2:], '01')
+ soa_serial = get_first_rec(data, name, "SOA")["content"].split(" ")[2]
+ self.assertEqual(soa_serial[-2:], "01")
- self.put_zone(name, {'dnssec': True})
+ self.put_zone(name, {"dnssec": True})
data = self.get_zone(name)
- soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2]
- self.assertEqual(soa_serial[-2:], '02')
+ soa_serial = get_first_rec(data, name, "SOA")["content"].split(" ")[2]
+ self.assertEqual(soa_serial[-2:], "02")
- self.put_zone(name, {'dnssec': False})
+ self.put_zone(name, {"dnssec": False})
data = self.get_zone(name)
- soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2]
- self.assertEqual(soa_serial[-2:], '03')
+ soa_serial = get_first_rec(data, name, "SOA")["content"].split(" ")[2]
+ self.assertEqual(soa_serial[-2:], "03")
def test_zone_absolute_url(self):
self.create_zone()
r = self.session.get(self.url("/api/v1/servers/localhost/zones"))
rdata = r.json()
print(rdata[0])
- self.assertTrue(rdata[0]['url'].startswith('/api/v'))
+ self.assertTrue(rdata[0]["url"].startswith("/api/v"))
def test_create_zone_metadata(self):
payload_metadata = {"type": "Metadata", "kind": "AXFR-SOURCE", "metadata": ["127.0.0.2"]}
- r = self.session.post(self.url("/api/v1/servers/localhost/zones/example.com/metadata"),
- data=json.dumps(payload_metadata))
+ r = self.session.post(
+ self.url("/api/v1/servers/localhost/zones/example.com/metadata"), data=json.dumps(payload_metadata)
+ )
rdata = r.json()
self.assertEqual(r.status_code, 201)
self.assertEqual(rdata["metadata"], payload_metadata["metadata"])
def test_create_zone_metadata_kind(self):
payload_metadata = {"metadata": ["127.0.0.2"]}
- r = self.session.put(self.url("/api/v1/servers/localhost/zones/example.com/metadata/AXFR-SOURCE"),
- data=json.dumps(payload_metadata))
+ r = self.session.put(
+ self.url("/api/v1/servers/localhost/zones/example.com/metadata/AXFR-SOURCE"),
+ data=json.dumps(payload_metadata),
+ )
rdata = r.json()
self.assertEqual(r.status_code, 200)
self.assertEqual(rdata["metadata"], payload_metadata["metadata"])
# test whether it prevents modification of certain kinds
for k in ("NSEC3NARROW", "NSEC3PARAM", "PRESIGNED", "LUA-AXFR-SCRIPT"):
payload = {"metadata": ["FOO", "BAR"]}
- r = self.session.put(self.url("/api/v1/servers/localhost/zones/example.com/metadata/%s" % k),
- data=json.dumps(payload))
+ r = self.session.put(
+ self.url("/api/v1/servers/localhost/zones/example.com/metadata/%s" % k), data=json.dumps(payload)
+ )
self.assertEqual(r.status_code, 422)
def test_retrieve_zone_metadata(self):
payload_metadata = {"type": "Metadata", "kind": "AXFR-SOURCE", "metadata": ["127.0.0.2"]}
- self.session.post(self.url("/api/v1/servers/localhost/zones/example.com/metadata"),
- data=json.dumps(payload_metadata))
+ self.session.post(
+ self.url("/api/v1/servers/localhost/zones/example.com/metadata"), data=json.dumps(payload_metadata)
+ )
r = self.session.get(self.url("/api/v1/servers/localhost/zones/example.com/metadata"))
rdata = r.json()
self.assertEqual(r.status_code, 200)
def test_create_external_zone_metadata(self):
payload_metadata = {"metadata": ["My very important message"]}
- r = self.session.put(self.url("/api/v1/servers/localhost/zones/example.com/metadata/X-MYMETA"),
- data=json.dumps(payload_metadata))
+ r = self.session.put(
+ self.url("/api/v1/servers/localhost/zones/example.com/metadata/X-MYMETA"), data=json.dumps(payload_metadata)
+ )
self.assertEqual(r.status_code, 200)
rdata = r.json()
self.assertEqual(rdata["metadata"], payload_metadata["metadata"])
def test_create_metadata_in_non_existent_zone(self):
payload_metadata = {"type": "Metadata", "kind": "AXFR-SOURCE", "metadata": ["127.0.0.2"]}
- r = self.session.post(self.url("/api/v1/servers/localhost/zones/idonotexist.123.456.example./metadata"),
- data=json.dumps(payload_metadata))
+ r = self.session.post(
+ self.url("/api/v1/servers/localhost/zones/idonotexist.123.456.example./metadata"),
+ data=json.dumps(payload_metadata),
+ )
self.assertEqual(r.status_code, 404)
# Note: errors should probably contain json (see #5988)
# self.assert_in_json_error('Could not find domain ', r.json())
def test_create_slave_zone(self):
# Test that nameservers can be absent for slave zones.
- name, payload, data = self.create_zone(kind='Slave', nameservers=None, masters=['127.0.0.2'])
- for k in ('name', 'masters', 'kind'):
+ name, payload, data = self.create_zone(kind="Slave", nameservers=None, masters=["127.0.0.2"])
+ for k in ("name", "masters", "kind"):
self.assertIn(k, data)
self.assertEqual(data[k], payload[k])
print("payload:", payload)
r = self.session.get(self.url("/api/v1/servers/localhost/zones"))
zonelist = r.json()
print("zonelist:", zonelist)
- self.assertIn(payload['name'], [zone['name'] for zone in zonelist])
+ self.assertIn(payload["name"], [zone["name"] for zone in zonelist])
# Also test that fetching the zone works.
- data = self.get_zone(data['id'])
+ data = self.get_zone(data["id"])
print("zone (fetched):", data)
- for k in ('name', 'masters', 'kind'):
+ for k in ("name", "masters", "kind"):
self.assertIn(k, data)
self.assertEqual(data[k], payload[k])
- self.assertEqual(data['serial'], 0)
- self.assertEqual(data['rrsets'], [])
+ self.assertEqual(data["serial"], 0)
+ self.assertEqual(data["rrsets"], [])
def test_create_consumer_zone(self):
# Test that nameservers can be absent for consumer zones.
- _, payload, data = self.create_zone(kind='Consumer', nameservers=None, masters=['127.0.0.2'])
+ _, payload, data = self.create_zone(kind="Consumer", nameservers=None, masters=["127.0.0.2"])
print("payload:", payload)
print("data:", data)
# Because consumer zones don't get a SOA, we need to test that they'll show up in the zone list.
r = self.session.get(self.url("/api/v1/servers/localhost/zones"))
zonelist = r.json()
print("zonelist:", zonelist)
- self.assertIn(payload['name'], [zone['name'] for zone in zonelist])
+ self.assertIn(payload["name"], [zone["name"] for zone in zonelist])
# Also test that fetching the zone works.
- r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + data['id']))
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + data["id"]))
data = r.json()
print("zone (fetched):", data)
- for k in ('name', 'masters', 'kind'):
+ for k in ("name", "masters", "kind"):
self.assertIn(k, data)
self.assertEqual(data[k], payload[k])
- self.assertEqual(data['serial'], 0)
- self.assertEqual(data['rrsets'], [])
+ self.assertEqual(data["serial"], 0)
+ self.assertEqual(data["rrsets"], [])
def test_create_consumer_zone_no_nameservers(self):
"""nameservers must be absent for Consumer zones"""
- self.create_zone(kind="Consumer", nameservers=["127.0.0.1"], expect_error="Nameservers MUST NOT be given for Consumer zones")
+ self.create_zone(
+ kind="Consumer", nameservers=["127.0.0.1"], expect_error="Nameservers MUST NOT be given for Consumer zones"
+ )
def test_create_consumer_zone_no_rrsets(self):
"""rrsets must be absent for Consumer zones"""
- rrsets = [{
- "name": "$NAME$",
- "type": "SOA",
- "ttl": 3600,
- "records": [{
- "content": "ns1.example.net. testmaster@example.net. 10 10800 3600 604800 3600",
- "disabled": False,
- }],
- }]
- self.create_zone(kind="Consumer", nameservers=None, rrsets=rrsets, expect_error="Zone data MUST NOT be given for Consumer zones")
+ rrsets = [
+ {
+ "name": "$NAME$",
+ "type": "SOA",
+ "ttl": 3600,
+ "records": [
+ {
+ "content": "ns1.example.net. testmaster@example.net. 10 10800 3600 604800 3600",
+ "disabled": False,
+ }
+ ],
+ }
+ ]
+ self.create_zone(
+ kind="Consumer",
+ nameservers=None,
+ rrsets=rrsets,
+ expect_error="Zone data MUST NOT be given for Consumer zones",
+ )
def test_find_zone_by_name(self):
- name = 'foo/' + unique_zone_name()
+ name = "foo/" + unique_zone_name()
name, payload, data = self.create_zone(name=name)
r = self.session.get(self.url("/api/v1/servers/localhost/zones?zone=" + name))
data = r.json()
print(data)
- self.assertEqual(data[0]['name'], name)
+ self.assertEqual(data[0]["name"], name)
def test_delete_slave_zone(self):
- name, payload, data = self.create_zone(kind='Slave', nameservers=None, masters=['127.0.0.2'])
- r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + data['id']))
+ name, payload, data = self.create_zone(kind="Slave", nameservers=None, masters=["127.0.0.2"])
+ r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + data["id"]))
r.raise_for_status()
def test_delete_consumer_zone(self):
- name, payload, data = self.create_zone(kind='Consumer', nameservers=None, masters=['127.0.0.2'])
- r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + data['id']))
+ name, payload, data = self.create_zone(kind="Consumer", nameservers=None, masters=["127.0.0.2"])
+ r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + data["id"]))
r.raise_for_status()
def test_retrieve_slave_zone(self):
- name, payload, data = self.create_zone(kind='Slave', nameservers=None, masters=['127.0.0.2'])
+ name, payload, data = self.create_zone(kind="Slave", nameservers=None, masters=["127.0.0.2"])
print("payload:", payload)
print("data:", data)
- r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/axfr-retrieve"))
+ r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data["id"] + "/axfr-retrieve"))
data = r.json()
print("status for axfr-retrieve:", data)
- self.assertEqual(data['result'], u'Added retrieval request for \'' + payload['name'] +
- '\' from primary 127.0.0.2')
+ self.assertEqual(data["result"], "Added retrieval request for '" + payload["name"] + "' from primary 127.0.0.2")
def test_notify_master_zone(self):
- name, payload, data = self.create_zone(kind='Master')
+ name, payload, data = self.create_zone(kind="Master")
print("payload:", payload)
print("data:", data)
- r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/notify"))
+ r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data["id"] + "/notify"))
data = r.json()
print("status for notify:", data)
- self.assertEqual(data['result'], 'Notification queued')
+ self.assertEqual(data["result"], "Notification queued")
def test_get_zone_with_symbols(self):
- name, payload, data = self.create_zone(name='foo/bar.'+unique_zone_name())
- name = payload['name']
- zone_id = (name.replace('/', '=2F'))
+ name, payload, data = self.create_zone(name="foo/bar." + unique_zone_name())
+ name = payload["name"]
+ zone_id = name.replace("/", "=2F")
data = self.get_zone(zone_id)
- for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'dnssec'):
+ for k in ("id", "url", "name", "masters", "kind", "last_check", "notified_serial", "serial", "dnssec"):
self.assertIn(k, data)
if k in payload:
self.assertEqual(data[k], payload[k])
def test_get_zone(self):
r = self.session.get(self.url("/api/v1/servers/localhost/zones"))
domains = r.json()
- example_com = [domain for domain in domains if domain['name'] == u'example.com.'][0]
- data = self.get_zone(example_com['id'])
- for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial'):
+ example_com = [domain for domain in domains if domain["name"] == "example.com."][0]
+ data = self.get_zone(example_com["id"])
+ for k in ("id", "url", "name", "masters", "kind", "last_check", "notified_serial", "serial"):
self.assertIn(k, data)
- self.assertEqual(data['name'], 'example.com.')
+ self.assertEqual(data["name"], "example.com.")
def test_get_zone_rrset(self):
- name = 'host-18000.example.com.'
+ name = "host-18000.example.com."
r = self.session.get(self.url("/api/v1/servers/localhost/zones"))
domains = r.json()
- example_com = [domain for domain in domains if domain['name'] == u'example.com.'][0]
+ example_com = [domain for domain in domains if domain["name"] == "example.com."][0]
# verify single record from name that has a single record
- data = self.get_zone(example_com['id'], rrset_name="host-18000.example.com.")
- for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'rrsets'):
+ data = self.get_zone(example_com["id"], rrset_name="host-18000.example.com.")
+ for k in ("id", "url", "name", "masters", "kind", "last_check", "notified_serial", "serial", "rrsets"):
self.assertIn(k, data)
- received_rrsets = data['rrsets']
+ received_rrsets = data["rrsets"]
for rrset in received_rrsets:
- remove_timestamp(rrset['records'])
- self.assertEqual(received_rrsets,
+ remove_timestamp(rrset["records"])
+ self.assertEqual(
+ received_rrsets,
[
{
- 'comments': [],
- 'name': name,
- 'records':
- [
- {
- 'content': '192.168.1.80',
- 'disabled': False
- }
- ],
- 'ttl': 120,
- 'type': 'A'
+ "comments": [],
+ "name": name,
+ "records": [{"content": "192.168.1.80", "disabled": False}],
+ "ttl": 120,
+ "type": "A",
}
- ]
+ ],
)
# disable previous record
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'A',
- 'ttl': 120,
- 'records':
- [
- {
- 'content': '192.168.1.80',
- 'disabled': True
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "A",
+ "ttl": 120,
+ "records": [{"content": "192.168.1.80", "disabled": True}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/example.com"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that the changed record is not found when asking for
# disabled records not to be included
- data = self.get_zone(example_com['id'], rrset_name="host-18000.example.com.", include_disabled="false")
- self.assertEqual(len(data['rrsets']), 0)
+ data = self.get_zone(example_com["id"], rrset_name="host-18000.example.com.", include_disabled="false")
+ self.assertEqual(len(data["rrsets"]), 0)
# verify that the changed record is found when explicitly asking for
# disabled records, and by default.
- data = self.get_zone(example_com['id'], rrset_name="host-18000.example.com.", include_disabled="true")
- self.assertEqual(get_rrset(data, name, 'A')['records'], rrset['records'])
- data = self.get_zone(example_com['id'], rrset_name="host-18000.example.com.")
- self.assertEqual(get_rrset(data, name, 'A')['records'], rrset['records'])
+ data = self.get_zone(example_com["id"], rrset_name="host-18000.example.com.", include_disabled="true")
+ self.assertEqual(get_rrset(data, name, "A")["records"], rrset["records"])
+ data = self.get_zone(example_com["id"], rrset_name="host-18000.example.com.")
+ self.assertEqual(get_rrset(data, name, "A")["records"], rrset["records"])
# verify two RRsets from a name that has two types with one record each
- powerdnssec_org = [domain for domain in domains if domain['name'] == u'powerdnssec.org.'][0]
- data = self.get_zone(powerdnssec_org['id'], rrset_name="localhost.powerdnssec.org.")
- for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'rrsets'):
+ powerdnssec_org = [domain for domain in domains if domain["name"] == "powerdnssec.org."][0]
+ data = self.get_zone(powerdnssec_org["id"], rrset_name="localhost.powerdnssec.org.")
+ for k in ("id", "url", "name", "masters", "kind", "last_check", "notified_serial", "serial", "rrsets"):
self.assertIn(k, data)
- received_rrsets = data['rrsets']
+ received_rrsets = data["rrsets"]
for rrset in received_rrsets:
- remove_timestamp(rrset['records'])
- self.assertEqual(sorted(received_rrsets, key=operator.itemgetter('type')),
+ remove_timestamp(rrset["records"])
+ self.assertEqual(
+ sorted(received_rrsets, key=operator.itemgetter("type")),
[
{
- 'comments': [],
- 'name': 'localhost.powerdnssec.org.',
- 'records':
- [
- {
- 'content': '127.0.0.1',
- 'disabled': False
- }
- ],
- 'ttl': 3600,
- 'type': 'A'
+ "comments": [],
+ "name": "localhost.powerdnssec.org.",
+ "records": [{"content": "127.0.0.1", "disabled": False}],
+ "ttl": 3600,
+ "type": "A",
},
{
- 'comments': [],
- 'name': 'localhost.powerdnssec.org.',
- 'records':
- [
- {
- 'content': '::1',
- 'disabled': False
- }
- ],
- 'ttl': 3600,
- 'type': 'AAAA'
+ "comments": [],
+ "name": "localhost.powerdnssec.org.",
+ "records": [{"content": "::1", "disabled": False}],
+ "ttl": 3600,
+ "type": "AAAA",
},
- ]
+ ],
)
# verify one RRset with one record from a name that has two, then filtered by type
- data = self.get_zone(powerdnssec_org['id'], rrset_name="localhost.powerdnssec.org.", rrset_type="AAAA")
- for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'rrsets'):
+ data = self.get_zone(powerdnssec_org["id"], rrset_name="localhost.powerdnssec.org.", rrset_type="AAAA")
+ for k in ("id", "url", "name", "masters", "kind", "last_check", "notified_serial", "serial", "rrsets"):
self.assertIn(k, data)
- received_rrsets = data['rrsets']
+ received_rrsets = data["rrsets"]
for rrset in received_rrsets:
- remove_timestamp(rrset['records'])
- self.assertEqual(received_rrsets,
+ remove_timestamp(rrset["records"])
+ self.assertEqual(
+ received_rrsets,
[
{
- 'comments': [],
- 'name': 'localhost.powerdnssec.org.',
- 'records':
- [
- {
- 'content': '::1',
- 'disabled': False
- }
- ],
- 'ttl': 3600,
- 'type': 'AAAA'
+ "comments": [],
+ "name": "localhost.powerdnssec.org.",
+ "records": [{"content": "::1", "disabled": False}],
+ "ttl": 3600,
+ "type": "AAAA",
}
- ]
+ ],
)
def test_import_zone_broken(self):
payload = {
- 'name': 'powerdns-broken.com',
- 'kind': 'Master',
- 'nameservers': [],
+ "name": "powerdns-broken.com",
+ "kind": "Master",
+ "nameservers": [],
}
- payload['zone'] = """
+ payload["zone"] = """
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58571
flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
def test_import_zone_axfr_outofzone(self):
# Ensure we don't create out-of-zone records
payload = {
- 'name': unique_zone_name(),
- 'kind': 'Master',
- 'nameservers': [],
+ "name": unique_zone_name(),
+ "kind": "Master",
+ "nameservers": [],
}
- payload['zone'] = """
+ payload["zone"] = """
%NAME% 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800
%NAME% 3600 IN NS powerdnssec2.ds9a.nl.
example.org. 3600 IN AAAA 2001:888:2000:1d::2
%NAME% 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800
-""".replace('%NAME%', payload['name'])
+""".replace("%NAME%", payload["name"])
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('RRset example.org. IN AAAA: Name is out of zone', r.json())
+ self.assert_in_json_error("RRset example.org. IN AAAA: Name is out of zone", r.json())
def test_import_zone_axfr(self):
payload = {
- 'name': 'powerdns.com.',
- 'kind': 'Master',
- 'nameservers': [],
- 'soa_edit_api': '', # turn off so exact SOA comparison works.
+ "name": "powerdns.com.",
+ "kind": "Master",
+ "nameservers": [],
+ "soa_edit_api": "", # turn off so exact SOA comparison works.
}
- payload['zone'] = """
+ payload["zone"] = """
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58571
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success_json(r)
data = r.json()
- self.assertIn('name', data)
+ self.assertIn("name", data)
expected = {
- 'NS': [
- {'content': 'powerdnssec1.ds9a.nl.'},
- {'content': 'powerdnssec2.ds9a.nl.'},
+ "NS": [
+ {"content": "powerdnssec1.ds9a.nl."},
+ {"content": "powerdnssec2.ds9a.nl."},
],
- 'SOA': [
- {'content': 'powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800'},
+ "SOA": [
+ {"content": "powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800"},
],
- 'MX': [
- {'content': '0 xs.powerdns.com.'},
+ "MX": [
+ {"content": "0 xs.powerdns.com."},
],
- 'A': [
- {'content': '82.94.213.34', 'name': 'powerdns.com.'},
+ "A": [
+ {"content": "82.94.213.34", "name": "powerdns.com."},
],
- 'AAAA': [
- {'content': '2001:888:2000:1d::2', 'name': 'powerdns.com.'},
+ "AAAA": [
+ {"content": "2001:888:2000:1d::2", "name": "powerdns.com."},
],
}
- eq_zone_rrsets(data['rrsets'], expected)
+ eq_zone_rrsets(data["rrsets"], expected)
if not is_auth_lmdb():
# check content in DB is stored WITHOUT trailing dot.
- dbrecs = get_db_records(payload['name'], 'NS')
- dbrec = next((dbrec for dbrec in dbrecs if dbrec['content'].startswith('powerdnssec1')))
- self.assertEqual(dbrec['content'], 'powerdnssec1.ds9a.nl')
+ dbrecs = get_db_records(payload["name"], "NS")
+ dbrec = next((dbrec for dbrec in dbrecs if dbrec["content"].startswith("powerdnssec1")))
+ self.assertEqual(dbrec["content"], "powerdnssec1.ds9a.nl")
def test_import_zone_bind(self):
payload = {
- 'name': 'example.org.',
- 'kind': 'Master',
- 'nameservers': [],
- 'soa_edit_api': '', # turn off so exact SOA comparison works.
+ "name": "example.org.",
+ "kind": "Master",
+ "nameservers": [],
+ "soa_edit_api": "", # turn off so exact SOA comparison works.
}
- payload['zone'] = """
+ payload["zone"] = """
$TTL 86400 ; 24 hours could have been written as 24h or 1d
; $TTL used for all RRs without explicit TTL value
$ORIGIN example.org.
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success_json(r)
data = r.json()
- self.assertIn('name', data)
+ self.assertIn("name", data)
expected = {
- 'NS': [
- {'content': 'ns1.example.org.'},
- {'content': 'ns2.smokeyjoe.com.'},
+ "NS": [
+ {"content": "ns1.example.org."},
+ {"content": "ns2.smokeyjoe.com."},
],
- 'SOA': [
- {'content': 'ns1.example.org. hostmaster.example.org. 2002022401 10800 15 604800 10800'},
+ "SOA": [
+ {"content": "ns1.example.org. hostmaster.example.org. 2002022401 10800 15 604800 10800"},
],
- 'MX': [
- {'content': '10 mail.another.com.'},
+ "MX": [
+ {"content": "10 mail.another.com."},
],
- 'A': [
- {'content': '192.168.0.1', 'name': 'ns1.example.org.'},
- {'content': '192.168.0.2', 'name': 'www.example.org.'},
- {'content': '192.168.0.3', 'name': 'bill.example.org.'},
- {'content': '192.168.0.4', 'name': 'fred.example.org.'},
+ "A": [
+ {"content": "192.168.0.1", "name": "ns1.example.org."},
+ {"content": "192.168.0.2", "name": "www.example.org."},
+ {"content": "192.168.0.3", "name": "bill.example.org."},
+ {"content": "192.168.0.4", "name": "fred.example.org."},
],
- 'CNAME': [
- {'content': 'www.example.org.', 'name': 'ftp.example.org.'},
+ "CNAME": [
+ {"content": "www.example.org.", "name": "ftp.example.org."},
],
}
- eq_zone_rrsets(data['rrsets'], expected)
+ eq_zone_rrsets(data["rrsets"], expected)
def test_import_zone_bind_cname_apex(self):
payload = {
- 'name': unique_zone_name(),
- 'kind': 'Master',
- 'nameservers': [],
+ "name": unique_zone_name(),
+ "kind": "Master",
+ "nameservers": [],
}
- payload['zone'] = """
+ payload["zone"] = """
$ORIGIN %NAME%
@ IN SOA ns1.example.org. hostmaster.example.org. (2002022401 3H 15 1W 3H)
@ IN NS ns1.example.org.
@ IN NS ns2.smokeyjoe.com.
@ IN CNAME www.example.org.
-""".replace('%NAME%', payload['name'])
+""".replace("%NAME%", payload["name"])
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('conflicts with existing NS RRset', r.json())
+ self.assert_in_json_error("conflicts with existing NS RRset", r.json())
def test_export_zone_json(self):
- name, payload, zone = self.create_zone(nameservers=['ns1.foo.com.', 'ns2.foo.com.'], soa_edit_api='')
+ name, payload, zone = self.create_zone(nameservers=["ns1.foo.com.", "ns2.foo.com."], soa_edit_api="")
# export it
r = self.session.get(
self.url("/api/v1/servers/localhost/zones/" + name + "/export"),
- headers={'accept': 'application/json;q=0.9,*/*;q=0.8'}
+ headers={"accept": "application/json;q=0.9,*/*;q=0.8"},
)
self.assert_success_json(r)
data = r.json()
- self.assertIn('zone', data)
- expected_data = [name + '\t3600\tIN\tNS\tns1.foo.com.',
- name + '\t3600\tIN\tNS\tns2.foo.com.',
- name + '\t3600\tIN\tSOA\ta.misconfigured.dns.server.invalid. hostmaster.' + name +
- ' 0 10800 3600 604800 3600']
- self.assertCountEqual(data['zone'].strip().split('\n'), expected_data)
+ self.assertIn("zone", data)
+ expected_data = [
+ name + "\t3600\tIN\tNS\tns1.foo.com.",
+ name + "\t3600\tIN\tNS\tns2.foo.com.",
+ name
+ + "\t3600\tIN\tSOA\ta.misconfigured.dns.server.invalid. hostmaster."
+ + name
+ + " 0 10800 3600 604800 3600",
+ ]
+ self.assertCountEqual(data["zone"].strip().split("\n"), expected_data)
def test_import_zone_consumer(self):
zonestring = """
3h ; minimum
)
"""
- self.create_zone(kind="Consumer", nameservers=[], zone=zonestring, expect_error="Zone data MUST NOT be given for Consumer zones")
+ self.create_zone(
+ kind="Consumer",
+ nameservers=[],
+ zone=zonestring,
+ expect_error="Zone data MUST NOT be given for Consumer zones",
+ )
def test_export_zone_text(self):
- name, payload, zone = self.create_zone(nameservers=['ns1.foo.com.', 'ns2.foo.com.'], soa_edit_api='')
+ name, payload, zone = self.create_zone(nameservers=["ns1.foo.com.", "ns2.foo.com."], soa_edit_api="")
# export it
- r = self.session.get(
- self.url("/api/v1/servers/localhost/zones/" + name + "/export")
- )
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + "/export"))
data = r.text.strip().split("\n")
- expected_data = [name + '\t3600\tIN\tNS\tns1.foo.com.',
- name + '\t3600\tIN\tNS\tns2.foo.com.',
- name + '\t3600\tIN\tSOA\ta.misconfigured.dns.server.invalid. hostmaster.' + name +
- ' 0 10800 3600 604800 3600']
+ expected_data = [
+ name + "\t3600\tIN\tNS\tns1.foo.com.",
+ name + "\t3600\tIN\tNS\tns2.foo.com.",
+ name
+ + "\t3600\tIN\tSOA\ta.misconfigured.dns.server.invalid. hostmaster."
+ + name
+ + " 0 10800 3600 604800 3600",
+ ]
self.assertCountEqual(data, expected_data)
def test_update_zone(self):
name, payload, zone = self.create_zone()
- name = payload['name']
+ name = payload["name"]
# update, set as Master and enable SOA-EDIT-API
payload = {
- 'kind': 'Master',
- 'masters': ['192.0.2.1', '192.0.2.2'],
- 'catalog': 'catalog.invalid.',
- 'soa_edit_api': 'EPOCH',
- 'soa_edit': 'EPOCH'
+ "kind": "Master",
+ "masters": ["192.0.2.1", "192.0.2.2"],
+ "catalog": "catalog.invalid.",
+ "soa_edit_api": "EPOCH",
+ "soa_edit": "EPOCH",
}
self.put_zone(name, payload)
data = self.get_zone(name)
self.assertIn(k, data)
self.assertEqual(data[k], payload[k])
# update, back to Native and empty(off)
- payload = {
- 'kind': 'Native',
- 'catalog': '',
- 'soa_edit_api': '',
- 'soa_edit': ''
- }
+ payload = {"kind": "Native", "catalog": "", "soa_edit_api": "", "soa_edit": ""}
self.put_zone(name, payload)
data = self.get_zone(name)
for k in payload.keys():
name, payload, zone = self.create_zone()
# rrset with incorrect changetype value
rrset = {
- 'changetype': 'ihavenoideawhatiamdoing',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [
- {
- "content": "127.0.0.1",
- "disabled": False
- }
- ]
+ "changetype": "ihavenoideawhatiamdoing",
+ "name": "a." + name,
+ "type": "A",
+ "ttl": 3600,
+ "records": [{"content": "127.0.0.1", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
self.assert_in_json_error("Changetype 'IHAVENOIDEAWHATIAMDOING' is not a valid value", r.json())
def test_zone_rr_bogus_update_2(self):
name, payload, zone = self.create_zone()
# extend rrset with no records
- rrset = {
- 'changetype': 'extend',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600
- }
- payload = {'rrsets': [rrset]}
+ rrset = {"changetype": "extend", "name": "a." + name, "type": "A", "ttl": 3600}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
self.assert_in_json_error("No record provided", r.json())
name, payload, zone = self.create_zone()
# prune rrset with two records
rrset = {
- 'changetype': 'prune',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [
- {
- "content": "127.0.0.1",
- "disabled": False
- },
- {
- "content": "127.0.0.2",
- "disabled": False
- }
- ]
+ "changetype": "prune",
+ "name": "a." + name,
+ "type": "A",
+ "ttl": 3600,
+ "records": [{"content": "127.0.0.1", "disabled": False}, {"content": "127.0.0.2", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
self.assert_in_json_error("Exactly one record should be provided", r.json())
name, payload, zone = self.create_zone()
# rrset with invalid characters which are not processed by parseRFC1035CharString
rrset = {
- 'changetype': 'replace',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [
- {
- "content": "(127.0.0.1)",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": "a." + name,
+ "type": "A",
+ "ttl": 3600,
+ "records": [{"content": "(127.0.0.1)", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
self.assert_in_json_error("Invalid character '(' in record content", r.json())
# rrset with empty contents
rrset = {
- 'changetype': 'replace',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [
- {
- "content": "",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": "a." + name,
+ "type": "A",
+ "ttl": 3600,
+ "records": [{"content": "", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
self.assert_in_json_error("missing field at the end of record content ''", r.json())
name, payload, zone = self.create_zone()
# do a replace (= update)
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'ns',
- 'ttl': 3600,
- 'records': [
- {
- "content": "ns1.bar.com.",
- "disabled": False
- },
- {
- "content": "ns2-disabled.bar.com.",
- "disabled": True
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "ns",
+ "ttl": 3600,
+ "records": [
+ {"content": "ns1.bar.com.", "disabled": False},
+ {"content": "ns2-disabled.bar.com.", "disabled": True},
+ ],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that (only) the new record is there
data = self.get_zone(name)
- self.assertCountEqual(get_rrset(data, name, 'NS')['records'], rrset['records'])
+ self.assertCountEqual(get_rrset(data, name, "NS")["records"], rrset["records"])
def test_zone_rr_update_lua(self):
# Important to test with LUA records, as their contents should not be
# normalized in any way.
name, payload, zone = self.create_zone()
- recname = 'lua.' + name
+ recname = "lua." + name
# do a replace (= update)
rrset = {
- 'changetype': 'replace',
- 'name': recname,
- 'type': 'LUA',
- 'ttl': 3600,
- 'records': [
- {
- "content": "TXT \"; return 'PowerDNS'\"",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": recname,
+ "type": "LUA",
+ "ttl": 3600,
+ "records": [{"content": "TXT \"; return 'PowerDNS'\"", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that (only) the new record is there
data = self.get_zone(name)
- self.assertEqual(get_rrset(data, recname, 'LUA')['records'], rrset['records'])
+ self.assertEqual(get_rrset(data, recname, "LUA")["records"], rrset["records"])
def test_zone_rr_update_mx(self):
# Important to test with MX records, as they have a priority field, which must end up in the content field.
name, payload, zone = self.create_zone()
# do a replace (= update)
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'MX',
- 'ttl': 3600,
- 'records': [
- {
- "content": "10 mail.example.org.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "MX",
+ "ttl": 3600,
+ "records": [{"content": "10 mail.example.org.", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that (only) the new record is there
data = self.get_zone(name)
- self.assertEqual(get_rrset(data, name, 'MX')['records'], rrset['records'])
+ self.assertEqual(get_rrset(data, name, "MX")["records"], rrset["records"])
def test_zone_rr_update_invalid_mx(self):
name, payload, zone = self.create_zone()
# do a replace (= update)
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'MX',
- 'ttl': 3600,
- 'records': [
- {
- "content": "10 mail@mx.example.org.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "MX",
+ "ttl": 3600,
+ "records": [{"content": "10 mail@mx.example.org.", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('non-hostname content', r.json())
+ self.assert_in_json_error("non-hostname content", r.json())
data = self.get_zone(name)
- self.assertIsNone(get_rrset(data, name, 'MX'))
+ self.assertIsNone(get_rrset(data, name, "MX"))
def test_zone_rr_update_invalid_txt(self):
name, payload, zone = self.create_zone()
# do a replace (= update)
rrset = {
- 'changetype': 'replace',
- 'name': 'ill-formed-txt.' + name,
- 'type': 'txt',
- 'ttl': 3600,
- 'records': [
- {
- "content": "\"TEST\\1\" \"TEST2\"",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": "ill-formed-txt." + name,
+ "type": "txt",
+ "ttl": 3600,
+ "records": [{"content": '"TEST\\1" "TEST2"', "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assertIn('Not in expected format (parsed as', r.json()['error'])
+ self.assertIn("Not in expected format (parsed as", r.json()["error"])
def test_zone_rr_update_with_escapes(self):
name, payload, zone = self.create_zone()
# do a replace (= update)
- recname = 'well-formed-txt.' + name
- content = "\"valid\\000record\""
+ recname = "well-formed-txt." + name
+ content = '"valid\\000record"'
rrset = {
- 'changetype': 'replace',
- 'name': recname,
- 'type': 'txt',
- 'ttl': 3600,
- 'records': [
- {
- "content": content,
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": recname,
+ "type": "txt",
+ "ttl": 3600,
+ "records": [{"content": content, "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that the new record has been correctly processed and the \000
# escape is unchanged
data = self.get_zone(name)
- self.assertEqual(get_rrset(data, recname, 'TXT')['records'][0]['content'], content)
+ self.assertEqual(get_rrset(data, recname, "TXT")["records"][0]["content"], content)
def test_zone_rr_update_opt(self):
name, payload, zone = self.create_zone()
# do a replace (= update)
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'OPT',
- 'ttl': 3600,
- 'records': [
- {
- "content": "9",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "OPT",
+ "ttl": 3600,
+ "records": [{"content": "9", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('OPT: invalid type given', r.json())
+ self.assert_in_json_error("OPT: invalid type given", r.json())
def test_zone_rr_update_multiple_rrsets(self):
name, payload, zone = self.create_zone()
rrset1 = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'NS',
- 'ttl': 3600,
- 'records': [
- {
-
- "content": "ns9999.example.com.",
- "disabled": False
- }
- ]
- }
+ "changetype": "replace",
+ "name": name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns9999.example.com.", "disabled": False}],
+ }
rrset2 = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'MX',
- 'ttl': 3600,
- 'records': [
- {
- "content": "10 mx444.example.com.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "MX",
+ "ttl": 3600,
+ "records": [{"content": "10 mx444.example.com.", "disabled": False}],
}
- payload = {'rrsets': [rrset1, rrset2]}
+ payload = {"rrsets": [rrset1, rrset2]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that all rrsets have been updated
data = self.get_zone(name)
- self.assertEqual(get_rrset(data, name, 'NS')['records'], rrset1['records'])
- self.assertEqual(get_rrset(data, name, 'MX')['records'], rrset2['records'])
+ self.assertEqual(get_rrset(data, name, "NS")["records"], rrset1["records"])
+ self.assertEqual(get_rrset(data, name, "MX")["records"], rrset2["records"])
def test_zone_rr_update_duplicate_record(self):
name, payload, zone = self.create_zone()
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'NS',
- 'ttl': 3600,
- 'records': [
+ "changetype": "replace",
+ "name": name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [
{"content": "ns9999.example.com.", "disabled": False},
{"content": "ns9996.example.com.", "disabled": False},
{"content": "ns9987.example.com.", "disabled": False},
{"content": "ns9988.example.com.", "disabled": False},
{"content": "ns9999.example.com.", "disabled": False},
- ]
+ ],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('duplicate record with content', r.json())
+ self.assert_in_json_error("duplicate record with content", r.json())
def test_zone_rr_update_duplicate_rrset(self):
name, payload, zone = self.create_zone()
rrset1 = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'NS',
- 'ttl': 3600,
- 'records': [
- {
- "content": "ns9999.example.com.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns9999.example.com.", "disabled": False}],
}
rrset2 = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'NS',
- 'ttl': 3600,
- 'records': [
- {
- "content": "ns9998.example.com.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns9998.example.com.", "disabled": False}],
}
- payload = {'rrsets': [rrset1, rrset2]}
+ payload = {"rrsets": [rrset1, rrset2]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('Duplicate RRset', r.json())
+ self.assert_in_json_error("Duplicate RRset", r.json())
def test_zone_rr_delete(self):
name, payload, zone = self.create_zone()
# do a delete of all NS records (these are created with the zone)
- rrset = {
- 'changetype': 'delete',
- 'name': name,
- 'type': 'NS'
- }
- payload = {'rrsets': [rrset]}
+ rrset = {"changetype": "delete", "name": name, "type": "NS"}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that the records are gone
data = self.get_zone(name)
- self.assertIsNone(get_rrset(data, name, 'NS'))
+ self.assertIsNone(get_rrset(data, name, "NS"))
def test_zone_rr_update_rrset_combine_replace_and_delete(self):
name, payload, zone = self.create_zone()
rrset1 = {
- 'changetype': 'delete',
- 'name': 'sub.' + name,
- 'type': 'CNAME',
+ "changetype": "delete",
+ "name": "sub." + name,
+ "type": "CNAME",
}
rrset2 = {
- 'changetype': 'replace',
- 'name': 'sub.' + name,
- 'type': 'CNAME',
- 'ttl': 500,
- 'records': [
- {
- "content": "www.example.org.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": "sub." + name,
+ "type": "CNAME",
+ "ttl": 500,
+ "records": [{"content": "www.example.org.", "disabled": False}],
}
- payload = {'rrsets': [rrset1, rrset2]}
+ payload = {"rrsets": [rrset1, rrset2]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that (only) the new record is there
data = self.get_zone(name)
- self.assertEqual(get_rrset(data, 'sub.' + name, 'CNAME')['records'], rrset2['records'])
+ self.assertEqual(get_rrset(data, "sub." + name, "CNAME")["records"], rrset2["records"])
def test_zone_rr_update_with_extend(self):
name, payload, zone = self.create_zone()
# add a single record with extend
rrset = {
- 'changetype': 'extend',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [
- {
- "content": "1.2.3.4",
- "disabled": False
- }
- ]
+ "changetype": "extend",
+ "name": "a." + name,
+ "type": "A",
+ "ttl": 3600,
+ "records": [{"content": "1.2.3.4", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that (only) the new record is there
data = self.get_zone(name)
- self.assertEqual(get_rrset(data, 'a.' + name, 'A')['records'], rrset['records'])
+ self.assertEqual(get_rrset(data, "a." + name, "A")["records"], rrset["records"])
# add the same record again
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that the zone contents did not change
data2 = self.get_zone(name)
- self.assertEqual(get_rrset(data, 'a.'+name), get_rrset(data2, 'a.'+name))
+ self.assertEqual(get_rrset(data, "a." + name), get_rrset(data2, "a." + name))
def test_zone_rr_bogus_extend(self):
name, payload, zone = self.create_zone()
# add a single record with extend
rrset = {
- 'changetype': 'extend',
- 'name': 'txt.'+name,
- 'type': 'TXT',
- 'ttl': 3600,
- 'records': [
- {
- "content": "\"hello\"",
- "disabled": False
- }
- ]
+ "changetype": "extend",
+ "name": "txt." + name,
+ "type": "TXT",
+ "ttl": 3600,
+ "records": [{"content": '"hello"', "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# try and add another record with a mismatching ttl
rrset2 = {
- 'changetype': 'extend',
- 'name': 'txt.'+name,
- 'type': 'TXT',
- 'ttl': 1234,
- 'records': [
- {
- "content": "\"hello again\"",
- "disabled": False
- }
- ]
+ "changetype": "extend",
+ "name": "txt." + name,
+ "type": "TXT",
+ "ttl": 1234,
+ "records": [{"content": '"hello again"', "disabled": False}],
}
- payload2 = {'rrsets': [rrset2]}
+ payload2 = {"rrsets": [rrset2]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload2),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('uses a different TTL value than the remainder of the RRset', r.json())
+ self.assert_in_json_error("uses a different TTL value than the remainder of the RRset", r.json())
def test_zone_rr_update_with_prune(self):
name, payload, zone = self.create_zone()
# fill a bunch of records
- a1 = { "content": "1.2.3.4", "disabled": False }
- a2 = { "content": "2.4.6.8", "disabled": False }
- a3 = { "content": "3.6.9.12", "disabled": False }
- a4 = { "content": "4.8.12.16", "disabled": False }
- rrset = {
- 'changetype': 'replace',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [ a1, a2, a3 ]
- }
- payload = {'rrsets': [rrset]}
+ a1 = {"content": "1.2.3.4", "disabled": False}
+ a2 = {"content": "2.4.6.8", "disabled": False}
+ a3 = {"content": "3.6.9.12", "disabled": False}
+ a4 = {"content": "4.8.12.16", "disabled": False}
+ rrset = {"changetype": "replace", "name": "a." + name, "type": "A", "ttl": 3600, "records": [a1, a2, a3]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
data = self.get_zone(name)
- self.assertEqual(get_rrset(data, 'a.' + name, 'A')['records'], rrset['records'])
+ self.assertEqual(get_rrset(data, "a." + name, "A")["records"], rrset["records"])
# remove middle record
- rrset = {
- 'changetype': 'prune',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [ a2 ]
- }
- payload = {'rrsets': [rrset]}
+ rrset = {"changetype": "prune", "name": "a." + name, "type": "A", "ttl": 3600, "records": [a2]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify the zone contents
data1 = self.get_zone(name)
# note that we can't assume anything about the order of the records
- records = get_rrset(data1, 'a.' + name, 'A')['records']
+ records = get_rrset(data1, "a." + name, "A")["records"]
self.assertEqual(len(records), 2)
self.assertIn(a1, records)
self.assertIn(a3, records)
# get_rrset above has removed the timestamps from data1, fetch the
# zone again, since we want to ensure the following operations do
# not change anything.
- if is_auth_lmdb(): # remove test when other backends support record imestamps
+ if is_auth_lmdb(): # remove test when other backends support record imestamps
data1 = self.get_zone(name)
# remove middle record again
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify the zone contents are unchanged, with no serial increase
data2 = self.get_zone(name)
self.assertEqual(data1, data2)
# remove nonexisting record
- rrset = {
- 'changetype': 'prune',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [ a4 ]
- }
- payload = {'rrsets': [rrset]}
+ rrset = {"changetype": "prune", "name": "a." + name, "type": "A", "ttl": 3600, "records": [a4]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify the zone contents are still unchanged
data3 = self.get_zone(name)
def test_zone_rr_update_with_everything(self):
name, payload, zone = self.create_zone()
# our heroic records
- a1 = { "content": "1.2.3.4", "disabled": False }
- a2 = { "content": "2.4.6.8", "disabled": False }
- a3 = { "content": "3.6.9.12", "disabled": False }
- a4 = { "content": "4.8.12.16", "disabled": False }
+ a1 = {"content": "1.2.3.4", "disabled": False}
+ a2 = {"content": "2.4.6.8", "disabled": False}
+ a3 = {"content": "3.6.9.12", "disabled": False}
+ a4 = {"content": "4.8.12.16", "disabled": False}
# timid record addition
- rrset1 = {
- 'changetype': 'extend',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [ a1 ]
- }
+ rrset1 = {"changetype": "extend", "name": "a." + name, "type": "A", "ttl": 3600, "records": [a1]}
# delete all the things!
- rrset2 = {
- 'changetype': 'delete',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': []
- }
+ rrset2 = {"changetype": "delete", "name": "a." + name, "type": "A", "ttl": 3600, "records": []}
# second half of the rrset
- rrset3 = {
- 'changetype': 'replace',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [ a3, a4 ]
- }
+ rrset3 = {"changetype": "replace", "name": "a." + name, "type": "A", "ttl": 3600, "records": [a3, a4]}
# timid record deletion
- rrset4 = {
- 'changetype': 'prune',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [ a3 ]
- }
+ rrset4 = {"changetype": "prune", "name": "a." + name, "type": "A", "ttl": 3600, "records": [a3]}
# first half of the rrset
- rrset5 = {
- 'changetype': 'extend',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [ a1 ]
- }
- rrset6 = {
- 'changetype': 'extend',
- 'name': 'a.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [ a2 ]
- }
- payload = {'rrsets': [rrset1, rrset2, rrset3, rrset4, rrset5, rrset6]}
+ rrset5 = {"changetype": "extend", "name": "a." + name, "type": "A", "ttl": 3600, "records": [a1]}
+ rrset6 = {"changetype": "extend", "name": "a." + name, "type": "A", "ttl": 3600, "records": [a2]}
+ payload = {"rrsets": [rrset1, rrset2, rrset3, rrset4, rrset5, rrset6]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify zone contents
data = self.get_zone(name)
# note that we can't assume anything about the order of the records
- records = get_rrset(data, 'a.' + name, 'A')['records']
+ records = get_rrset(data, "a." + name, "A")["records"]
self.assertEqual(len(records), 3)
self.assertIn(a1, records)
self.assertIn(a2, records)
def test_zone_disable_reenable(self):
# This also tests that SOA-EDIT-API works.
- name, payload, zone = self.create_zone(soa_edit_api='EPOCH')
+ name, payload, zone = self.create_zone(soa_edit_api="EPOCH")
# disable zone by disabling SOA
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'SOA',
- 'ttl': 3600,
- 'records': [
- {
- "content": "ns1.bar.com. hostmaster.foo.org. 1 1 1 1 1",
- "disabled": True
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "SOA",
+ "ttl": 3600,
+ "records": [{"content": "ns1.bar.com. hostmaster.foo.org. 1 1 1 1 1", "disabled": True}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# check SOA serial has been edited
data = self.get_zone(name)
- soa_serial1 = get_first_rec(data, name, 'SOA')['content'].split()[2]
- self.assertNotEqual(soa_serial1, '1')
+ soa_serial1 = get_first_rec(data, name, "SOA")["content"].split()[2]
+ self.assertNotEqual(soa_serial1, "1")
# make sure domain is still in zone list (disabled SOA!)
r = self.session.get(self.url("/api/v1/servers/localhost/zones"))
domains = r.json()
- self.assertEqual(len([domain for domain in domains if domain['name'] == name]), 1)
+ self.assertEqual(len([domain for domain in domains if domain["name"] == name]), 1)
# sleep 1sec to ensure the EPOCH value changes for the next request
time.sleep(1)
# verify that modifying it still works
- rrset['records'][0]['disabled'] = False
- payload = {'rrsets': [rrset]}
+ rrset["records"][0]["disabled"] = False
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# check SOA serial has been edited again
data = self.get_zone(name)
- soa_serial2 = get_first_rec(data, name, 'SOA')['content'].split()[2]
- self.assertNotEqual(soa_serial2, '1')
+ soa_serial2 = get_first_rec(data, name, "SOA")["content"].split()[2]
+ self.assertNotEqual(soa_serial2, "1")
self.assertNotEqual(soa_serial2, soa_serial1)
def test_zone_rr_update_out_of_zone(self):
name, payload, zone = self.create_zone()
# replace with qname mismatch
rrset = {
- 'changetype': 'replace',
- 'name': 'not-in-zone.',
- 'type': 'NS',
- 'ttl': 3600,
- 'records': [
- {
- "content": "ns1.bar.com.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": "not-in-zone.",
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns1.bar.com.", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('out of zone', r.json())
+ self.assert_in_json_error("out of zone", r.json())
def test_zone_rr_update_restricted_chars(self):
name, payload, zone = self.create_zone()
# replace with qname mismatch
rrset = {
- 'changetype': 'replace',
- 'name': 'test:' + name,
- 'type': 'NS',
- 'ttl': 3600,
- 'records': [
- {
- "content": "ns1.bar.com.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": "test:" + name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns1.bar.com.", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('contains unsupported characters', r.json())
+ self.assert_in_json_error("contains unsupported characters", r.json())
def test_rrset_unknown_type(self):
name, payload, zone = self.create_zone()
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'FAFAFAFA', # obviously a FIPv6 address
- 'ttl': 3600,
- 'records': [
- {
- "content": "4.3.2.1",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "FAFAFAFA", # obviously a FIPv6 address
+ "ttl": 3600,
+ "records": [{"content": "4.3.2.1", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('unknown type', r.json())
+ self.assert_in_json_error("unknown type", r.json())
- @parameterized.expand([
- ('CNAME', ),
- ])
+ @parameterized.expand(
+ [
+ ("CNAME",),
+ ]
+ )
def test_rrset_exclusive_and_other(self, qtype):
name, payload, zone = self.create_zone()
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': qtype,
- 'ttl': 3600,
- 'records': [
- {
- "content": "example.org.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": qtype,
+ "ttl": 3600,
+ "records": [{"content": "example.org.", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('Conflicts with pre-existing RRset', r.json())
+ self.assert_in_json_error("Conflicts with pre-existing RRset", r.json())
- @parameterized.expand([
- ('CNAME', ),
- ])
+ @parameterized.expand(
+ [
+ ("CNAME",),
+ ]
+ )
def test_rrset_other_and_exclusive(self, qtype):
name, payload, zone = self.create_zone()
rrset = {
- 'changetype': 'replace',
- 'name': 'sub.'+name,
- 'type': qtype,
- 'ttl': 3600,
- 'records': [
- {
- "content": "example.org.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": "sub." + name,
+ "type": qtype,
+ "ttl": 3600,
+ "records": [{"content": "example.org.", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
rrset = {
- 'changetype': 'replace',
- 'name': 'sub.'+name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [
- {
- "content": "1.2.3.4",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": "sub." + name,
+ "type": "A",
+ "ttl": 3600,
+ "records": [{"content": "1.2.3.4", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('Conflicts with pre-existing RRset', r.json())
-
- @parameterized.expand([
- ('', 'SOA', ['ns1.example.org. test@example.org. 10 10800 3600 604800 3600', 'ns2.example.org. test@example.org. 10 10800 3600 604800 3600']),
- ('sub.', 'CNAME', ['01.example.org.', '02.example.org.']),
- ])
+ self.assert_in_json_error("Conflicts with pre-existing RRset", r.json())
+
+ @parameterized.expand(
+ [
+ (
+ "",
+ "SOA",
+ [
+ "ns1.example.org. test@example.org. 10 10800 3600 604800 3600",
+ "ns2.example.org. test@example.org. 10 10800 3600 604800 3600",
+ ],
+ ),
+ ("sub.", "CNAME", ["01.example.org.", "02.example.org."]),
+ ]
+ )
def test_rrset_single_qtypes(self, label, qtype, contents):
name, payload, zone = self.create_zone()
rrset = {
- 'changetype': 'replace',
- 'name': label + name,
- 'type': qtype,
- 'ttl': 3600,
- 'records': [
- {
- "content": contents[0],
- "disabled": False
- },
- {
- "content": contents[1],
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": label + name,
+ "type": qtype,
+ "ttl": 3600,
+ "records": [{"content": contents[0], "disabled": False}, {"content": contents[1], "disabled": False}],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('IN ' + qtype + ': only one such record', r.json())
+ self.assert_in_json_error("IN " + qtype + ": only one such record", r.json())
def test_rrset_zone_apex(self):
name, payload, zone = self.create_zone()
rrset1 = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'SOA',
- 'ttl': 3600,
- 'records': [
- {
- "content": 'ns1.example.org. test@example.org. 10 10800 3600 604800 3600',
- "disabled": False
- },
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "SOA",
+ "ttl": 3600,
+ "records": [
+ {"content": "ns1.example.org. test@example.org. 10 10800 3600 604800 3600", "disabled": False},
+ ],
}
rrset2 = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'DNAME',
- 'ttl': 3600,
- 'records': [
- {
- "content": 'example.com.',
- "disabled": False
- },
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "DNAME",
+ "ttl": 3600,
+ "records": [
+ {"content": "example.com.", "disabled": False},
+ ],
}
- payload = {'rrsets': [rrset1, rrset2]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset1, rrset2]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r) # user should be able to create DNAME at APEX as per RFC 6672 section 2.3
- @parameterized.expand([
- ('SOA', 'ns1.example.org. test@example.org. 10 10800 3600 604800 1800'),
- ('DNSKEY', '257 3 8 AwEAAb/+pXOZWYQ8mv9WM5dFva8WU9jcIUdDuEjldbyfnkQ/xlrJC5zAEfhYhrea3SmIPmMTDimLqbh3/4SMTNPTUF+9+U1vpNfIRTFadqsmuU9Fddz3JqCcYwEpWbReg6DJOeyu+9oBoIQkPxFyLtIXEPGlQzrynKubn04Cx83I6NfzDTraJT3jLHKeW5PVc1ifqKzHz5TXdHHTA7NkJAa0sPcZCoNE1LpnJI/wcUpRUiuQhoLFeT1E432GuPuZ7y+agElGj0NnBxEgnHrhrnZWUbULpRa/il+Cr5Taj988HqX9Xdm6FjcP4Lbuds/44U7U8du224Q8jTrZ57Yvj4VDQKc='),
- ])
+ @parameterized.expand(
+ [
+ ("SOA", "ns1.example.org. test@example.org. 10 10800 3600 604800 1800"),
+ (
+ "DNSKEY",
+ "257 3 8 AwEAAb/+pXOZWYQ8mv9WM5dFva8WU9jcIUdDuEjldbyfnkQ/xlrJC5zAEfhYhrea3SmIPmMTDimLqbh3/4SMTNPTUF+9+U1vpNfIRTFadqsmuU9Fddz3JqCcYwEpWbReg6DJOeyu+9oBoIQkPxFyLtIXEPGlQzrynKubn04Cx83I6NfzDTraJT3jLHKeW5PVc1ifqKzHz5TXdHHTA7NkJAa0sPcZCoNE1LpnJI/wcUpRUiuQhoLFeT1E432GuPuZ7y+agElGj0NnBxEgnHrhrnZWUbULpRa/il+Cr5Taj988HqX9Xdm6FjcP4Lbuds/44U7U8du224Q8jTrZ57Yvj4VDQKc=",
+ ),
+ ]
+ )
def test_only_at_apex(self, qtype, content):
- name, payload, zone = self.create_zone(soa_edit_api='')
+ name, payload, zone = self.create_zone(soa_edit_api="")
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': qtype,
- 'ttl': 3600,
- 'records': [
- {
- "content": content,
- "disabled": False
- },
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": qtype,
+ "ttl": 3600,
+ "records": [
+ {"content": content, "disabled": False},
+ ],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that the new record is there
data = self.get_zone(name)
- self.assertEqual(get_rrset(data, name, qtype)['records'], rrset['records'])
+ self.assertEqual(get_rrset(data, name, qtype)["records"], rrset["records"])
rrset = {
- 'changetype': 'replace',
- 'name': 'sub.' + name,
- 'type': qtype,
- 'ttl': 3600,
- 'records': [
- {
- "content": content,
- "disabled": False
- },
- ]
+ "changetype": "replace",
+ "name": "sub." + name,
+ "type": qtype,
+ "ttl": 3600,
+ "records": [
+ {"content": content, "disabled": False},
+ ],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('only allowed at apex', r.json())
+ self.assert_in_json_error("only allowed at apex", r.json())
data = self.get_zone(name)
- self.assertIsNone(get_rrset(data, 'sub.' + name, qtype))
+ self.assertIsNone(get_rrset(data, "sub." + name, qtype))
- @parameterized.expand([
- ('DS', '44030 8 2 d4c3d5552b8679faeebc317e5f048b614b2e5f607dc57f1553182d49ab2179f7'),
- ])
+ @parameterized.expand(
+ [
+ ("DS", "44030 8 2 d4c3d5552b8679faeebc317e5f048b614b2e5f607dc57f1553182d49ab2179f7"),
+ ]
+ )
def test_not_allowed_at_apex(self, qtype, content):
name, payload, zone = self.create_zone()
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': qtype,
- 'ttl': 3600,
- 'records': [
- {
- "content": content,
- "disabled": False
- },
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": qtype,
+ "ttl": 3600,
+ "records": [
+ {"content": content, "disabled": False},
+ ],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('not allowed at apex', r.json())
+ self.assert_in_json_error("not allowed at apex", r.json())
data = self.get_zone(name)
- self.assertIsNone(get_rrset(data, 'sub.' + name, qtype))
+ self.assertIsNone(get_rrset(data, "sub." + name, qtype))
rrset = {
- 'changetype': 'replace',
- 'name': 'sub.' + name,
- 'type': qtype,
- 'ttl': 3600,
- 'records': [
- {
- "content": content,
- "disabled": False
- },
- ]
+ "changetype": "replace",
+ "name": "sub." + name,
+ "type": qtype,
+ "ttl": 3600,
+ "records": [
+ {"content": content, "disabled": False},
+ ],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# verify that the new record is there
data = self.get_zone(name)
- self.assertEqual(get_rrset(data, 'sub.' + name, qtype)['records'], rrset['records'])
+ self.assertEqual(get_rrset(data, "sub." + name, qtype)["records"], rrset["records"])
def test_rr_svcb(self):
name, payload, zone = self.create_zone()
rrset = {
- 'changetype': 'replace',
- 'name': 'svcb.' + name,
- 'type': 'SVCB',
- 'ttl': 3600,
- 'records': [
+ "changetype": "replace",
+ "name": "svcb." + name,
+ "type": "SVCB",
+ "ttl": 3600,
+ "records": [
{
"content": '40 . mandatory=alpn alpn=h2,h3 ipv4hint=192.0.2.1,192.0.2.2 ech="dG90YWxseSBib2d1cyBlY2hjb25maWcgdmFsdWU="',
- "disabled": False
+ "disabled": False,
},
- ]
+ ],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
def test_rrset_ns_dname_exclude(self):
name, payload, zone = self.create_zone()
rrset = {
- 'changetype': 'replace',
- 'name': 'delegation.'+name,
- 'type': 'NS',
- 'ttl': 3600,
- 'records': [
- {
- "content": "ns.example.org.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": "delegation." + name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns.example.org.", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
rrset = {
- 'changetype': 'replace',
- 'name': 'delegation.'+name,
- 'type': 'DNAME',
- 'ttl': 3600,
- 'records': [
- {
- "content": "example.com.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": "delegation." + name,
+ "type": "DNAME",
+ "ttl": 3600,
+ "records": [{"content": "example.com.", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('Cannot have both NS and DNAME except in zone apex', r.json())
-
-## FIXME: Enable this when it's time for it
-# def test_rrset_dname_nothing_under(self):
-# name, payload, zone = self.create_zone()
-# rrset = {
-# 'changetype': 'replace',
-# 'name': 'delegation.'+name,
-# 'type': 'DNAME',
-# 'ttl': 3600,
-# 'records': [
-# {
-# "content": "example.com.",
-# "disabled": False
-# }
-# ]
-# }
-# payload = {'rrsets': [rrset]}
-# r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
-# headers={'content-type': 'application/json'})
-# self.assert_success(r)
-# rrset = {
-# 'changetype': 'replace',
-# 'name': 'sub.delegation.'+name,
-# 'type': 'A',
-# 'ttl': 3600,
-# 'records': [
-# {
-# "content": "1.2.3.4",
-# "disabled": False
-# }
-# ]
-# }
-# payload = {'rrsets': [rrset]}
-# r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
-# headers={'content-type': 'application/json'})
-# self.assertEqual(r.status_code, 422)
-# self.assert_in_json_error('You cannot have record(s) under CNAME/DNAME', r.json())
+ self.assert_in_json_error("Cannot have both NS and DNAME except in zone apex", r.json())
+
+ ## FIXME: Enable this when it's time for it
+ # def test_rrset_dname_nothing_under(self):
+ # name, payload, zone = self.create_zone()
+ # rrset = {
+ # 'changetype': 'replace',
+ # 'name': 'delegation.'+name,
+ # 'type': 'DNAME',
+ # 'ttl': 3600,
+ # 'records': [
+ # {
+ # "content": "example.com.",
+ # "disabled": False
+ # }
+ # ]
+ # }
+ # payload = {'rrsets': [rrset]}
+ # r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
+ # headers={'content-type': 'application/json'})
+ # self.assert_success(r)
+ # rrset = {
+ # 'changetype': 'replace',
+ # 'name': 'sub.delegation.'+name,
+ # 'type': 'A',
+ # 'ttl': 3600,
+ # 'records': [
+ # {
+ # "content": "1.2.3.4",
+ # "disabled": False
+ # }
+ # ]
+ # }
+ # payload = {'rrsets': [rrset]}
+ # r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
+ # headers={'content-type': 'application/json'})
+ # self.assertEqual(r.status_code, 422)
+ # self.assert_in_json_error('You cannot have record(s) under CNAME/DNAME', r.json())
def test_create_zone_with_leading_space(self):
name, payload, zone = self.create_zone()
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'A',
- 'ttl': 3600,
- 'records': [
- {
- "content": " 4.3.2.1",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "A",
+ "ttl": 3600,
+ "records": [{"content": " 4.3.2.1", "disabled": False}],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
@unittest.skipIf(is_auth_lmdb(), "No out-of-zone storage in LMDB")
def test_zone_rr_delete_out_of_zone(self):
name, payload, zone = self.create_zone()
- rrset = {
- 'changetype': 'delete',
- 'name': 'not-in-zone.',
- 'type': 'NS'
- }
- payload = {'rrsets': [rrset]}
+ rrset = {"changetype": "delete", "name": "not-in-zone.", "type": "NS"}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
print(r.content)
self.assert_success(r) # succeed so users can fix their wrong, old data
name, payload, zone = self.create_zone()
r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + name))
self.assertEqual(r.status_code, 204)
- self.assertNotIn('Content-Type', r.headers)
+ self.assertNotIn("Content-Type", r.headers)
def test_zone_comment_create(self):
name, payload, zone = self.create_zone()
rrset1 = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'NS',
- 'ttl': 3600,
- 'comments': [
+ "changetype": "replace",
+ "name": name,
+ "type": "NS",
+ "ttl": 3600,
+ "comments": [
{
- 'account': 'test1',
- 'content': 'blah blah',
+ "account": "test1",
+ "content": "blah blah",
},
{
- 'account': 'test2',
- 'content': 'blah blah bleh',
- }
- ]
+ "account": "test2",
+ "content": "blah blah bleh",
+ },
+ ],
}
rrset2 = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'SOA',
- 'ttl': 3600,
- 'comments': [
- {
- 'account': 'test3',
- 'content': 'this should not show up later'
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "SOA",
+ "ttl": 3600,
+ "comments": [{"account": "test3", "content": "this should not show up later"}],
}
- payload = {'rrsets': [rrset1, rrset2]}
+ payload = {"rrsets": [rrset1, rrset2]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
if is_auth_lmdb():
self.assert_error_json(r) # No comments in LMDB
return
# make sure the comments have been set, and that the NS
# records are still present
data = self.get_zone(name, rrset_name=name, rrset_type="NS")
- serverset = get_rrset(data, name, 'NS')
+ serverset = get_rrset(data, name, "NS")
print(serverset)
- self.assertNotEqual(serverset['records'], [])
- self.assertNotEqual(serverset['comments'], [])
+ self.assertNotEqual(serverset["records"], [])
+ self.assertNotEqual(serverset["comments"], [])
# verify that modified_at has been set by pdns
- self.assertNotEqual([c for c in serverset['comments']][0]['modified_at'], 0)
+ self.assertNotEqual([c for c in serverset["comments"]][0]["modified_at"], 0)
# verify that unrelated comments do not leak into the result
- self.assertEqual(get_rrset(data, name, 'SOA'), None)
+ self.assertEqual(get_rrset(data, name, "SOA"), None)
# verify that TTL is correct (regression test)
- self.assertEqual(serverset['ttl'], 3600)
+ self.assertEqual(serverset["ttl"], 3600)
def test_zone_comment_delete(self):
# Test: Delete ONLY comments.
name, payload, zone = self.create_zone()
- rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'NS',
- 'comments': []
- }
- payload = {'rrsets': [rrset]}
+ rrset = {"changetype": "replace", "name": name, "type": "NS", "comments": []}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# make sure the NS records are still present
data = self.get_zone(name)
- serverset = get_rrset(data, name, 'NS')
+ serverset = get_rrset(data, name, "NS")
print(serverset)
- self.assertNotEqual(serverset['records'], [])
- self.assertEqual(serverset['comments'], [])
+ self.assertNotEqual(serverset["records"], [])
+ self.assertEqual(serverset["comments"], [])
@unittest.skipIf(is_auth_lmdb(), "No comments in LMDB")
def test_zone_comment_out_of_range_modified_at(self):
# Test if a modified_at outside of the 32 bit range throws an error
name, payload, zone = self.create_zone()
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'NS',
- 'comments': [
- {
- 'account': 'test1',
- 'content': 'oh hi there',
- 'modified_at': '4294967297'
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "NS",
+ "comments": [{"account": "test1", "content": "oh hi there", "modified_at": "4294967297"}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
self.assert_in_json_error("Key 'modified_at' is out of range", r.json())
name, payload, zone = self.create_zone()
# create a comment
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'NS',
- 'comments': [
- {
- 'account': 'test1',
- 'content': 'oh hi there',
- 'modified_at': 1111
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "NS",
+ "comments": [{"account": "test1", "content": "oh hi there", "modified_at": 1111}],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# replace rrset records
rrset2 = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'NS',
- 'ttl': 3600,
- 'records': [
- {
- "content": "ns1.bar.com.",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns1.bar.com.", "disabled": False}],
}
- payload2 = {'rrsets': [rrset2]}
+ payload2 = {"rrsets": [rrset2]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload2),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# make sure the comments still exist
data = self.get_zone(name)
- serverset = get_rrset(data, name, 'NS')
+ serverset = get_rrset(data, name, "NS")
print(serverset)
- self.assertEqual(serverset['records'], rrset2['records'])
- self.assertEqual(serverset['comments'], rrset['comments'])
+ self.assertEqual(serverset["records"], rrset2["records"])
+ self.assertEqual(serverset["comments"], rrset["comments"])
def test_search_rr_exact_zone(self):
name = unique_zone_name()
- self.create_zone(name=name, serial=22, soa_edit_api='')
- r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=" + name.rstrip('.')))
+ self.create_zone(name=name, serial=22, soa_edit_api="")
+ r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=" + name.rstrip(".")))
self.assert_success_json(r)
json = r.json()
print(json)
remove_timestamp(json)
- self.assertCountEqual(json, [
- {u'object_type': u'zone', u'name': name, u'zone_id': name},
- {u'content': u'ns1.example.com.',
- u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False,
- u'ttl': 3600, u'type': u'NS', u'name': name},
- {u'content': u'ns2.example.com.',
- u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False,
- u'ttl': 3600, u'type': u'NS', u'name': name},
- {u'content': u'a.misconfigured.dns.server.invalid. hostmaster.'+name+' 22 10800 3600 604800 3600',
- u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False,
- u'ttl': 3600, u'type': u'SOA', u'name': name},
- ])
+ self.assertCountEqual(
+ json,
+ [
+ {"object_type": "zone", "name": name, "zone_id": name},
+ {
+ "content": "ns1.example.com.",
+ "zone_id": name,
+ "zone": name,
+ "object_type": "record",
+ "disabled": False,
+ "ttl": 3600,
+ "type": "NS",
+ "name": name,
+ },
+ {
+ "content": "ns2.example.com.",
+ "zone_id": name,
+ "zone": name,
+ "object_type": "record",
+ "disabled": False,
+ "ttl": 3600,
+ "type": "NS",
+ "name": name,
+ },
+ {
+ "content": "a.misconfigured.dns.server.invalid. hostmaster." + name + " 22 10800 3600 604800 3600",
+ "zone_id": name,
+ "zone": name,
+ "object_type": "record",
+ "disabled": False,
+ "ttl": 3600,
+ "type": "SOA",
+ "name": name,
+ },
+ ],
+ )
def test_search_rr_exact_zone_filter_type_zone(self):
name = unique_zone_name()
data_type = "zone"
- self.create_zone(name=name, serial=22, soa_edit_api='')
- r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=" + name.rstrip('.') + "&object_type=" + data_type))
+ self.create_zone(name=name, serial=22, soa_edit_api="")
+ r = self.session.get(
+ self.url("/api/v1/servers/localhost/search-data?q=" + name.rstrip(".") + "&object_type=" + data_type)
+ )
self.assert_success_json(r)
print(r.json())
- self.assertEqual(r.json(), [
- {u'object_type': u'zone', u'name': name, u'zone_id': name},
- ])
+ self.assertEqual(
+ r.json(),
+ [
+ {"object_type": "zone", "name": name, "zone_id": name},
+ ],
+ )
def test_search_rr_exact_zone_filter_type_record(self):
name = unique_zone_name()
data_type = "record"
- self.create_zone(name=name, serial=22, soa_edit_api='')
- r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=" + name.rstrip('.') + "&object_type=" + data_type))
+ self.create_zone(name=name, serial=22, soa_edit_api="")
+ r = self.session.get(
+ self.url("/api/v1/servers/localhost/search-data?q=" + name.rstrip(".") + "&object_type=" + data_type)
+ )
self.assert_success_json(r)
json = r.json()
print(json)
remove_timestamp(json)
- self.assertCountEqual(json, [
- {u'content': u'ns1.example.com.',
- u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False,
- u'ttl': 3600, u'type': u'NS', u'name': name},
- {u'content': u'ns2.example.com.',
- u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False,
- u'ttl': 3600, u'type': u'NS', u'name': name},
- {u'content': u'a.misconfigured.dns.server.invalid. hostmaster.'+name+' 22 10800 3600 604800 3600',
- u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False,
- u'ttl': 3600, u'type': u'SOA', u'name': name},
- ])
+ self.assertCountEqual(
+ json,
+ [
+ {
+ "content": "ns1.example.com.",
+ "zone_id": name,
+ "zone": name,
+ "object_type": "record",
+ "disabled": False,
+ "ttl": 3600,
+ "type": "NS",
+ "name": name,
+ },
+ {
+ "content": "ns2.example.com.",
+ "zone_id": name,
+ "zone": name,
+ "object_type": "record",
+ "disabled": False,
+ "ttl": 3600,
+ "type": "NS",
+ "name": name,
+ },
+ {
+ "content": "a.misconfigured.dns.server.invalid. hostmaster." + name + " 22 10800 3600 604800 3600",
+ "zone_id": name,
+ "zone": name,
+ "object_type": "record",
+ "disabled": False,
+ "ttl": 3600,
+ "type": "SOA",
+ "name": name,
+ },
+ ],
+ )
def test_search_rr_substring(self):
name = unique_zone_name()
self.assertEqual(len(r.json()), 4)
def test_search_rr_case_insensitive(self):
- name = unique_zone_name()+'testsuffix.'
+ name = unique_zone_name() + "testsuffix."
self.create_zone(name=name)
r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=*testSUFFIX*"))
self.assert_success_json(r)
@unittest.skipIf(is_auth_lmdb(), "No comments in LMDB")
def test_search_rr_comment(self):
name = unique_zone_name()
- rrsets = [{
- "name": name,
- "type": "AAAA",
- "ttl": 3600,
- "records": [{
- "content": "2001:DB8::1",
- "disabled": False,
- }],
- "comments": [{
- "account": "test AAAA",
- "content": "blah",
- "modified_at": 11112,
- }],
- }]
+ rrsets = [
+ {
+ "name": name,
+ "type": "AAAA",
+ "ttl": 3600,
+ "records": [
+ {
+ "content": "2001:DB8::1",
+ "disabled": False,
+ }
+ ],
+ "comments": [
+ {
+ "account": "test AAAA",
+ "content": "blah",
+ "modified_at": 11112,
+ }
+ ],
+ }
+ ]
name, payload, data = self.create_zone(name=name, rrsets=rrsets)
r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=blah"))
self.assert_success_json(r)
data = r.json()
# should return the AAAA record
self.assertEqual(len(data), 1)
- self.assertEqual(data[0]['object_type'], 'comment')
- self.assertEqual(data[0]['type'], 'AAAA')
- self.assertEqual(data[0]['name'], name)
- self.assertEqual(data[0]['content'], rrsets[0]['comments'][0]['content'])
+ self.assertEqual(data[0]["object_type"], "comment")
+ self.assertEqual(data[0]["type"], "AAAA")
+ self.assertEqual(data[0]["name"], name)
+ self.assertEqual(data[0]["content"], rrsets[0]["comments"][0]["content"])
def test_search_after_rectify_with_ent(self):
name = unique_zone_name()
- search = name.split('.')[0]
+ search = name.split(".")[0]
rrset = {
- "name": 'sub.sub.' + name,
+ "name": "sub.sub." + name,
"type": "A",
"ttl": 3600,
- "records": [{
- "content": "4.3.2.1",
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": "4.3.2.1",
+ "disabled": False,
+ }
+ ],
}
self.create_zone(name=name, rrsets=[rrset])
pdnsutil_rectify(name)
name = unique_zone_name()
rrsets = [
{
- "name": 'a.' + name,
+ "name": "a." + name,
"type": "AAAA",
"ttl": 3600,
- "records": [{
- "content": "2001:DB8::1",
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": "2001:DB8::1",
+ "disabled": False,
+ }
+ ],
},
{
- "name": 'b.' + name,
+ "name": "b." + name,
"type": "AAAA",
"ttl": 3600,
- "records": [{
- "content": "2001:DB8::2",
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": "2001:DB8::2",
+ "disabled": False,
+ }
+ ],
},
]
- self.create_zone(name=name, rrsets=rrsets, dnssec=True, nsec3param='1 0 1 ab')
- dbrecs = get_db_records(name, 'AAAA')
- self.assertIsNotNone(dbrecs[0]['ordername'])
+ self.create_zone(name=name, rrsets=rrsets, dnssec=True, nsec3param="1 0 1 ab")
+ dbrecs = get_db_records(name, "AAAA")
+ self.assertIsNotNone(dbrecs[0]["ordername"])
def test_default_api_rectify_nodnssec(self):
"""Without any DNSSEC settings, rectify should still add ENTs. Setup the zone
name = unique_zone_name()
rrsets = [
{
- "name": 'a.sub.' + name,
+ "name": "a.sub." + name,
"type": "AAAA",
"ttl": 3600,
- "records": [{
- "content": "2001:DB8::1",
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": "2001:DB8::1",
+ "disabled": False,
+ }
+ ],
},
{
- "name": 'b.sub.' + name,
+ "name": "b.sub." + name,
"type": "AAAA",
"ttl": 3600,
- "records": [{
- "content": "2001:DB8::2",
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": "2001:DB8::2",
+ "disabled": False,
+ }
+ ],
},
]
self.create_zone(name=name, rrsets=rrsets)
# default-api-rectify is yes (by default). expect rectify to have happened.
- assert 'Rcode: 0 ' in sdig('sub.' + name, 'TXT')
+ assert "Rcode: 0 " in sdig("sub." + name, "TXT")
@unittest.skipIf(is_auth_lmdb(), "No get_db_records for LMDB")
def test_override_api_rectify(self):
name = unique_zone_name()
rrsets = [
{
- "name": 'a.' + name,
+ "name": "a." + name,
"type": "AAAA",
"ttl": 3600,
- "records": [{
- "content": "2001:DB8::1",
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": "2001:DB8::1",
+ "disabled": False,
+ }
+ ],
},
{
- "name": 'b.' + name,
+ "name": "b." + name,
"type": "AAAA",
"ttl": 3600,
- "records": [{
- "content": "2001:DB8::2",
- "disabled": False,
- }],
+ "records": [
+ {
+ "content": "2001:DB8::2",
+ "disabled": False,
+ }
+ ],
},
]
- self.create_zone(name=name, rrsets=rrsets, api_rectify=False, dnssec=True, nsec3param='1 0 1 ab')
- dbrecs = get_db_records(name, 'AAAA')
- self.assertIsNone(dbrecs[0]['ordername'])
+ self.create_zone(name=name, rrsets=rrsets, api_rectify=False, dnssec=True, nsec3param="1 0 1 ab")
+ dbrecs = get_db_records(name, "AAAA")
+ self.assertIsNone(dbrecs[0]["ordername"])
@unittest.skipIf(is_auth_lmdb(), "No get_db_records for LMDB")
def test_explicit_rectify_success(self):
- name, _, data = self.create_zone = self.create_zone(api_rectify=False, dnssec=True, nsec3param='1 0 1 ab')
- dbrecs = get_db_records(name, 'SOA')
- self.assertIsNone(dbrecs[0]['ordername'])
- r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/rectify"))
+ name, _, data = self.create_zone = self.create_zone(api_rectify=False, dnssec=True, nsec3param="1 0 1 ab")
+ dbrecs = get_db_records(name, "SOA")
+ self.assertIsNone(dbrecs[0]["ordername"])
+ r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data["id"] + "/rectify"))
self.assertEqual(r.status_code, 200)
- dbrecs = get_db_records(name, 'SOA')
- self.assertIsNotNone(dbrecs[0]['ordername'])
+ dbrecs = get_db_records(name, "SOA")
+ self.assertIsNotNone(dbrecs[0]["ordername"])
def test_explicit_rectify_slave(self):
# Some users want to move a zone to kind=Slave and then rectify, without a re-transfer.
- name, _, data = self.create_zone = self.create_zone(api_rectify=False, dnssec=True, nsec3param='1 0 1 ab')
- self.put_zone(data['id'], {'kind': 'Slave'})
- r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/rectify"))
+ name, _, data = self.create_zone = self.create_zone(api_rectify=False, dnssec=True, nsec3param="1 0 1 ab")
+ self.put_zone(data["id"], {"kind": "Slave"})
+ r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data["id"] + "/rectify"))
self.assertEqual(r.status_code, 200)
if not is_auth_lmdb():
- dbrecs = get_db_records(name, 'SOA')
- self.assertIsNotNone(dbrecs[0]['ordername'])
+ dbrecs = get_db_records(name, "SOA")
+ self.assertIsNotNone(dbrecs[0]["ordername"])
def test_cname_at_ent_place(self):
name, payload, zone = self.create_zone(dnssec=True, api_rectify=True)
rrset = {
- 'changetype': 'replace',
- 'name': 'sub2.sub1.' + name,
- 'type': "A",
- 'ttl': 3600,
- 'records': [{
- 'content': "4.3.2.1",
- 'disabled': False,
- }],
- }
- payload = {'rrsets': [rrset]}
+ "changetype": "replace",
+ "name": "sub2.sub1." + name,
+ "type": "A",
+ "ttl": 3600,
+ "records": [
+ {
+ "content": "4.3.2.1",
+ "disabled": False,
+ }
+ ],
+ }
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
- self.url("/api/v1/servers/localhost/zones/" + zone['id']),
+ self.url("/api/v1/servers/localhost/zones/" + zone["id"]),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
rrset = {
- 'changetype': 'replace',
- 'name': 'sub1.' + name,
- 'type': "CNAME",
- 'ttl': 3600,
- 'records': [{
- 'content': "www.example.org.",
- 'disabled': False,
- }],
- }
- payload = {'rrsets': [rrset]}
+ "changetype": "replace",
+ "name": "sub1." + name,
+ "type": "CNAME",
+ "ttl": 3600,
+ "records": [
+ {
+ "content": "www.example.org.",
+ "disabled": False,
+ }
+ ],
+ }
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
- self.url("/api/v1/servers/localhost/zones/" + zone['id']),
+ self.url("/api/v1/servers/localhost/zones/" + zone["id"]),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
def test_rrset_parameter_post_false(self):
name = unique_zone_name()
- payload = {
- 'name': name,
- 'kind': 'Native',
- 'nameservers': ['ns1.example.com.', 'ns2.example.com.']
- }
+ payload = {"name": name, "kind": "Native", "nameservers": ["ns1.example.com.", "ns2.example.com."]}
r = self.session.post(
self.url("/api/v1/servers/localhost/zones?rrsets=false"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
print(r.json())
self.assert_success_json(r)
self.assertEqual(r.status_code, 201)
- self.assertEqual(r.json().get('rrsets'), None)
+ self.assertEqual(r.json().get("rrsets"), None)
def test_rrset_false_parameter(self):
name = unique_zone_name()
- self.create_zone(name=name, kind='Native')
+ self.create_zone(name=name, kind="Native")
data = self.get_zone(name, rrsets="false")
- self.assertEqual(data.get('rrsets'), None)
+ self.assertEqual(data.get("rrsets"), None)
def test_rrset_true_parameter(self):
name = unique_zone_name()
- self.create_zone(name=name, kind='Native')
+ self.create_zone(name=name, kind="Native")
data = self.get_zone(name, rrsets="true")
- self.assertEqual(len(data['rrsets']), 2)
+ self.assertEqual(len(data["rrsets"]), 2)
def test_wrong_rrset_parameter(self):
name = unique_zone_name()
- self.create_zone(name=name, kind='Native')
- self.get_zone(
- name, rrsets="foobar",
- expect_error="'rrsets' request parameter value 'foobar' is not supported"
- )
+ self.create_zone(name=name, kind="Native")
+ self.get_zone(name, rrsets="foobar", expect_error="'rrsets' request parameter value 'foobar' is not supported")
def test_put_master_tsig_key_ids_non_existent(self):
name = unique_zone_name()
- keyname = unique_zone_name().split('.')[0]
- self.create_zone(name=name, kind='Native')
- payload = {
- 'master_tsig_key_ids': [keyname]
- }
- self.put_zone(name, payload, expect_error='A TSIG key with the name')
+ keyname = unique_zone_name().split(".")[0]
+ self.create_zone(name=name, kind="Native")
+ payload = {"master_tsig_key_ids": [keyname]}
+ self.put_zone(name, payload, expect_error="A TSIG key with the name")
def test_put_slave_tsig_key_ids_non_existent(self):
name = unique_zone_name()
- keyname = unique_zone_name().split('.')[0]
- self.create_zone(name=name, kind='Native')
- payload = {
- 'slave_tsig_key_ids': [keyname]
- }
- self.put_zone(name, payload, expect_error='A TSIG key with the name')
+ keyname = unique_zone_name().split(".")[0]
+ self.create_zone(name=name, kind="Native")
+ payload = {"slave_tsig_key_ids": [keyname]}
+ self.put_zone(name, payload, expect_error="A TSIG key with the name")
def test_zone_replace_rrsets_basic(self):
"""Basic test: all automatic modification is off, on replace the new rrsets are ingested as is."""
- name, _, _ = self.create_zone(dnssec=False, soa_edit='', soa_edit_api='')
+ name, _, _ = self.create_zone(dnssec=False, soa_edit="", soa_edit_api="")
rrsets = [
- {'name': name, 'type': 'SOA', 'ttl': 3600, 'records': [{'content': 'invalid. hostmaster.invalid. 1 10800 3600 604800 3600'}]},
- {'name': name, 'type': 'NS', 'ttl': 3600, 'records': [{'content': 'ns1.example.org.'}, {'content': 'ns2.example.org.'}]},
- {'name': 'www.' + name, 'type': 'A', 'ttl': 3600, 'records': [{'content': '192.0.2.1'}]},
- {'name': 'sub.' + name, 'type': 'NS', 'ttl': 3600, 'records': [{'content': 'ns1.example.org.'}]},
+ {
+ "name": name,
+ "type": "SOA",
+ "ttl": 3600,
+ "records": [{"content": "invalid. hostmaster.invalid. 1 10800 3600 604800 3600"}],
+ },
+ {
+ "name": name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns1.example.org."}, {"content": "ns2.example.org."}],
+ },
+ {"name": "www." + name, "type": "A", "ttl": 3600, "records": [{"content": "192.0.2.1"}]},
+ {"name": "sub." + name, "type": "NS", "ttl": 3600, "records": [{"content": "ns1.example.org."}]},
]
- self.put_zone(name, {'rrsets': rrsets})
+ self.put_zone(name, {"rrsets": rrsets})
data = self.get_zone(name)
for rrset in rrsets:
- rrset.setdefault('comments', [])
- for record in rrset['records']:
- record.setdefault('disabled', False)
- received_rrsets = data['rrsets']
+ rrset.setdefault("comments", [])
+ for record in rrset["records"]:
+ record.setdefault("disabled", False)
+ received_rrsets = data["rrsets"]
for rrset in received_rrsets:
- remove_timestamp(rrset['records'])
+ remove_timestamp(rrset["records"])
assert_eq_rrsets(received_rrsets, rrsets)
def test_zone_replace_rrsets_dnssec(self):
"""With dnssec: check automatic rectify is done"""
name, _, _ = self.create_zone(dnssec=True)
rrsets = [
- {'name': name, 'type': 'SOA', 'ttl': 3600, 'records': [{'content': 'invalid. hostmaster.invalid. 1 10800 3600 604800 3600'}]},
- {'name': name, 'type': 'NS', 'ttl': 3600, 'records': [{'content': 'ns1.example.org.'}, {'content': 'ns2.example.org.'}]},
- {'name': 'www.' + name, 'type': 'A', 'ttl': 3600, 'records': [{'content': '192.0.2.1'}]},
+ {
+ "name": name,
+ "type": "SOA",
+ "ttl": 3600,
+ "records": [{"content": "invalid. hostmaster.invalid. 1 10800 3600 604800 3600"}],
+ },
+ {
+ "name": name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns1.example.org."}, {"content": "ns2.example.org."}],
+ },
+ {"name": "www." + name, "type": "A", "ttl": 3600, "records": [{"content": "192.0.2.1"}]},
]
- self.put_zone(name, {'rrsets': rrsets})
+ self.put_zone(name, {"rrsets": rrsets})
if not is_auth_lmdb():
# lmdb: skip, no get_db_records implementations
- dbrecs = get_db_records(name, 'A')
- assert dbrecs[0]['ordername'] is not None # default = rectify enabled
+ dbrecs = get_db_records(name, "A")
+ assert dbrecs[0]["ordername"] is not None # default = rectify enabled
def test_zone_replace_rrsets_with_soa_edit(self):
"""SOA-EDIT was enabled before rrsets will be replaced"""
- name, _, _ = self.create_zone(soa_edit='INCEPTION-INCREMENT', soa_edit_api='SOA-EDIT-INCREASE')
+ name, _, _ = self.create_zone(soa_edit="INCEPTION-INCREMENT", soa_edit_api="SOA-EDIT-INCREASE")
rrsets = [
- {'name': name, 'type': 'SOA', 'ttl': 3600, 'records': [{'content': 'invalid. hostmaster.invalid. 1 10800 3600 604800 3600'}]},
- {'name': name, 'type': 'NS', 'ttl': 3600, 'records': [{'content': 'ns1.example.org.'}, {'content': 'ns2.example.org.'}]},
- {'name': 'www.' + name, 'type': 'A', 'ttl': 3600, 'records': [{'content': '192.0.2.1'}]},
- {'name': 'sub.' + name, 'type': 'NS', 'ttl': 3600, 'records': [{'content': 'ns1.example.org.'}]},
+ {
+ "name": name,
+ "type": "SOA",
+ "ttl": 3600,
+ "records": [{"content": "invalid. hostmaster.invalid. 1 10800 3600 604800 3600"}],
+ },
+ {
+ "name": name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns1.example.org."}, {"content": "ns2.example.org."}],
+ },
+ {"name": "www." + name, "type": "A", "ttl": 3600, "records": [{"content": "192.0.2.1"}]},
+ {"name": "sub." + name, "type": "NS", "ttl": 3600, "records": [{"content": "ns1.example.org."}]},
]
- self.put_zone(name, {'rrsets': rrsets})
+ self.put_zone(name, {"rrsets": rrsets})
data = self.get_zone(name)
- soa = [rrset['records'][0]['content'] for rrset in data['rrsets'] if rrset['type'] == 'SOA'][0]
+ soa = [rrset["records"][0]["content"] for rrset in data["rrsets"] if rrset["type"] == "SOA"][0]
assert int(soa.split()[2]) > 1 # serial is larger than what we sent
def test_zone_replace_rrsets_no_soa_primary(self):
"""Replace all RRsets but supply no SOA. Should fail."""
name, _, _ = self.create_zone()
rrsets = [
- {'name': name, 'type': 'NS', 'ttl': 3600, 'records': [{'content': 'ns1.example.org.'}, {'content': 'ns2.example.org.'}]}
+ {
+ "name": name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns1.example.org."}, {"content": "ns2.example.org."}],
+ }
+ ]
+ self.put_zone(name, {"rrsets": rrsets}, expect_error="Must give SOA record for zone when replacing all RR sets")
+
+ @parameterized.expand(
+ [
+ (None, []),
+ (
+ None,
+ [
+ {
+ "name": "$NAME$",
+ "type": "SOA",
+ "ttl": 3600,
+ "records": [{"content": "invalid. hostmaster.invalid. 1 10800 3600 604800 3600"}],
+ },
+ ],
+ ),
]
- self.put_zone(name, {'rrsets': rrsets}, expect_error='Must give SOA record for zone when replacing all RR sets')
-
- @parameterized.expand([
- (None, []),
- (None, [
- {'name': '$NAME$', 'type': 'SOA', 'ttl': 3600, 'records': [{'content': 'invalid. hostmaster.invalid. 1 10800 3600 604800 3600'}]},
- ]),
- ])
+ )
def test_zone_replace_rrsets_secondary(self, expected_error, rrsets):
"""
Replace all RRsets in a SECONDARY zone.
If no SOA is given, this should still succeed, also setting zone stale (but cannot assert this here).
"""
- name, _, _ = self.create_zone(kind='Secondary', nameservers=None, masters=['127.0.0.2'])
- self.put_zone(name, {'rrsets': templated_rrsets(rrsets, name)}, expect_error=expected_error)
-
- @parameterized.expand([
- (None, []),
- ("Modifying RRsets in Consumer zones is unsupported", [
- {'name': '$NAME$', 'type': 'SOA', 'ttl': 3600, 'records': [{'content': 'invalid. hostmaster.invalid. 1 10800 3600 604800 3600'}]},
- ]),
- ])
+ name, _, _ = self.create_zone(kind="Secondary", nameservers=None, masters=["127.0.0.2"])
+ self.put_zone(name, {"rrsets": templated_rrsets(rrsets, name)}, expect_error=expected_error)
+
+ @parameterized.expand(
+ [
+ (None, []),
+ (
+ "Modifying RRsets in Consumer zones is unsupported",
+ [
+ {
+ "name": "$NAME$",
+ "type": "SOA",
+ "ttl": 3600,
+ "records": [{"content": "invalid. hostmaster.invalid. 1 10800 3600 604800 3600"}],
+ },
+ ],
+ ),
+ ]
+ )
def test_zone_replace_rrsets_consumer(self, expected_error, rrsets):
- name, _, _ = self.create_zone(kind='Consumer', nameservers=None, masters=['127.0.0.2'])
- self.put_zone(name, {'rrsets': templated_rrsets(rrsets, name)}, expect_error=expected_error)
+ name, _, _ = self.create_zone(kind="Consumer", nameservers=None, masters=["127.0.0.2"])
+ self.put_zone(name, {"rrsets": templated_rrsets(rrsets, name)}, expect_error=expected_error)
def test_zone_replace_rrsets_negative_ttl(self):
- name, _, _ = self.create_zone(dnssec=False, soa_edit='', soa_edit_api='')
+ name, _, _ = self.create_zone(dnssec=False, soa_edit="", soa_edit_api="")
rrsets = [
- {'name': name, 'type': 'SOA', 'ttl': -1, 'records': [{'content': 'invalid. hostmaster.invalid. 1 10800 3600 604800 3600'}]},
+ {
+ "name": name,
+ "type": "SOA",
+ "ttl": -1,
+ "records": [{"content": "invalid. hostmaster.invalid. 1 10800 3600 604800 3600"}],
+ },
+ ]
+ self.put_zone(name, {"rrsets": rrsets}, expect_error="Key 'ttl' is not a positive Integer")
+
+ @parameterized.expand(
+ [
+ (
+ "IN MX: non-hostname content",
+ [{"name": "$NAME$", "type": "MX", "ttl": 3600, "records": [{"content": "10 mail@mx.example.org."}]}],
+ ),
+ (
+ "out of zone",
+ [{"name": "not-in-zone.", "type": "NS", "ttl": 3600, "records": [{"content": "ns1.example.org."}]}],
+ ),
+ (
+ "contains unsupported characters",
+ [{"name": "test:.$NAME$", "type": "NS", "ttl": 3600, "records": [{"content": "ns1.example.org."}]}],
+ ),
+ (
+ "unknown type",
+ [{"name": "$NAME$", "type": "INVALID", "ttl": 3600, "records": [{"content": "192.0.2.1"}]}],
+ ),
+ (
+ "conflicts with existing NS RRset",
+ [{"name": "$NAME$", "type": "CNAME", "ttl": 3600, "records": [{"content": "example.org."}]}],
+ ),
]
- self.put_zone(name, {'rrsets': rrsets}, expect_error="Key 'ttl' is not a positive Integer")
-
- @parameterized.expand([
- ("IN MX: non-hostname content", [{'name': '$NAME$', 'type': 'MX', 'ttl': 3600, 'records': [{"content": "10 mail@mx.example.org."}]}]),
- ("out of zone", [{'name': 'not-in-zone.', 'type': 'NS', 'ttl': 3600, 'records': [{"content": "ns1.example.org."}]}]),
- ("contains unsupported characters", [{'name': 'test:.$NAME$', 'type': 'NS', 'ttl': 3600, 'records': [{"content": "ns1.example.org."}]}]),
- ("unknown type", [{'name': '$NAME$', 'type': 'INVALID', 'ttl': 3600, 'records': [{"content": "192.0.2.1"}]}]),
- ("conflicts with existing NS RRset", [{'name': '$NAME$', 'type': 'CNAME', 'ttl': 3600, 'records': [{"content": "example.org."}]}]),
- ])
+ )
def test_zone_replace_rrsets_invalid(self, expected_error, invalid_rrsets):
"""Test validation of RRsets before replacing them"""
- name, _, _ = self.create_zone(dnssec=False, soa_edit='', soa_edit_api='')
+ name, _, _ = self.create_zone(dnssec=False, soa_edit="", soa_edit_api="")
base_rrsets = [
- {'name': name, 'type': 'SOA', 'ttl': 3600, 'records': [{'content': 'invalid. hostmaster.invalid. 1 10800 3600 604800 3600'}]},
- {'name': name, 'type': 'NS', 'ttl': 3600, 'records': [{'content': 'ns1.example.org.'}, {'content': 'ns2.example.org.'}]},
+ {
+ "name": name,
+ "type": "SOA",
+ "ttl": 3600,
+ "records": [{"content": "invalid. hostmaster.invalid. 1 10800 3600 604800 3600"}],
+ },
+ {
+ "name": name,
+ "type": "NS",
+ "ttl": 3600,
+ "records": [{"content": "ns1.example.org."}, {"content": "ns2.example.org."}],
+ },
]
rrsets = base_rrsets + templated_rrsets(invalid_rrsets, name)
- self.put_zone(name, {'rrsets': rrsets}, expect_error=expected_error)
+ self.put_zone(name, {"rrsets": rrsets}, expect_error=expected_error)
@unittest.skipIf(not is_auth_lmdb(), "No rrset timestamps except with LMDB")
def test_check_rrset_modified_at(self):
- name = 'host-18000.example.com.'
+ name = "host-18000.example.com."
r = self.session.get(self.url("/api/v1/servers/localhost/zones"))
domains = r.json()
- example_com = [domain for domain in domains if domain['name'] == u'example.com.'][0]
+ example_com = [domain for domain in domains if domain["name"] == "example.com."][0]
# verify single record from name that has a single record
- data = self.get_zone(example_com['id'], rrset_name="host-18000.example.com.")
- modified_at = data['rrsets'][0]['records'][0]['modified_at']
+ data = self.get_zone(example_com["id"], rrset_name="host-18000.example.com.")
+ modified_at = data["rrsets"][0]["records"][0]["modified_at"]
# write back the same data anyway, to get a new timestamp
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'A',
- 'ttl': 120,
- 'records':
- [
+ "changetype": "replace",
+ "name": name,
+ "type": "A",
+ "ttl": 120,
+ "records": [
{
- 'content': '192.168.1.80',
+ "content": "192.168.1.80",
}
- ]
+ ],
}
- payload = {'rrsets': [rrset]}
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/example.com"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# get the changed record.
- data = self.get_zone(example_com['id'], rrset_name="host-18000.example.com.")
- modified_at_new = data['rrsets'][0]['records'][0]['modified_at']
+ data = self.get_zone(example_com["id"], rrset_name="host-18000.example.com.")
+ modified_at_new = data["rrsets"][0]["records"][0]["modified_at"]
self.assertGreater(modified_at_new, modified_at)
@unittest.skipIf(is_auth_lmdb(), "Needs to perform database update")
def test_access_zone_with_invalid_content(self):
name, payload, zone = self.create_zone()
rrset = {
- 'changetype': 'replace',
- 'name': name,
- 'type': 'TXT',
- 'ttl': 3600,
- 'records': [
- {
- "content": "\"innocuous data\"",
- "disabled": False
- }
- ]
+ "changetype": "replace",
+ "name": name,
+ "type": "TXT",
+ "ttl": 3600,
+ "records": [{"content": '"innocuous data"', "disabled": False}],
}
- payload = {'rrsets': [rrset]}
- r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ payload = {"rrsets": [rrset]}
+ r = self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + name),
+ data=json.dumps(payload),
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
# Now alter the data - see get_db_records() for inspiration
- badcontent = 'invalid \"TXT data'
+ badcontent = 'invalid "TXT data'
db, placeholder = get_auth_db()
cur = db.cursor()
- cur.execute("""
+ cur.execute(
+ """
UPDATE records
- SET content="""+placeholder+"""
- WHERE name="""+placeholder+""" AND type='TXT'"""
- ,
- (badcontent, name.rstrip('.')))
- cur.execute('COMMIT') # Figuring out how many hours I wasted on this test because of this missing line is left as an exercise to the reader
+ SET content="""
+ + placeholder
+ + """
+ WHERE name="""
+ + placeholder
+ + """ AND type='TXT'""",
+ (badcontent, name.rstrip(".")),
+ )
+ cur.execute(
+ "COMMIT"
+ ) # Figuring out how many hours I wasted on this test because of this missing line is left as an exercise to the reader
cur.close()
db.close()
# Try and get the zone data
r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name))
self.assertEqual(r.status_code, 422)
- self.assertIn('Data field in DNS should end on a quote', r.json()['error'])
+ self.assertIn("Data field in DNS should end on a quote", r.json()["error"])
def test_underscore_names(self):
name = unique_zone_name()
- self.create_zone(name=name, kind='Native')
+ self.create_zone(name=name, kind="Native")
payload_metadata = {"type": "Metadata", "kind": "RFC1123-CONFORMANCE", "metadata": ["0"]}
- r = self.session.post(self.url("/api/v1/servers/localhost/zones/" + name + "/metadata"),
- data=json.dumps(payload_metadata))
+ r = self.session.post(
+ self.url("/api/v1/servers/localhost/zones/" + name + "/metadata"), data=json.dumps(payload_metadata)
+ )
rdata = r.json()
self.assertEqual(r.status_code, 201)
self.assertEqual(rdata["metadata"], payload_metadata["metadata"])
rrset = {
- 'changetype': 'replace',
- 'name': "_underscores_r_us_."+name,
- 'type': "A",
- 'ttl': 3600,
- 'records': [{
- "content": "42.42.42.42",
- "disabled": False,
- }],
- }
- payload = {'rrsets': [rrset]}
+ "changetype": "replace",
+ "name": "_underscores_r_us_." + name,
+ "type": "A",
+ "ttl": 3600,
+ "records": [
+ {
+ "content": "42.42.42.42",
+ "disabled": False,
+ }
+ ],
+ }
+ payload = {"rrsets": [rrset]}
r = self.session.patch(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
data = self.get_zone(name)
# check our record has appeared
- self.assertEqual(get_rrset(data, rrset['name'], 'A')['records'], rrset['records'])
+ self.assertEqual(get_rrset(data, rrset["name"], "A")["records"], rrset["records"])
+
@unittest.skipIf(not is_auth(), "Not applicable")
class AuthRootZone(ZonesApiTestCase, AuthZonesHelperMixin):
-
def setUp(self):
super(AuthRootZone, self).setUp()
# zone name is not unique, so delete the zone before each individual test.
self.session.delete(self.url("/api/v1/servers/localhost/zones/=2E"))
def test_create_zone(self):
- name, payload, data = self.create_zone(name='.', serial=22, soa_edit_api='')
- for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'soa_edit_api', 'soa_edit', 'account'):
+ name, payload, data = self.create_zone(name=".", serial=22, soa_edit_api="")
+ for k in (
+ "id",
+ "url",
+ "name",
+ "masters",
+ "kind",
+ "last_check",
+ "notified_serial",
+ "serial",
+ "soa_edit_api",
+ "soa_edit",
+ "account",
+ ):
self.assertIn(k, data)
if k in payload:
self.assertEqual(data[k], payload[k])
# validate generated SOA
- rec = get_first_rec(data, '.', 'SOA')
+ rec = get_first_rec(data, ".", "SOA")
self.assertEqual(
- rec['content'],
- "a.misconfigured.dns.server.invalid. hostmaster. " + str(payload['serial']) +
- " 10800 3600 604800 3600"
+ rec["content"],
+ "a.misconfigured.dns.server.invalid. hostmaster. " + str(payload["serial"]) + " 10800 3600 604800 3600",
)
# Regression test: verify zone list works
zonelist = self.session.get(self.url("/api/v1/servers/localhost/zones")).json()
print("zonelist:", zonelist)
- self.assertIn(payload['name'], [zone['name'] for zone in zonelist])
+ self.assertIn(payload["name"], [zone["name"] for zone in zonelist])
# Also test that fetching the zone works.
- print("id:", data['id'])
- self.assertEqual(data['id'], '=2E')
- data = self.get_zone(data['id'])
+ print("id:", data["id"])
+ self.assertEqual(data["id"], "=2E")
+ data = self.get_zone(data["id"])
print("zone (fetched):", data)
- for k in ('name', 'kind'):
+ for k in ("name", "kind"):
self.assertIn(k, data)
self.assertEqual(data[k], payload[k])
- self.assertEqual(data['rrsets'][0]['name'], '.')
+ self.assertEqual(data["rrsets"][0]["name"], ".")
def test_update_zone(self):
- name, payload, zone = self.create_zone(name='.')
- zone_id = '=2E'
+ name, payload, zone = self.create_zone(name=".")
+ zone_id = "=2E"
# update, set as Master and enable SOA-EDIT-API
payload = {
- 'kind': 'Master',
- 'masters': ['192.0.2.1', '192.0.2.2'],
- 'soa_edit_api': 'EPOCH',
- 'soa_edit': 'EPOCH'
+ "kind": "Master",
+ "masters": ["192.0.2.1", "192.0.2.2"],
+ "soa_edit_api": "EPOCH",
+ "soa_edit": "EPOCH",
}
self.put_zone(zone_id, payload)
data = self.get_zone(zone_id)
self.assertIn(k, data)
self.assertEqual(data[k], payload[k])
# update, back to Native and empty(off)
- payload = {
- 'kind': 'Native',
- 'soa_edit_api': '',
- 'soa_edit': ''
- }
+ payload = {"kind": "Native", "soa_edit_api": "", "soa_edit": ""}
self.put_zone(zone_id, payload)
data = self.get_zone(zone_id)
for k in payload.keys():
@unittest.skipIf(not is_recursor(), "Not applicable")
class RecursorZones(ZonesApiTestCase):
-
def create_zone(self, name=None, kind=None, rd=False, servers=None, notify_allowed=False):
if name is None:
name = unique_zone_name()
if servers is None:
servers = []
payload = {
- 'name': name,
- 'kind': kind,
- 'servers': servers,
- 'recursion_desired': rd,
- 'notify_allowed': notify_allowed
+ "name": name,
+ "kind": kind,
+ "servers": servers,
+ "recursion_desired": rd,
+ "notify_allowed": notify_allowed,
}
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success_json(r)
return payload, r.json()
def test_create_auth_zone(self):
- payload, data = self.create_zone(kind='Native')
+ payload, data = self.create_zone(kind="Native")
for k in payload.keys():
self.assertEqual(data[k], payload[k])
def test_create_zone_no_name(self):
payload = {
- 'name': '',
- 'kind': 'Native',
- 'servers': ['8.8.8.8'],
- 'recursion_desired': False,
+ "name": "",
+ "kind": "Native",
+ "servers": ["8.8.8.8"],
+ "recursion_desired": False,
}
print(payload)
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 422)
- self.assert_in_json_error('is not canonical', r.json())
+ self.assert_in_json_error("is not canonical", r.json())
def test_create_forwarded_zone(self):
- payload, data = self.create_zone(kind='Forwarded', rd=False, servers=['8.8.8.8'])
+ payload, data = self.create_zone(kind="Forwarded", rd=False, servers=["8.8.8.8"])
# return values are normalized
- payload['servers'][0] += ':53'
+ payload["servers"][0] += ":53"
for k in payload.keys():
self.assertEqual(data[k], payload[k])
def test_create_forwarded_zone_notify_allowed(self):
- payload, data = self.create_zone(kind='Forwarded', rd=False, servers=['8.8.8.8'], notify_allowed=True)
+ payload, data = self.create_zone(kind="Forwarded", rd=False, servers=["8.8.8.8"], notify_allowed=True)
# return values are normalized
- payload['servers'][0] += ':53'
+ payload["servers"][0] += ":53"
for k in payload.keys():
self.assertEqual(data[k], payload[k])
def test_create_forwarded_rd_zone(self):
- payload, data = self.create_zone(name='google.com.', kind='Forwarded', rd=True, servers=['8.8.8.8'])
+ payload, data = self.create_zone(name="google.com.", kind="Forwarded", rd=True, servers=["8.8.8.8"])
# return values are normalized
- payload['servers'][0] += ':53'
+ payload["servers"][0] += ":53"
for k in payload.keys():
self.assertEqual(data[k], payload[k])
def test_create_auth_zone_with_symbols(self):
- payload, data = self.create_zone(name='foo/bar.'+unique_zone_name(), kind='Native')
- expected_id = (payload['name'].replace('/', '=2F'))
+ payload, data = self.create_zone(name="foo/bar." + unique_zone_name(), kind="Native")
+ expected_id = payload["name"].replace("/", "=2F")
for k in payload.keys():
self.assertEqual(data[k], payload[k])
- self.assertEqual(data['id'], expected_id)
+ self.assertEqual(data["id"], expected_id)
def test_rename_auth_zone(self):
- payload, data = self.create_zone(kind='Native')
- name = payload['name']
+ payload, data = self.create_zone(kind="Native")
+ name = payload["name"]
# now rename it
- payload = {
- 'name': 'renamed-'+name,
- 'kind': 'Native',
- 'recursion_desired': False
- }
+ payload = {"name": "renamed-" + name, "kind": "Native", "recursion_desired": False}
r = self.session.put(
self.url("/api/v1/servers/localhost/zones/" + name),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success(r)
- data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + payload['name'])).json()
+ data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + payload["name"])).json()
for k in payload.keys():
self.assertEqual(data[k], payload[k])
def test_zone_delete(self):
- payload, zone = self.create_zone(kind='Native')
- name = payload['name']
+ payload, zone = self.create_zone(kind="Native")
+ name = payload["name"]
r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + name))
self.assertEqual(r.status_code, 204)
- self.assertNotIn('Content-Type', r.headers)
+ self.assertNotIn("Content-Type", r.headers)
def test_search_rr_exact_zone(self):
name = unique_zone_name()
- self.create_zone(name=name, kind='Native')
+ self.create_zone(name=name, kind="Native")
r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=" + name))
self.assert_success_json(r)
print(r.json())
- self.assertEqual(r.json(), [{u'type': u'zone', u'name': name, u'zone_id': name}])
+ self.assertEqual(r.json(), [{"type": "zone", "name": name, "zone_id": name}])
def test_search_rr_substring(self):
- name = 'search-rr-zone.name.'
- self.create_zone(name=name, kind='Native')
+ name = "search-rr-zone.name."
+ self.create_zone(name=name, kind="Native")
r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=rr-zone"))
self.assert_success_json(r)
print(r.json())
# should return zone, SOA
self.assertEqual(len(r.json()), 2)
+
@unittest.skipIf(not is_auth(), "Not applicable")
class AuthZoneKeys(ZonesApiTestCase, AuthZonesHelperMixin):
-
def test_get_keys(self):
- r = self.session.get(
- self.url("/api/v1/servers/localhost/zones/powerdnssec.org./cryptokeys"))
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones/powerdnssec.org./cryptokeys"))
self.assert_success_json(r)
keys = r.json()
self.assertGreater(len(keys), 0)
key0 = deepcopy(keys[0])
- del key0['dnskey']
- del key0['ds']
+ del key0["dnskey"]
+ del key0["ds"]
expected = {
- u'algorithm': u'ECDSAP256SHA256',
- u'bits': 256,
- u'active': True,
- u'type': u'Cryptokey',
- u'keytype': u'csk',
- u'flags': 257,
- u'published': True,
- u'id': 1}
+ "algorithm": "ECDSAP256SHA256",
+ "bits": 256,
+ "active": True,
+ "type": "Cryptokey",
+ "keytype": "csk",
+ "flags": 257,
+ "published": True,
+ "id": 1,
+ }
self.assertEqual(key0, expected)
- keydata = keys[0]['dnskey'].split()
+ keydata = keys[0]["dnskey"].split()
self.assertEqual(len(keydata), 4)
def test_get_keys_with_cds(self):
payload_metadata = {"type": "Metadata", "kind": "PUBLISH-CDS", "metadata": ["4"]}
- r = self.session.post(self.url("/api/v1/servers/localhost/zones/powerdnssec.org./metadata"),
- data=json.dumps(payload_metadata))
+ r = self.session.post(
+ self.url("/api/v1/servers/localhost/zones/powerdnssec.org./metadata"), data=json.dumps(payload_metadata)
+ )
rdata = r.json()
self.assertEqual(r.status_code, 201)
self.assertEqual(rdata["metadata"], payload_metadata["metadata"])
- r = self.session.get(
- self.url("/api/v1/servers/localhost/zones/powerdnssec.org./cryptokeys"))
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones/powerdnssec.org./cryptokeys"))
self.assert_success_json(r)
keys = r.json()
self.assertGreater(len(keys), 0)
key0 = deepcopy(keys[0])
- self.assertEqual(len(key0['cds']), 1)
- self.assertIn(key0['cds'][0], key0['ds'])
- self.assertEqual(key0['cds'][0].split()[2], '4')
- del key0['dnskey']
- del key0['ds']
- del key0['cds']
+ self.assertEqual(len(key0["cds"]), 1)
+ self.assertIn(key0["cds"][0], key0["ds"])
+ self.assertEqual(key0["cds"][0].split()[2], "4")
+ del key0["dnskey"]
+ del key0["ds"]
+ del key0["cds"]
expected = {
- u'algorithm': u'ECDSAP256SHA256',
- u'bits': 256,
- u'active': True,
- u'type': u'Cryptokey',
- u'keytype': u'csk',
- u'flags': 257,
- u'published': True,
- u'id': 1}
+ "algorithm": "ECDSAP256SHA256",
+ "bits": 256,
+ "active": True,
+ "type": "Cryptokey",
+ "keytype": "csk",
+ "flags": 257,
+ "published": True,
+ "id": 1,
+ }
self.assertEqual(key0, expected)
- keydata = keys[0]['dnskey'].split()
+ keydata = keys[0]["dnskey"].split()
self.assertEqual(len(keydata), 4)
r = self.session.delete(self.url("/api/v1/servers/localhost/zones/powerdnssec.org./metadata/PUBLISH-CDS"))
from test_helper import ApiTestCase, is_auth, pdnsutil, unique_zone_name
+
@unittest.skipIf(not is_auth(), "Not applicable")
class Cryptokeys(ApiTestCase):
-
def setUp(self):
super(Cryptokeys, self).setUp()
self.keyid = 0
self.zone = unique_zone_name()
self.zone_nodot = self.zone[:-1]
- payload = {
- 'name': self.zone,
- 'kind': 'Native',
- 'nameservers': ['ns1.example.com.', 'ns2.example.com.']
- }
+ payload = {"name": self.zone, "kind": "Native", "nameservers": ["ns1.example.com.", "ns2.example.com."]}
r = self.session.post(
self.url("/api/v1/servers/localhost/zones"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assert_success_json(r)
self.assertEqual(r.status_code, 201)
self.remove_zone_key(self.keyid)
# Adding a key to self.zone using the pdnsutil command
- def add_zone_key(self, status=['inactive']):
+ def add_zone_key(self, status=["inactive"]):
return pdnsutil("add-zone-key", self.zone_nodot, "ksk", *status)
# Removes a key from self.zone by id using the pdnsutil command
def test_delete(self):
self.keyid = self.add_zone_key()
- #checks the status code. I don't know how to test explicit that the backend fail removing a key.
- r = self.session.delete(self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid))
+ # checks the status code. I don't know how to test explicit that the backend fail removing a key.
+ r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys/" + self.keyid))
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
def test_get_wrong_zone(self):
self.keyid = self.add_zone_key()
- r = self.session.get(self.url("/api/v1/servers/localhost/zones/"+self.zone+"fail/cryptokeys/"+self.keyid))
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + self.zone + "fail/cryptokeys/" + self.keyid))
self.assertEqual(r.status_code, 404)
def test_delete_wrong_id(self):
self.keyid = self.add_zone_key()
- r = self.session.delete(self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/1234567"))
+ r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys/1234567"))
self.assertEqual(r.status_code, 404)
def test_delete_wrong_zone(self):
self.keyid = self.add_zone_key()
- #checks for not covered zonename
- r = self.session.delete(self.url("/api/v1/servers/localhost/zones/"+self.zone+"fail/cryptokeys/"+self.keyid))
+ # checks for not covered zonename
+ r = self.session.delete(
+ self.url("/api/v1/servers/localhost/zones/" + self.zone + "fail/cryptokeys/" + self.keyid)
+ )
self.assertEqual(r.status_code, 404)
def test_delete_key_is_gone(self):
self.keyid = self.add_zone_key()
self.remove_zone_key(self.keyid)
- #checks for key is gone. Its ok even if no key had to be deleted. Or something went wrong with the backend.
- r = self.session.delete(self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid))
+ # checks for key is gone. Its ok even if no key had to be deleted. Or something went wrong with the backend.
+ r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys/" + self.keyid))
self.assertEqual(r.status_code, 404)
# Prepares the json object for Post and sends it to the server
- def add_key(self, content='', type='ksk', active='true', algo='', bits=None):
+ def add_key(self, content="", type="ksk", active="true", algo="", bits=None):
payload = {
- 'keytype': type,
- 'active': active,
+ "keytype": type,
+ "active": active,
}
if algo:
- payload['algorithm'] = algo
+ payload["algorithm"] = algo
if bits is not None:
- payload['bits'] = bits
- if content != '':
- payload['content'] = content
+ payload["bits"] = bits
+ if content != "":
+ payload["content"] = content
print("create key with payload:", payload)
r = self.session.post(
- self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys"),
+ self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys"),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
return r
# Test POST for a positive result and delete the added key
- def post_helper(self, content='', algo='', bits=None):
+ def post_helper(self, content="", algo="", bits=None):
r = self.add_key(content=content, algo=algo, bits=bits)
self.assert_success_json(r)
self.assertEqual(r.status_code, 201)
response = r.json()
# Only a ksk added, so expected type is csk
- self.assertEqual(response['keytype'], 'csk')
- self.keyid = response['id']
+ self.assertEqual(response["keytype"], "csk")
+ self.keyid = response["id"]
# Check if the key is actually added
out = pdnsutil("list-keys", self.zone_nodot)
self.assertIn(self.zone_nodot, out)
# Test POST to add a key with specific name
def test_post_specific_name(self):
- self.post_helper(algo='ecdsa256')
+ self.post_helper(algo="ecdsa256")
# Test POST to add a private key from external resource
def test_post_content(self):
- self.post_helper(content="Private-key-format: v1.2\n"+
- "Algorithm: 8 (RSASHA256)\n"+
- "Modulus: 4GlYLGgDI7ohnP8SmEW8EBERbNRusDcg0VQda/EPVHU=\n"+
- "PublicExponent: AQAB\n"+
- "PrivateExponent: JBnuXF5zOtkjtSz3odV+Fk5UNUTTeCsiI16dkcM7TVU=\n"+
- "Prime1: /w7TM4118RoSEvP8+dgnCw==\n"+
- "Prime2: 4T2KhkYLa3w7rdK3Cb2ifw==\n"+
- "Exponent1: 3aeKj9Ct4JuhfWsgPBhGxQ==\n"+
- "Exponent2: tfh1OMPQKBdnU6iATjNR2w==\n"+
- "Coefficient: eVrHe/kauqOewSKndIImrg==)\n")
+ self.post_helper(
+ content="Private-key-format: v1.2\n"
+ + "Algorithm: 8 (RSASHA256)\n"
+ + "Modulus: 4GlYLGgDI7ohnP8SmEW8EBERbNRusDcg0VQda/EPVHU=\n"
+ + "PublicExponent: AQAB\n"
+ + "PrivateExponent: JBnuXF5zOtkjtSz3odV+Fk5UNUTTeCsiI16dkcM7TVU=\n"
+ + "Prime1: /w7TM4118RoSEvP8+dgnCw==\n"
+ + "Prime2: 4T2KhkYLa3w7rdK3Cb2ifw==\n"
+ + "Exponent1: 3aeKj9Ct4JuhfWsgPBhGxQ==\n"
+ + "Exponent2: tfh1OMPQKBdnU6iATjNR2w==\n"
+ + "Coefficient: eVrHe/kauqOewSKndIImrg==)\n"
+ )
def test_post_wrong_key_format(self):
r = self.add_key(content="trollololoooolll")
self.assert_error_json(r)
self.assertEqual(r.status_code, 422)
- self.assertIn("Key could not be parsed. Make sure your key format is correct.",r.json()['error'])
+ self.assertIn("Key could not be parsed. Make sure your key format is correct.", r.json()["error"])
def test_post_wrong_keytype(self):
- r = self.add_key(type='sdfdhhgj')
+ r = self.add_key(type="sdfdhhgj")
self.assert_error_json(r)
self.assertEqual(r.status_code, 422)
- self.assertIn("Invalid keytype",r.json()['error'])
+ self.assertIn("Invalid keytype", r.json()["error"])
def test_post_wrong_bits_format(self):
- r = self.add_key(bits='sdfdhhgj')
+ r = self.add_key(bits="sdfdhhgj")
self.assert_error_json(r)
self.assertEqual(r.status_code, 422)
- self.assertIn("'bits' must be a positive integer value",r.json()['error'])
+ self.assertIn("'bits' must be a positive integer value", r.json()["error"])
- r = self.add_key(bits='5.5')
+ r = self.add_key(bits="5.5")
self.assert_error_json(r)
self.assertEqual(r.status_code, 422)
- self.assertIn("'bits' must be a positive integer value",r.json()['error'])
+ self.assertIn("'bits' must be a positive integer value", r.json()["error"])
- r = self.add_key(bits='-6')
+ r = self.add_key(bits="-6")
self.assert_error_json(r)
self.assertEqual(r.status_code, 422)
- self.assertIn("'bits' must be a positive integer value",r.json()['error'])
+ self.assertIn("'bits' must be a positive integer value", r.json()["error"])
def test_post_unsupported_algorithm(self):
- r = self.add_key(algo='lkjhgf')
+ r = self.add_key(algo="lkjhgf")
self.assert_error_json(r)
self.assertEqual(r.status_code, 422)
- self.assertIn("Unknown algorithm:",r.json()['error'])
+ self.assertIn("Unknown algorithm:", r.json()["error"])
def test_post_forgot_bits(self):
r = self.add_key(algo="rsasha256")
self.assert_error_json(r)
self.assertEqual(r.status_code, 422)
- self.assertIn("key requires the size (in bits) to be passed", r.json()['error'])
+ self.assertIn("key requires the size (in bits) to be passed", r.json()["error"])
def test_post_wrong_bit_size(self):
r = self.add_key(algo=10, bits=30)
self.assert_error_json(r)
- self.assertEqual(r.status_code,422)
- self.assertIn("The algorithm does not support the given bit size.", r.json()['error'])
+ self.assertEqual(r.status_code, 422)
+ self.assertIn("The algorithm does not support the given bit size.", r.json()["error"])
def test_post_can_not_guess_key_size(self):
r = self.add_key(algo=17)
self.assert_error_json(r)
- self.assertEqual(r.status_code,422)
- self.assertIn("Can not guess key size for algorithm", r.json()['error'])
+ self.assertEqual(r.status_code, 422)
+ self.assertIn("Can not guess key size for algorithm", r.json()["error"])
def test_put_activate_key(self):
self.keyid = self.add_zone_key()
payload = {
- 'active': True,
- 'published': True,
+ "active": True,
+ "published": True,
}
r = self.session.put(
- self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+ self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys/" + self.keyid),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
self.assertIn("Active", out)
def test_put_deactivate_key(self):
- self.keyid = self.add_zone_key(status=['active'])
+ self.keyid = self.add_zone_key(status=["active"])
# deactivate key
payload2 = {
- 'active': False,
- 'published': True,
+ "active": False,
+ "published": True,
}
r = self.session.put(
- self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+ self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys/" + self.keyid),
data=json.dumps(payload2),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
# deactivate key
payload = {
- 'active': False,
- 'published': True,
+ "active": False,
+ "published": True,
}
r = self.session.put(
- self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+ self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys/" + self.keyid),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
self.assertIn("Inactive", out)
def test_put_activate_active_key(self):
- self.keyid = self.add_zone_key(status=['active'])
+ self.keyid = self.add_zone_key(status=["active"])
# activate key
payload2 = {
- 'active': True,
- 'published': True,
+ "active": True,
+ "published": True,
}
r = self.session.put(
- self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+ self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys/" + self.keyid),
data=json.dumps(payload2),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
self.assertIn("Active", out)
def test_put_unpublish_key(self):
- self.keyid = self.add_zone_key(status=['active'])
+ self.keyid = self.add_zone_key(status=["active"])
payload = {
- 'active': True,
- 'published': False,
+ "active": True,
+ "published": False,
}
r = self.session.put(
- self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+ self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys/" + self.keyid),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
self.assertIn("Unpublished", out)
def test_put_publish_key(self):
- self.keyid = self.add_zone_key(status=['active', 'unpublished'])
+ self.keyid = self.add_zone_key(status=["active", "unpublished"])
# deactivate key
payload2 = {
- 'active': True,
- 'published': True,
+ "active": True,
+ "published": True,
}
r = self.session.put(
- self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+ self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys/" + self.keyid),
data=json.dumps(payload2),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
self.assertIn("Published", out)
def test_put_publish_published_key(self):
- self.keyid = self.add_zone_key(status=['active'])
+ self.keyid = self.add_zone_key(status=["active"])
# deactivate key
payload = {
- 'active': True,
- 'published': True,
+ "active": True,
+ "published": True,
}
r = self.session.put(
- self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+ self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys/" + self.keyid),
data=json.dumps(payload),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
self.assertIn("Published", out)
def test_put_unpublish_unpublished_key(self):
- self.keyid = self.add_zone_key(status=['active', 'unpublished'])
+ self.keyid = self.add_zone_key(status=["active", "unpublished"])
# activate key
payload2 = {
- 'active': True,
- 'published': False,
+ "active": True,
+ "published": False,
}
r = self.session.put(
- self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+ self.url("/api/v1/servers/localhost/zones/" + self.zone + "/cryptokeys/" + self.keyid),
data=json.dumps(payload2),
- headers={'content-type': 'application/json'})
+ headers={"content-type": "application/json"},
+ )
self.assertEqual(r.status_code, 204)
self.assertEqual(r.content, b"")
else:
from urllib.parse import urljoin
-DAEMON = os.environ.get('DAEMON', 'authoritative')
-PDNSUTIL_CMD = os.environ.get('PDNSUTIL_CMD', 'NOT_SET BUT_THIS MIGHT_BE_A_LIST').split(' ')
-BACKEND = os.environ.get('BACKEND', 'gsqlite3')
-MYSQL_DB = os.environ.get('MYSQL_DB', 'pdnsapi')
-MYSQL_USER = os.environ.get('MYSQL_USER', 'root')
-MYSQL_HOST = os.environ.get('MYSQL_HOST', 'localhost')
-MYSQL_PASSWD = os.environ.get('MYSQL_PASWORD', '')
-PGSQL_DB = os.environ.get('PGSQL_DB', 'pdnsapi')
-SQLITE_DB = os.environ.get('SQLITE_DB', 'pdns.sqlite3')
-LMDB_DB = os.environ.get('SQLITE_DB', 'pdns.lmdb')
-SDIG = os.environ.get('SDIG', 'sdig')
-DNSPORT = os.environ.get('DNSPORT', '53')
+DAEMON = os.environ.get("DAEMON", "authoritative")
+PDNSUTIL_CMD = os.environ.get("PDNSUTIL_CMD", "NOT_SET BUT_THIS MIGHT_BE_A_LIST").split(" ")
+BACKEND = os.environ.get("BACKEND", "gsqlite3")
+MYSQL_DB = os.environ.get("MYSQL_DB", "pdnsapi")
+MYSQL_USER = os.environ.get("MYSQL_USER", "root")
+MYSQL_HOST = os.environ.get("MYSQL_HOST", "localhost")
+MYSQL_PASSWD = os.environ.get("MYSQL_PASWORD", "")
+PGSQL_DB = os.environ.get("PGSQL_DB", "pdnsapi")
+SQLITE_DB = os.environ.get("SQLITE_DB", "pdns.sqlite3")
+LMDB_DB = os.environ.get("SQLITE_DB", "pdns.lmdb")
+SDIG = os.environ.get("SDIG", "sdig")
+DNSPORT = os.environ.get("DNSPORT", "53")
-class ApiTestCase(unittest.TestCase):
+class ApiTestCase(unittest.TestCase):
def setUp(self):
# TODO: config
- self.server_address = '127.0.0.1'
- self.webServerBasicAuthPassword = 'something'
- self.server_port = int(os.environ.get('WEBPORT', '5580'))
- self.server_url = 'http://%s:%s/' % (self.server_address, self.server_port)
- self.server_web_password = os.environ.get('WEBPASSWORD', 'MISSING')
+ self.server_address = "127.0.0.1"
+ self.webServerBasicAuthPassword = "something"
+ self.server_port = int(os.environ.get("WEBPORT", "5580"))
+ self.server_url = "http://%s:%s/" % (self.server_address, self.server_port)
+ self.server_web_password = os.environ.get("WEBPASSWORD", "MISSING")
self.session = requests.Session()
- self.session.headers = {'X-API-Key': os.environ.get('APIKEY', 'changeme-key'), 'Origin': 'http://%s:%s' % (self.server_address, self.server_port)}
+ self.session.headers = {
+ "X-API-Key": os.environ.get("APIKEY", "changeme-key"),
+ "Origin": "http://%s:%s" % (self.server_address, self.server_port),
+ }
if is_recursor():
- self.server_url = 'https://%s:%s/' % (self.server_address, self.server_port)
- self.session.verify = 'ca.pem'
+ self.server_url = "https://%s:%s/" % (self.server_address, self.server_port)
+ self.session.verify = "ca.pem"
def url(self, relative_url):
return urljoin(self.server_url, relative_url)
except Exception:
print(result.content)
raise
- self.assertEqual(result.headers['Content-Type'], 'application/json')
+ self.assertEqual(result.headers["Content-Type"], "application/json")
def assert_error_json(self, result):
- self.assertTrue(400 <= result.status_code < 600, "Response has not an error code "+str(result.status_code))
- self.assertEqual(result.headers['Content-Type'], 'application/json', "Response status code "+str(result.status_code))
+ self.assertTrue(400 <= result.status_code < 600, "Response has not an error code " + str(result.status_code))
+ self.assertEqual(
+ result.headers["Content-Type"], "application/json", "Response status code " + str(result.status_code)
+ )
def assert_success(self, result):
try:
def unique_zone_name():
- return 'test-' + datetime.now().strftime('%d%H%S%M%f') + '.org.'
+ return "test-" + datetime.now().strftime("%d%H%S%M%f") + ".org."
+
def unique_tsigkey_name():
- return 'test-' + datetime.now().strftime('%d%H%S%M%f') + '-key'
+ return "test-" + datetime.now().strftime("%d%H%S%M%f") + "-key"
+
def is_auth():
- return DAEMON == 'authoritative'
+ return DAEMON == "authoritative"
+
def is_auth_lmdb():
- return DAEMON == 'authoritative' and BACKEND == 'lmdb'
+ return DAEMON == "authoritative" and BACKEND == "lmdb"
+
def is_recursor():
- return DAEMON == 'recursor'
+ return DAEMON == "recursor"
def get_auth_db():
"""Return Connection to Authoritative backend DB."""
- if BACKEND == 'gmysql':
+ if BACKEND == "gmysql":
return mysql.connector.connect(database=MYSQL_DB, user=MYSQL_USER, host=MYSQL_HOST, password=MYSQL_PASSWD), "%s"
- elif BACKEND == 'gpgsql':
+ elif BACKEND == "gpgsql":
return psycopg2.connect(database=PGSQL_DB), "%s"
else:
return sqlite3.Connection(SQLITE_DB), "?"
def get_db_records(zonename, qtype):
db, placeholder = get_auth_db()
cur = db.cursor()
- cur.execute("""
+ cur.execute(
+ """
SELECT name, type, content, ttl, ordername
FROM records
- WHERE type = """+placeholder+""" AND domain_id = (
- SELECT id FROM domains WHERE name = """+placeholder+"""
- )""", (qtype, zonename.rstrip('.')))
+ WHERE type = """
+ + placeholder
+ + """ AND domain_id = (
+ SELECT id FROM domains WHERE name = """
+ + placeholder
+ + """
+ )""",
+ (qtype, zonename.rstrip(".")),
+ )
rows = cur.fetchall()
cur.close()
db.close()
- recs = [{'name': row[0], 'type': row[1], 'content': row[2], 'ttl': row[3], 'ordername': row[4]} for row in rows]
+ recs = [{"name": row[0], "type": row[1], "content": row[2], "ttl": row[3], "ordername": row[4]} for row in rows]
print("DB Records:", recs)
return recs
def pdnsutil(subcommand, *args):
try:
- return subprocess.check_output(PDNSUTIL_CMD + [subcommand] + list(args), close_fds=True).decode('ascii')
+ return subprocess.check_output(PDNSUTIL_CMD + [subcommand] + list(args), close_fds=True).decode("ascii")
except subprocess.CalledProcessError as except_inst:
- raise RuntimeError("pdnsutil %s %s failed: %s" % (subcommand, args, except_inst.output.decode('ascii', errors='replace')))
+ raise RuntimeError(
+ "pdnsutil %s %s failed: %s" % (subcommand, args, except_inst.output.decode("ascii", errors="replace"))
+ )
+
def pdnsutil_rectify(zonename):
"""Run pdnsutil rectify-zone on the given zone."""
- pdnsutil('rectify-zone', zonename)
+ pdnsutil("rectify-zone", zonename)
+
def sdig(*args):
if is_auth():
- sdig_command_line = [SDIG, '127.0.0.1', str(DNSPORT)] + list(args)
+ sdig_command_line = [SDIG, "127.0.0.1", str(DNSPORT)] + list(args)
else:
- sdig_command_line = [SDIG, '127.0.0.1', str(DNSPORT)] + list(args) + ["recurse"]
+ sdig_command_line = [SDIG, "127.0.0.1", str(DNSPORT)] + list(args) + ["recurse"]
try:
- return subprocess.check_output(sdig_command_line).decode('utf-8')
+ return subprocess.check_output(sdig_command_line).decode("utf-8")
except subprocess.CalledProcessError as except_inst:
- raise RuntimeError("sdig %s failed: %s" % (sdig_command_line, except_inst.output.decode('ascii', errors='replace')))
+ raise RuntimeError(
+ "sdig %s failed: %s" % (sdig_command_line, except_inst.output.decode("ascii", errors="replace"))
+ )
from eqdnsmessage import AssertEqualDNSMessageMixin
+
class AuthTest(AssertEqualDNSMessageMixin, unittest.TestCase):
"""
Setup auth required for the tests
"""
- _confdir = 'auth'
+ _confdir = "auth"
_authPort = 5300
_backend = os.getenv("AUTH_BACKEND", "bind")
""",
gsqlite3="""
zone-cache-refresh-interval=0
-""")
+""",
+ )
_config_params = []
# - {soa} => value of _SOA
# - {prefix} value of _PREFIX
_zones = {
- 'example.org': """
+ "example.org": """
example.org. 3600 IN SOA {soa}
example.org. 3600 IN NS ns1.example.org.
example.org. 3600 IN NS ns2.example.org.
}
_zone_keys = {
- 'example.org': """
+ "example.org": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
""",
}
- _auth_cmd = [os.environ['PDNS']]
- if sys.platform != 'darwin':
- _auth_cmd = ['authbind'] + _auth_cmd
+ _auth_cmd = [os.environ["PDNS"]]
+ if sys.platform != "darwin":
+ _auth_cmd = ["authbind"] + _auth_cmd
_auth_env = {}
_auths = {}
- _PREFIX = os.environ['PREFIX']
- _PDNS_MODULE_DIR = os.environ['PDNS_MODULE_DIR']
+ _PREFIX = os.environ["PREFIX"]
+ _PDNS_MODULE_DIR = os.environ["PDNS_MODULE_DIR"]
@classmethod
def maybeAddVariant(cls, zone):
- if cls._backend_variants and cls._backend == 'lmdb':
+ if cls._backend_variants and cls._backend == "lmdb":
return zone + "..variant"
else:
return zone
@classmethod
def generateAuthZone(cls, confdir, zonename, zonecontent):
- with open(os.path.join(confdir, '%s.zone' % zonename), 'w') as zonefile:
+ with open(os.path.join(confdir, "%s.zone" % zonename), "w") as zonefile:
zonefile.write(zonecontent.format(prefix=cls._PREFIX, soa=cls._SOA))
@classmethod
def generateAuthNamedConf(cls, confdir, zones):
- with open(os.path.join(confdir, 'named.conf'), 'w') as namedconf:
- namedconf.write("""
+ with open(os.path.join(confdir, "named.conf"), "w") as namedconf:
+ namedconf.write(
+ """
options {
directory "%s";
-};""" % confdir)
+};"""
+ % confdir
+ )
for zonename in zones:
- zone = '.' if zonename == 'ROOT' else zonename
+ zone = "." if zonename == "ROOT" else zonename
- namedconf.write("""
+ namedconf.write(
+ """
zone "%s" {
type primary;
file "%s.zone";
- };""" % (zone, zonename))
+ };"""
+ % (zone, zonename)
+ )
@classmethod
def generateAuthConfig(cls, confdir):
- bind_dnssec_db = os.path.join(confdir, 'bind-dnssec.sqlite3')
+ bind_dnssec_db = os.path.join(confdir, "bind-dnssec.sqlite3")
params = tuple([getattr(cls, param) for param in cls._config_params])
- with open(os.path.join(confdir, 'pdns.conf'), 'w') as pdnsconf:
- args = dict(backend=cls._backend,
- confdir=confdir,
- prefix=cls._PREFIX,
- bind_dnssec_db=bind_dnssec_db,
- PDNS_MODULE_DIR=cls._PDNS_MODULE_DIR
- )
+ with open(os.path.join(confdir, "pdns.conf"), "w") as pdnsconf:
+ args = dict(
+ backend=cls._backend,
+ confdir=confdir,
+ prefix=cls._PREFIX,
+ bind_dnssec_db=bind_dnssec_db,
+ PDNS_MODULE_DIR=cls._PDNS_MODULE_DIR,
+ )
pdnsconf.write((cls._config_template_default + cls._backend_configs[cls._backend]).format(**args))
pdnsconf.write(cls._config_template.format(**args) % params)
- if cls._backend == 'gsqlite3':
+ if cls._backend == "gsqlite3":
os.system("sqlite3 ./configs/auth/powerdns.sqlite < ../modules/gsqlite3backend/schema.sqlite3.sql")
- if cls._backend == 'lmdb':
+ if cls._backend == "lmdb":
os.system("rm -f pdns.lmdb*")
- if cls._backend == 'bind':
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'create-bind-db',
- bind_dnssec_db]
+ if cls._backend == "bind":
+ pdnsutilCmd = [os.environ["PDNSUTIL"], "--config-dir=%s" % confdir, "create-bind-db", bind_dnssec_db]
- print(' '.join(pdnsutilCmd))
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
@classmethod
def secureZone(cls, confdir, zonename, key=None):
- zone = '.' if zonename == 'ROOT' else zonename
+ zone = "." if zonename == "ROOT" else zonename
if not key:
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'secure-zone',
- cls.maybeAddVariant(zone)]
+ pdnsutilCmd = [
+ os.environ["PDNSUTIL"],
+ "--config-dir=%s" % confdir,
+ "secure-zone",
+ cls.maybeAddVariant(zone),
+ ]
else:
- keyfile = os.path.join(confdir, 'dnssec.key')
- with open(keyfile, 'w') as fdKeyfile:
+ keyfile = os.path.join(confdir, "dnssec.key")
+ with open(keyfile, "w") as fdKeyfile:
fdKeyfile.write(key)
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'import-zone-key',
- cls.maybeAddVariant(zone),
- keyfile,
- 'active',
- 'ksk']
-
- print(' '.join(pdnsutilCmd))
+ pdnsutilCmd = [
+ os.environ["PDNSUTIL"],
+ "--config-dir=%s" % confdir,
+ "import-zone-key",
+ cls.maybeAddVariant(zone),
+ keyfile,
+ "active",
+ "ksk",
+ ]
+
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
@classmethod
def generateAllAuthConfig(cls, confdir):
cls.generateAuthConfig(confdir)
- if cls._backend == 'bind':
+ if cls._backend == "bind":
cls.generateAuthNamedConf(confdir, cls._zones.keys())
for zonename, zonecontent in cls._zones.items():
- cls.generateAuthZone(confdir,
- zonename,
- zonecontent)
+ cls.generateAuthZone(confdir, zonename, zonecontent)
if cls._zone_keys.get(zonename, None):
cls.secureZone(confdir, zonename, cls._zone_keys.get(zonename))
- elif cls._backend == 'lmdb':
+ elif cls._backend == "lmdb":
for zonename, zonecontent in cls._zones.items():
- cls.generateAuthZone(confdir,
- zonename,
- zonecontent)
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'load-zone',
- cls.maybeAddVariant(zonename),
- os.path.join(confdir, '%s.zone' % zonename)]
-
- print(' '.join(pdnsutilCmd))
+ cls.generateAuthZone(confdir, zonename, zonecontent)
+ pdnsutilCmd = [
+ os.environ["PDNSUTIL"],
+ "--config-dir=%s" % confdir,
+ "load-zone",
+ cls.maybeAddVariant(zonename),
+ os.path.join(confdir, "%s.zone" % zonename),
+ ]
+
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
if cls._zone_keys.get(zonename, None):
cls.secureZone(confdir, zonename, cls._zone_keys.get(zonename))
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'view-add-zone',
- 'one-view',
- cls.maybeAddVariant(zonename)]
- print(' '.join(pdnsutilCmd))
+ pdnsutilCmd = [
+ os.environ["PDNSUTIL"],
+ "--config-dir=%s" % confdir,
+ "view-add-zone",
+ "one-view",
+ cls.maybeAddVariant(zonename),
+ ]
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
-
- for net in ['0.0.0.0/0', '::/0']:
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'set-network',
- net,
- 'one-view']
- print(' '.join(pdnsutilCmd))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
+
+ for net in ["0.0.0.0/0", "::/0"]:
+ pdnsutilCmd = [os.environ["PDNSUTIL"], "--config-dir=%s" % confdir, "set-network", net, "one-view"]
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
- elif cls._backend == 'gsqlite3':
+ elif cls._backend == "gsqlite3":
# this is not a supported config from the user, but some of the test_*.py files use gsqlite3
return
else:
raise RuntimeError("unknown backend " + cls._backend + " specified")
-
@classmethod
def waitForTCPSocket(cls, ipaddress, port):
for try_number in range(0, 100):
return
except Exception as err:
if err.errno != errno.ECONNREFUSED:
- print(f'Error occurred: {try_number} {err}', file=sys.stderr)
+ print(f"Error occurred: {try_number} {err}", file=sys.stderr)
time.sleep(0.1)
@classmethod
print("Launching pdns_server..")
authcmd = list(cls._auth_cmd)
- authcmd.append('--config-dir=%s' % confdir)
- authcmd.append('--local-address=%s' % ipaddress)
- authcmd.append('--local-port=%s' % cls._authPort)
- authcmd.append('--loglevel=9')
- print(' '.join(authcmd))
- logFile = os.path.join(confdir, 'pdns.log')
- with open(logFile, 'w') as fdLog:
- cls._auths[ipaddress] = subprocess.Popen(authcmd, close_fds=True,
- stdout=fdLog, stderr=fdLog,
- env=cls._auth_env)
+ authcmd.append("--config-dir=%s" % confdir)
+ authcmd.append("--local-address=%s" % ipaddress)
+ authcmd.append("--local-port=%s" % cls._authPort)
+ authcmd.append("--loglevel=9")
+ print(" ".join(authcmd))
+ logFile = os.path.join(confdir, "pdns.log")
+ with open(logFile, "w") as fdLog:
+ cls._auths[ipaddress] = subprocess.Popen(
+ authcmd, close_fds=True, stdout=fdLog, stderr=fdLog, env=cls._auth_env
+ )
cls.waitForTCPSocket(ipaddress, cls._authPort)
if cls._auths[ipaddress].poll() is not None:
print(f"\n*** startAuth log for {logFile} ***")
- with open(logFile, 'r') as fdLog:
+ with open(logFile, "r") as fdLog:
print(fdLog.read())
print(f"*** End startAuth log for {logFile} ***")
- raise AssertionError('%s failed (%d)' % (authcmd, cls._auths[ipaddress].returncode))
+ raise AssertionError("%s failed (%d)" % (authcmd, cls._auths[ipaddress].returncode))
@classmethod
def setUpSockets(cls):
- print("Setting up UDP socket..")
- cls._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- cls._sock.settimeout(2.0)
- cls._sock.connect((cls._PREFIX + ".1", cls._authPort))
+ print("Setting up UDP socket..")
+ cls._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ cls._sock.settimeout(2.0)
+ cls._sock.connect((cls._PREFIX + ".1", cls._authPort))
@classmethod
def startResponders(cls):
cls.startResponders()
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
cls.createConfigDir(confdir)
cls.generateAllAuthConfig(confdir)
missingEdnsFlags = [ednsflag for ednsflag in ednsflags if ednsflag not in msgEdnsFlags]
if len(missingFlags) or len(missingEdnsFlags) or len(msgFlags) > len(flags):
- raise AssertionError("Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s" %
- (' '.join(flags), ' '.join(ednsflags),
- ' '.join(msgFlags), ' '.join(msgEdnsFlags),
- msg.question[0]))
+ raise AssertionError(
+ "Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s"
+ % (" ".join(flags), " ".join(ednsflags), " ".join(msgFlags), " ".join(msgEdnsFlags), msg.question[0])
+ )
def assertMessageIsAuthenticated(self, msg):
"""Asserts that the message has the AD bit set
raise TypeError("msg is not a dns.message.Message")
msgFlags = dns.flags.to_text(msg.flags)
- self.assertIn('AD', msgFlags, "No AD flag found in the message for %s" % msg.question[0].name)
+ self.assertIn("AD", msgFlags, "No AD flag found in the message for %s" % msg.question[0].name)
def assertRRsetInAnswer(self, msg, rrset):
"""Asserts the rrset (without comparing TTL) exists in the
@param msg: the dns.message.Message to check
@param rrset: a dns.rrset.RRset object"""
- ret = ''
+ ret = ""
if not isinstance(msg, dns.message.Message):
raise TypeError("msg is not a dns.message.Message")
@param msg: the dns.message.Message to check
@param rrset: a dns.rrset.RRset object"""
- ret = ''
+ ret = ""
if not isinstance(msg, dns.message.Message):
raise TypeError("msg is not a dns.message.Message")
found = True
if not found:
- raise AssertionError("RRset not found in answer\n%s" %
- "\n".join(([ans.to_text() for ans in msg.answer])))
+ raise AssertionError("RRset not found in answer\n%s" % "\n".join(([ans.to_text() for ans in msg.answer])))
def assertNoneRRsetInAnswer(self, msg, rrsets):
"""Asserts that none of the supplied rrsets exist (without comparing TTL)
found = True
if found:
- raise AssertionError("RRset incorrectly found in answer\n%s" %
- "\n".join(([ans.to_text() for ans in msg.answer])))
+ raise AssertionError(
+ "RRset incorrectly found in answer\n%s" % "\n".join(([ans.to_text() for ans in msg.answer]))
+ )
def assertMatchingRRSIGInAnswer(self, msg, coveredRRset, keys=None):
"""Looks for coveredRRset in the answer section and if there is an RRSIG RRset
msgRRsigRRSet = None
msgRRSet = None
- ret = ''
+ ret = ""
for ans in msg.answer:
ret += ans.to_text() + "\n"
raise AssertionError("RRset for '%s' not found in answer" % msg.question[0].to_text())
if not msgRRsigRRSet:
- raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg.question[0].to_text(), ret))
+ raise AssertionError(
+ "No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg.question[0].to_text(), ret)
+ )
if keys:
try:
raise AssertionError("RRSIG found in answers for:\n%s" % ret)
def assertAnswerEmpty(self, msg):
- self.assertEqual(len(msg.answer), 0, "Data found in the answer section for %s:\n%s" % (msg.question[0].to_text(), '\n'.join([i.to_text() for i in msg.answer])))
+ self.assertEqual(
+ len(msg.answer),
+ 0,
+ "Data found in the answer section for %s:\n%s"
+ % (msg.question[0].to_text(), "\n".join([i.to_text() for i in msg.answer])),
+ )
def assertAnswerNotEmpty(self, msg):
self.assertGreater(len(msg.answer), 0, "Answer is empty")
msgRcode = msg.rcode()
wantedRcode = rcode
- raise AssertionError("Rcode for %s is %s, expected %s." % (msg.question[0].to_text(), msgRcode, wantedRcode))
+ raise AssertionError(
+ "Rcode for %s is %s, expected %s." % (msg.question[0].to_text(), msgRcode, wantedRcode)
+ )
def assertAuthorityHasSOA(self, msg):
if not isinstance(msg, dns.message.Message):
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-""" Class to implement draft-ietf-dnsop-edns-client-subnet (previously known as
+"""Class to implement draft-ietf-dnsop-edns-client-subnet (previously known as
draft-vandergaast-edns-client-subnet.
The contained class supports both IPv4 and IPv6 addresses.
Requirements:
dnspython (http://www.dnspython.org/)
"""
+
from __future__ import print_function
from __future__ import division
n = socket.inet_pton(family, ip)
if family == socket.AF_INET6:
f = FAMILY_IPV6
- hi, lo = struct.unpack('!QQ', n)
+ hi, lo = struct.unpack("!QQ", n)
ip = hi << 64 | lo
elif family == socket.AF_INET:
f = FAMILY_IPV4
- ip = struct.unpack('!L', n)[0]
+ ip = struct.unpack("!L", n)[0]
except Exception:
pass
ip = self.ip >> bits - self.mask
- if (self.mask % 8 != 0):
+ if self.mask % 8 != 0:
ip = ip << 8 - (self.mask % 8)
return ip
def is_draft(self):
- """" Determines whether this instance is using the draft option code """
+ """ " Determines whether this instance is using the draft option code"""
return self.option == DRAFT_OPTION_CODE
def to_wire(self, file=None):
mask_bits = self.mask
if mask_bits % 8 != 0:
- mask_bits += 8 - (self.mask % 8)
+ mask_bits += 8 - (self.mask % 8)
if self.family == FAMILY_IPV4:
test = struct.pack("!L", ip)
elif self.family == FAMILY_IPV6:
- test = struct.pack("!QQ", ip >> 64, ip & (2 ** 64 - 1))
- test = test[-(mask_bits // 8):]
+ test = struct.pack("!QQ", ip >> 64, ip & (2**64 - 1))
+ test = test[-(mask_bits // 8) :]
format = "!HBB%ds" % (mask_bits // 8)
data = struct.pack(format, self.family, self.mask, self.scope, test)
An instance of ClientSubnetOption based on the ENDS packet
"""
- data = wire[current:current + olen]
+ data = wire[current : current + olen]
(family, mask, scope) = struct.unpack("!HBB", data[:4])
c_mask = mask
ip = struct.unpack_from("!%ds" % (c_mask // 8), data, 4)[0]
- if (family == FAMILY_IPV4):
- ip = ip + b'\0' * ((32 - c_mask) // 8)
+ if family == FAMILY_IPV4:
+ ip = ip + b"\0" * ((32 - c_mask) // 8)
ip = socket.inet_ntop(socket.AF_INET, ip)
- elif (family == FAMILY_IPV6):
- ip = ip + b'\0' * ((128 - c_mask) // 8)
+ elif family == FAMILY_IPV6:
+ ip = ip + b"\0" * ((128 - c_mask) // 8)
ip = socket.inet_ntop(socket.AF_INET6, ip)
else:
raise Exception("Returned a family other then IPv4 or IPv6")
# needed in 2.0.0..
@classmethod
def from_wire_parser(cls, otype, parser):
- family, src, scope = parser.get_struct('!HBB')
+ family, src, scope = parser.get_struct("!HBB")
addrlen = int(math.ceil(src / 8.0))
prefix = parser.get_bytes(addrlen)
if family == 1:
pad = 4 - addrlen
- addr = dns.ipv4.inet_ntoa(prefix + b'\x00' * pad)
+ addr = dns.ipv4.inet_ntoa(prefix + b"\x00" * pad)
elif family == 2:
pad = 16 - addrlen
- addr = dns.ipv6.inet_ntoa(prefix + b'\x00' * pad)
+ addr = dns.ipv6.inet_ntoa(prefix + b"\x00" * pad)
else:
- raise ValueError('unsupported family')
+ raise ValueError("unsupported family")
return cls(addr, src, scope, otype)
def __repr__(self):
if self.family == FAMILY_IPV4:
- ip = socket.inet_ntop(socket.AF_INET, struct.pack('!L', self.ip))
+ ip = socket.inet_ntop(socket.AF_INET, struct.pack("!L", self.ip))
elif self.family == FAMILY_IPV6:
- ip = socket.inet_ntop(socket.AF_INET6,
- struct.pack('!QQ',
- self.ip >> 64,
- self.ip & (2 ** 64 - 1)))
-
- return "%s(%s, %s, %s)" % (
- self.__class__.__name__,
- ip,
- self.mask,
- self.scope
- )
+ ip = socket.inet_ntop(socket.AF_INET6, struct.pack("!QQ", self.ip >> 64, self.ip & (2**64 - 1)))
+
+ return "%s(%s, %s, %s)" % (self.__class__.__name__, ip, self.mask, self.scope)
def to_text(self):
return self.__repr__()
print("Found ClientSubnetOption...", end=None, file=sys.stderr)
if not cso.family == options.family:
error = True
- print("\nFailed: returned family (%d) is different from the passed family (%d)" % (options.family, cso.family), file=sys.stderr)
+ print(
+ "\nFailed: returned family (%d) is different from the passed family (%d)"
+ % (options.family, cso.family),
+ file=sys.stderr,
+ )
if not cso.calculate_ip() == options.calculate_ip():
error = True
- print("\nFailed: returned ip (%s) is different from the passed ip (%s)." % (options.calculate_ip(), cso.calculate_ip()), file=sys.stderr)
+ print(
+ "\nFailed: returned ip (%s) is different from the passed ip (%s)."
+ % (options.calculate_ip(), cso.calculate_ip()),
+ file=sys.stderr,
+ )
if not options.mask == cso.mask:
error = True
- print("\nFailed: returned mask bits (%d) is different from the passed mask bits (%d)" % (options.mask, cso.mask), file=sys.stderr)
+ print(
+ "\nFailed: returned mask bits (%d) is different from the passed mask bits (%d)"
+ % (options.mask, cso.mask),
+ file=sys.stderr,
+ )
if not options.scope != 0:
print("\nWarning: scope indicates edns-clientsubnet data is not used", file=sys.stderr)
if options.is_draft():
else:
print("Failed: No ClientSubnetOption returned", file=sys.stderr)
- parser = argparse.ArgumentParser(description='draft-vandergaast-edns-client-subnet-01 tester')
- parser.add_argument('nameserver', help='The nameserver to test')
- parser.add_argument('rr', help='DNS record that should return an EDNS enabled response')
- parser.add_argument('-s', '--subnet', help='Specifies an IP to pass as the client subnet.', default='192.0.2.0')
- parser.add_argument('-m', '--mask', type=int, help='CIDR mask to use for subnet')
- parser.add_argument('--timeout', type=int, help='Set the timeout for query to TIMEOUT seconds, default=10', default=10)
- parser.add_argument('-t', '--type', help='DNS query type, default=A', default='A')
+ parser = argparse.ArgumentParser(description="draft-vandergaast-edns-client-subnet-01 tester")
+ parser.add_argument("nameserver", help="The nameserver to test")
+ parser.add_argument("rr", help="DNS record that should return an EDNS enabled response")
+ parser.add_argument("-s", "--subnet", help="Specifies an IP to pass as the client subnet.", default="192.0.2.0")
+ parser.add_argument("-m", "--mask", type=int, help="CIDR mask to use for subnet")
+ parser.add_argument(
+ "--timeout", type=int, help="Set the timeout for query to TIMEOUT seconds, default=10", default=10
+ )
+ parser.add_argument("-t", "--type", help="DNS query type, default=A", default="A")
args = parser.parse_args()
if not args.mask:
- if ':' in args.subnet:
+ if ":" in args.subnet:
args.mask = 48
else:
args.mask = 24
edns-subnet-processing=yes
"""
- _config_params = ['_PREFIX']
+ _config_params = ["_PREFIX"]
_zones = {
- 'example.org': """
+ "example.org": """
example.org. 3600 IN SOA {soa}
example.org. 3600 IN NS ns1.example.org.
example.org. 3600 IN NS ns2.example.org.
def startResponders(cls):
global aliasUDPReactorRunning
- address = cls._PREFIX + '.1'
+ address = cls._PREFIX + ".1"
port = 5301
if not aliasUDPReactorRunning:
aliasUDPReactorRunning = True
if not reactor.running:
- cls._ALIASResponder = threading.Thread(name='ALIAS Responder',
- target=reactor.run,
- args=(False,))
+ cls._ALIASResponder = threading.Thread(name="ALIAS Responder", target=reactor.run, args=(False,))
cls._ALIASResponder.setDaemon(True)
cls._ALIASResponder.start()
def testNoError(self):
- expected_a = [dns.rrset.from_text('noerror.example.org.',
- 0, dns.rdataclass.IN, 'A',
- '192.0.2.1')]
- expected_aaaa = [dns.rrset.from_text('noerror.example.org.',
- 0, dns.rdataclass.IN, 'AAAA',
- '2001:DB8::1')]
-
- query = dns.message.make_query('noerror.example.org', 'A')
+ expected_a = [dns.rrset.from_text("noerror.example.org.", 0, dns.rdataclass.IN, "A", "192.0.2.1")]
+ expected_aaaa = [dns.rrset.from_text("noerror.example.org.", 0, dns.rdataclass.IN, "AAAA", "2001:DB8::1")]
+
+ query = dns.message.make_query("noerror.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_a)
self.assertEqual(len(res.options), 0) # this checks that we don't invent ECS on non-ECS queries
- query = dns.message.make_query('noerror.example.org', 'AAAA')
+ query = dns.message.make_query("noerror.example.org", "AAAA")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_aaaa)
- query = dns.message.make_query('noerror.example.org', 'ANY')
+ query = dns.message.make_query("noerror.example.org", "ANY")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_a)
self.assertAnyRRsetInAnswer(res, expected_aaaa)
# NODATA
- query = dns.message.make_query('noerror.example.org', 'MX')
+ query = dns.message.make_query("noerror.example.org", "MX")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(len(res.answer), 0)
def testNxDomain(self):
- query = dns.message.make_query('nxd.example.org', 'A')
+ query = dns.message.make_query("nxd.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
- query = dns.message.make_query('nxd.example.org', 'AAAA')
+ query = dns.message.make_query("nxd.example.org", "AAAA")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
# TODO this should actually return SOA + NS?
- query = dns.message.make_query('nxd.example.org', 'ANY')
+ query = dns.message.make_query("nxd.example.org", "ANY")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
def testServFail(self):
- query = dns.message.make_query('servfail.example.org', 'A')
+ query = dns.message.make_query("servfail.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
- query = dns.message.make_query('servfail.example.org', 'AAAA')
+ query = dns.message.make_query("servfail.example.org", "AAAA")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
# TODO this should actually return SOA + NS?
- query = dns.message.make_query('servfail.example.org', 'ANY')
+ query = dns.message.make_query("servfail.example.org", "ANY")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
def testNoErrorTCP(self):
- expected_a = [dns.rrset.from_text('noerror.example.org.',
- 0, dns.rdataclass.IN, 'A',
- '192.0.2.1')]
- expected_aaaa = [dns.rrset.from_text('noerror.example.org.',
- 0, dns.rdataclass.IN, 'AAAA',
- '2001:DB8::1')]
-
- query = dns.message.make_query('noerror.example.org', 'A')
+ expected_a = [dns.rrset.from_text("noerror.example.org.", 0, dns.rdataclass.IN, "A", "192.0.2.1")]
+ expected_aaaa = [dns.rrset.from_text("noerror.example.org.", 0, dns.rdataclass.IN, "AAAA", "2001:DB8::1")]
+
+ query = dns.message.make_query("noerror.example.org", "A")
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_a)
- query = dns.message.make_query('noerror.example.org', 'AAAA')
+ query = dns.message.make_query("noerror.example.org", "AAAA")
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_aaaa)
- query = dns.message.make_query('noerror.example.org', 'ANY')
+ query = dns.message.make_query("noerror.example.org", "ANY")
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_a)
self.assertAnyRRsetInAnswer(res, expected_aaaa)
# NODATA
- query = dns.message.make_query('noerror.example.org', 'MX')
+ query = dns.message.make_query("noerror.example.org", "MX")
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(len(res.answer), 0)
def testNxDomainTCP(self):
- query = dns.message.make_query('nxd.example.org', 'A')
+ query = dns.message.make_query("nxd.example.org", "A")
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
- query = dns.message.make_query('nxd.example.org', 'AAAA')
+ query = dns.message.make_query("nxd.example.org", "AAAA")
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
- query = dns.message.make_query('nxd.example.org', 'ANY')
+ query = dns.message.make_query("nxd.example.org", "ANY")
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
def testServFailTCP(self):
- query = dns.message.make_query('servfail.example.org', 'A')
+ query = dns.message.make_query("servfail.example.org", "A")
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
- query = dns.message.make_query('servfail.example.org', 'AAAA')
+ query = dns.message.make_query("servfail.example.org", "AAAA")
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
- query = dns.message.make_query('servfail.example.org', 'ANY')
+ query = dns.message.make_query("servfail.example.org", "ANY")
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
def testECS(self):
- expected_a = [dns.rrset.from_text('subnet.example.org.',
- 0, dns.rdataclass.IN, 'A',
- '192.0.2.1')]
- expected_aaaa = [dns.rrset.from_text('subnet.example.org.',
- 0, dns.rdataclass.IN, 'AAAA',
- '2001:DB8::1')]
-
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.0', 24)
- ecso2 = clientsubnetoption.ClientSubnetOption('1.2.3.0', 24, 22)
- query = dns.message.make_query('subnet.example.org', 'A', use_edns=True, options=[ecso])
+ expected_a = [dns.rrset.from_text("subnet.example.org.", 0, dns.rdataclass.IN, "A", "192.0.2.1")]
+ expected_aaaa = [dns.rrset.from_text("subnet.example.org.", 0, dns.rdataclass.IN, "AAAA", "2001:DB8::1")]
+
+ ecso = clientsubnetoption.ClientSubnetOption("1.2.3.0", 24)
+ ecso2 = clientsubnetoption.ClientSubnetOption("1.2.3.0", 24, 22)
+ query = dns.message.make_query("subnet.example.org", "A", use_edns=True, options=[ecso])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_a)
self.assertEqual(res.options[0], ecso2)
- ecso = clientsubnetoption.ClientSubnetOption('2001:db8:db6:db5::', 64)
- ecso2 = clientsubnetoption.ClientSubnetOption('2001:db8:db6:db5::', 64, 48)
- query = dns.message.make_query('subnet.example.org', 'AAAA', use_edns=True, options=[ecso])
+ ecso = clientsubnetoption.ClientSubnetOption("2001:db8:db6:db5::", 64)
+ ecso2 = clientsubnetoption.ClientSubnetOption("2001:db8:db6:db5::", 64, 48)
+ query = dns.message.make_query("subnet.example.org", "AAAA", use_edns=True, options=[ecso])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_aaaa)
self.assertEqual(res.options[0], ecso2)
def testECSWrong(self):
- expected_a = [dns.rrset.from_text('subnetwrong.example.org.',
- 0, dns.rdataclass.IN, 'A',
- '192.0.2.1')]
- expected_aaaa = [dns.rrset.from_text('subnetwrong.example.org.',
- 0, dns.rdataclass.IN, 'AAAA',
- '2001:DB8::1')]
-
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.0', 24) # FIXME change all IPs to documentation space in this file
- ecso2 = clientsubnetoption.ClientSubnetOption('1.2.3.0', 24, 22)
- query = dns.message.make_query('subnetwrong.example.org', 'A', use_edns=True, options=[ecso])
+ expected_a = [dns.rrset.from_text("subnetwrong.example.org.", 0, dns.rdataclass.IN, "A", "192.0.2.1")]
+ expected_aaaa = [dns.rrset.from_text("subnetwrong.example.org.", 0, dns.rdataclass.IN, "AAAA", "2001:DB8::1")]
+
+ ecso = clientsubnetoption.ClientSubnetOption(
+ "1.2.3.0", 24
+ ) # FIXME change all IPs to documentation space in this file
+ ecso2 = clientsubnetoption.ClientSubnetOption("1.2.3.0", 24, 22)
+ query = dns.message.make_query("subnetwrong.example.org", "A", use_edns=True, options=[ecso])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_a)
self.assertEqual(res.options[0], ecso2)
- ecso = clientsubnetoption.ClientSubnetOption('2001:db8:db6:db5::', 64)
- ecso2 = clientsubnetoption.ClientSubnetOption('2001:db8:db6:db5::', 64, 48)
- query = dns.message.make_query('subnetwrong.example.org', 'AAAA', use_edns=True, options=[ecso])
+ ecso = clientsubnetoption.ClientSubnetOption("2001:db8:db6:db5::", 64)
+ ecso2 = clientsubnetoption.ClientSubnetOption("2001:db8:db6:db5::", 64, 48)
+ query = dns.message.make_query("subnetwrong.example.org", "AAAA", use_edns=True, options=[ecso])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_aaaa)
self.assertEqual(res.options[0], ecso2)
def testECSNone(self):
- expected_a = [dns.rrset.from_text('noerror.example.org.',
- 0, dns.rdataclass.IN, 'A',
- '192.0.2.1')]
- expected_aaaa = [dns.rrset.from_text('noerror.example.org.',
- 0, dns.rdataclass.IN, 'AAAA',
- '2001:DB8::1')]
-
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.0', 24)
- ecso2 = clientsubnetoption.ClientSubnetOption('1.2.3.0', 24, 0)
- query = dns.message.make_query('noerror.example.org', 'A', use_edns=True, options=[ecso])
+ expected_a = [dns.rrset.from_text("noerror.example.org.", 0, dns.rdataclass.IN, "A", "192.0.2.1")]
+ expected_aaaa = [dns.rrset.from_text("noerror.example.org.", 0, dns.rdataclass.IN, "AAAA", "2001:DB8::1")]
+
+ ecso = clientsubnetoption.ClientSubnetOption("1.2.3.0", 24)
+ ecso2 = clientsubnetoption.ClientSubnetOption("1.2.3.0", 24, 0)
+ query = dns.message.make_query("noerror.example.org", "A", use_edns=True, options=[ecso])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_a)
self.assertEqual(res.options[0], ecso2)
- ecso = clientsubnetoption.ClientSubnetOption('2001:db8:db6:db5::', 64)
- ecso2 = clientsubnetoption.ClientSubnetOption('2001:db8:db6:db5::', 64, 0)
- query = dns.message.make_query('noerror.example.org', 'AAAA', use_edns=True, options=[ecso])
+ ecso = clientsubnetoption.ClientSubnetOption("2001:db8:db6:db5::", 64)
+ ecso2 = clientsubnetoption.ClientSubnetOption("2001:db8:db6:db5::", 64, 0)
+ query = dns.message.make_query("noerror.example.org", "AAAA", use_edns=True, options=[ecso])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected_aaaa)
self.assertEqual(res.options[0], ecso2)
+
class AliasUDPResponder(DatagramProtocol):
def datagramReceived(self, datagram, address):
request = dns.message.from_wire(datagram)
name = question.name
name_text = name.to_text()
- if name_text in ('noerror.example.com.', 'subnet.example.com.', 'subnetwrong.example.com.'):
-
+ if name_text in ("noerror.example.com.", "subnet.example.com.", "subnetwrong.example.com."):
do_ecs = False
do_ecs_wrong = False
- if name_text == 'subnet.example.com.':
+ if name_text == "subnet.example.com.":
do_ecs = True
- elif name_text == 'subnetwrong.example.com.':
+ elif name_text == "subnetwrong.example.com.":
do_ecs = True
do_ecs_wrong = True
response.set_rcode(dns.rcode.NOERROR)
- if question.rdtype in [dns.rdatatype.A,
- dns.rdatatype.ANY]:
- response.answer.append(
- dns.rrset.from_text(
- name,
- 0, dns.rdataclass.IN, 'A', '192.0.2.1'))
-
- if question.rdtype in [dns.rdatatype.AAAA,
- dns.rdatatype.ANY]:
- response.answer.append(
- dns.rrset.from_text(name,
- 0, dns.rdataclass.IN, 'AAAA',
- '2001:DB8::1'))
+ if question.rdtype in [dns.rdatatype.A, dns.rdatatype.ANY]:
+ response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.1"))
+
+ if question.rdtype in [dns.rdatatype.AAAA, dns.rdatatype.ANY]:
+ response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, "AAAA", "2001:DB8::1"))
if do_ecs:
if request.options[0].family == clientsubnetoption.FAMILY_IPV4:
- ecso = clientsubnetoption.ClientSubnetOption('5.6.7.0' if do_ecs_wrong else '1.2.3.0', 24, 22)
+ ecso = clientsubnetoption.ClientSubnetOption("5.6.7.0" if do_ecs_wrong else "1.2.3.0", 24, 22)
else:
- ecso = clientsubnetoption.ClientSubnetOption('2600::' if do_ecs_wrong else '2001:db8:db6:db5::', 64, 48)
+ ecso = clientsubnetoption.ClientSubnetOption(
+ "2600::" if do_ecs_wrong else "2001:db8:db6:db5::", 64, 48
+ )
response.use_edns(edns=True, options=[ecso])
- if name_text == 'nxd.example.com.':
+ if name_text == "nxd.example.com.":
response.set_rcode(dns.rcode.NXDOMAIN)
response.authority.append(
dns.rrset.from_text(
- 'example.com.',
- 0, dns.rdataclass.IN, 'SOA', 'ns1.example.com. hostmaster.example.com. 2018062101 1 2 3 4'))
-
- if name_text == 'servfail.example.com.':
+ "example.com.",
+ 0,
+ dns.rdataclass.IN,
+ "SOA",
+ "ns1.example.com. hostmaster.example.com. 2018062101 1 2 3 4",
+ )
+ )
+
+ if name_text == "servfail.example.com.":
response.set_rcode(dns.rcode.SERVFAIL)
self.transport.write(response.to_wire(max_size=65535), address)
"""
_zones = {
- 'example.org': """
+ "example.org": """
example.org. 3600 IN SOA {soa}
example.org. 3600 IN NS ns1.example.org.
example.org. 3600 IN NS ns2.example.org.
cls.startResponders()
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
cls.createConfigDir(confdir)
cls.generateAllAuthConfig(confdir)
@classmethod
def setUpSockets(cls):
- print("Setting up UDP socket..")
- cls._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- cls._sock.settimeout(2.0)
- cls._sock.connect((cls._PREFIX + ".2", cls._authPort))
+ print("Setting up UDP socket..")
+ cls._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ cls._sock.settimeout(2.0)
+ cls._sock.connect((cls._PREFIX + ".2", cls._authPort))
def testA(self):
"""Test to see if we get a reply from 127.0.0.2 if auth is bound to ANY address"""
- query = dns.message.make_query('www.example.org', 'A')
+ query = dns.message.make_query("www.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, 0)
class TestAuthSignal(AuthTest):
- _backend = 'gsqlite3'
+ _backend = "gsqlite3"
_config_template_default = """
module-dir={PDNS_MODULE_DIR}
gsqlite3-pragma-foreign-keys=yes
gsqlite3-dnssec=yes
"""
+
@classmethod
def setUpClass(cls):
super().setUpClass()
- cls.signaling_domain = dns.name.from_text('_signal.ns1.example.net')
- cls.signaling_prefix = dns.name.from_text('_dsboot.cds-cdnskey.test').relativize(dns.name.root)
+ cls.signaling_domain = dns.name.from_text("_signal.ns1.example.net")
+ cls.signaling_prefix = dns.name.from_text("_dsboot.cds-cdnskey.test").relativize(dns.name.root)
cls.signaling_qname = cls.signaling_prefix.concatenate(cls.signaling_domain)
os.system("$PDNSUTIL --config-dir=configs/auth zone create _signal.ns1.example.net")
os.system("$PDNSUTIL --config-dir=configs/auth zone set-signaling _signal.ns1.example.net")
- query = dns.message.make_query('_signal.ns1.example.net', 'DNSKEY')
+ query = dns.message.make_query("_signal.ns1.example.net", "DNSKEY")
res = cls.sendUDPQuery(query)
cls.signaling_keytag = dns.dnssec.key_id(res.answer[0][0])
os.system("$PDNSUTIL --config-dir=configs/auth zone set-publish-cdnskey cds-cdnskey.test")
def _signalingQuery(self, rdtype):
- query = dns.message.make_query('cds-cdnskey.test', rdtype)
+ query = dns.message.make_query("cds-cdnskey.test", rdtype)
res1 = self.sendUDPQuery(query)
query = dns.message.make_query(self.signaling_qname, rdtype, use_edns=True, want_dnssec=True)
self.assertEqual(rrset1.to_rdataset(), rrset2.to_rdataset())
# ... and signed by the signaling zone
- rrsig_correct = any(rrset.rdtype == dns.rdatatype.RRSIG and rrset.covers == rdtype and rrset[0].key_tag == self.signaling_keytag for rrset in res2.answer)
+ rrsig_correct = any(
+ rrset.rdtype == dns.rdatatype.RRSIG and rrset.covers == rdtype and rrset[0].key_tag == self.signaling_keytag
+ for rrset in res2.answer
+ )
self.assertTrue(rrsig_correct, f"RRSIG({rdtype}) with proper keytag not found")
def testSignalingCDSQuery(self):
os.system("$PDNSUTIL --config-dir=configs/auth zone create no-signaling.test")
os.system("$PDNSUTIL --config-dir=configs/auth zone secure no-signaling.test")
- signaling_prefix = dns.name.from_text('_dsboot.no-signaling.test').relativize(dns.name.root)
+ signaling_prefix = dns.name.from_text("_dsboot.no-signaling.test").relativize(dns.name.root)
qname = signaling_prefix.concatenate(self.signaling_domain)
for rdtype, nsec3windows in {
- dns.rdatatype.CDS: ((0, b'\x00\x00\x00\x00\x00\x02\x00\x08'),), # RRSIG CDNSKEY
- dns.rdatatype.CDNSKEY: ((0, b'\x00\x00\x00\x00\x00\x02\x00\x10'),) # RRSIG CDS
+ dns.rdatatype.CDS: ((0, b"\x00\x00\x00\x00\x00\x02\x00\x08"),), # RRSIG CDNSKEY
+ dns.rdatatype.CDNSKEY: ((0, b"\x00\x00\x00\x00\x00\x02\x00\x10"),), # RRSIG CDS
}.items():
query = dns.message.make_query(qname, rdtype, use_edns=True, want_dnssec=True)
res = self.sendUDPQuery(query)
self.assertEqual(rrset.to_rdataset()[0].windows, nsec3windows)
def testSignalingQueryNXDOMAIN(self):
- signaling_prefix = dns.name.from_text('_dsboot.othername.test').relativize(dns.name.root)
+ signaling_prefix = dns.name.from_text("_dsboot.othername.test").relativize(dns.name.root)
qname = signaling_prefix.concatenate(self.signaling_domain)
for rdtype in (dns.rdatatype.CDS, dns.rdatatype.CDNSKEY):
query = dns.message.make_query(qname, rdtype, use_edns=True, want_dnssec=True)
from authtests import AuthTest
+
class TestCarbon(AuthTest):
- _carbonNamespace = 'NS'
- _carbonInstance = 'Instance'
+ _carbonNamespace = "NS"
+ _carbonInstance = "Instance"
_carbonServerName = "carbonname1"
_carbonInterval = 2
_carbonServer1Port = 8000
carbon-interval=%s
carbon-ourname=%s
carbon-server=127.0.0.1:%s,127.0.01:%s
- """ % (_carbonNamespace, _carbonInstance, _carbonInterval, _carbonServerName, _carbonServer1Port, _carbonServer2Port)
+ """ % (
+ _carbonNamespace,
+ _carbonInstance,
+ _carbonInterval,
+ _carbonServerName,
+ _carbonServer1Port,
+ _carbonServer2Port,
+ )
@classmethod
def CarbonResponder(cls, port):
while True:
(conn, _) = sock.accept()
conn.settimeout(2.0)
- lines = b''
+ lines = b""
while True:
data = conn.recv(4096)
if not data:
@classmethod
def startResponders(cls):
- cls._CarbonResponder1 = threading.Thread(name='Carbon Responder 1', target=cls.CarbonResponder, args=[cls._carbonServer1Port])
+ cls._CarbonResponder1 = threading.Thread(
+ name="Carbon Responder 1", target=cls.CarbonResponder, args=[cls._carbonServer1Port]
+ )
cls._CarbonResponder1.setDaemon(True)
cls._CarbonResponder1.start()
- cls._CarbonResponder2 = threading.Thread(name='Carbon Responder 2', target=cls.CarbonResponder, args=[cls._carbonServer2Port])
+ cls._CarbonResponder2 = threading.Thread(
+ name="Carbon Responder 2", target=cls.CarbonResponder, args=[cls._carbonServer2Port]
+ )
cls._CarbonResponder2.setDaemon(True)
cls._CarbonResponder2.start()
self.assertTrue(data1)
self.assertGreater(len(data1.splitlines()), 1)
- expectedStart = b"%s.%s.%s." % (self._carbonNamespace.encode('UTF8'), self._carbonServerName.encode('UTF-8'), self._carbonInstance.encode('UTF8'))
+ expectedStart = b"%s.%s.%s." % (
+ self._carbonNamespace.encode("UTF8"),
+ self._carbonServerName.encode("UTF-8"),
+ self._carbonInstance.encode("UTF8"),
+ )
for line in data1.splitlines():
self.assertTrue(line.startswith(expectedStart))
- parts = line.split(b' ')
+ parts = line.split(b" ")
self.assertEqual(len(parts), 3)
self.assertTrue(parts[1].isdigit())
self.assertTrue(parts[2].isdigit())
self.assertTrue(data2)
self.assertGreater(len(data2.splitlines()), 1)
- expectedStart = b"%s.%s.%s." % (self._carbonNamespace.encode('UTF8'), self._carbonServerName.encode('UTF-8'), self._carbonInstance.encode('UTF8'))
+ expectedStart = b"%s.%s.%s." % (
+ self._carbonNamespace.encode("UTF8"),
+ self._carbonServerName.encode("UTF-8"),
+ self._carbonInstance.encode("UTF8"),
+ )
for line in data2.splitlines():
self.assertTrue(line.startswith(expectedStart))
- parts = line.split(b' ')
+ parts = line.split(b" ")
self.assertEqual(len(parts), 3)
self.assertTrue(parts[1].isdigit())
self.assertTrue(parts[2].isdigit())
for key in self._carbonCounters:
value = self._carbonCounters[key]
self.assertGreaterEqual(value, 1)
-
from authtests import AuthTest
+
class TestEdnsCookies(AuthTest):
_config_template = """
launch={backend}
"""
_zones = {
- 'example.org': """
+ "example.org": """
example.org. 3600 IN SOA {soa}
example.org. 3600 IN NS ns1.example.org.
example.org. 3600 IN NS ns2.example.org.
def sendAndExpectNoCookie(self, msg, rcode):
res = self.sendUDPQuery(msg)
self.assertRcodeEqual(res, rcode)
- self.assertFalse(any([opt.otype == dns.edns.COOKIE for
- opt in res.options]))
+ self.assertFalse(any([opt.otype == dns.edns.COOKIE for opt in res.options]))
def getCookieFromServer(self):
- opts = [
- dns.edns.GenericOption(dns.edns.COOKIE,
- b'\x22\x11\x33\x44\x55\x66\x77\x88')]
- query = dns.message.make_query('www.example.org', 'A', options=opts)
+ opts = [dns.edns.GenericOption(dns.edns.COOKIE, b"\x22\x11\x33\x44\x55\x66\x77\x88")]
+ query = dns.message.make_query("www.example.org", "A", options=opts)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, 23) # BADCOOKIE
for opt in res.options:
return None
def testNoCookie(self):
- query = dns.message.make_query('www.example.org', 'A', use_edns=0)
+ query = dns.message.make_query("www.example.org", "A", use_edns=0)
self.sendAndExpectNoCookie(query, dns.rcode.NOERROR)
def testClientCookieTooShort(self):
- opts = [dns.edns.GenericOption(dns.edns.COOKIE, b'\x22')]
- query = dns.message.make_query('www.example.org', 'A', options=opts)
+ opts = [dns.edns.GenericOption(dns.edns.COOKIE, b"\x22")]
+ query = dns.message.make_query("www.example.org", "A", options=opts)
self.sendAndExpectNoCookie(query, dns.rcode.FORMERR)
- opts = [dns.edns.GenericOption(dns.edns.COOKIE,
- b'\x22\x11\x33\x44\x55\x66\x77')]
- query = dns.message.make_query('www.example.org', 'A', options=opts)
+ opts = [dns.edns.GenericOption(dns.edns.COOKIE, b"\x22\x11\x33\x44\x55\x66\x77")]
+ query = dns.message.make_query("www.example.org", "A", options=opts)
self.sendAndExpectNoCookie(query, dns.rcode.FORMERR)
def testServerCookieTooShort(self):
- opts = [
- dns.edns.GenericOption(dns.edns.COOKIE,
- b'\x22\x11\x33\x44\x55\x66\x77\x88\x99')]
- query = dns.message.make_query('www.example.org', 'A', options=opts)
+ opts = [dns.edns.GenericOption(dns.edns.COOKIE, b"\x22\x11\x33\x44\x55\x66\x77\x88\x99")]
+ query = dns.message.make_query("www.example.org", "A", options=opts)
self.sendAndExpectNoCookie(query, dns.rcode.FORMERR)
opts = [
- dns.edns.GenericOption(dns.edns.COOKIE,
- b'\x22\x11\x33\x44\x55\x66\x77\x88' +
- b'\x22\x11\x33\x44\x55\x66\x77')]
- query = dns.message.make_query('www.example.org', 'A', options=opts)
+ dns.edns.GenericOption(
+ dns.edns.COOKIE, b"\x22\x11\x33\x44\x55\x66\x77\x88" + b"\x22\x11\x33\x44\x55\x66\x77"
+ )
+ ]
+ query = dns.message.make_query("www.example.org", "A", options=opts)
self.sendAndExpectNoCookie(query, dns.rcode.FORMERR)
def testOnlyClientCookie(self):
- opts = [
- dns.edns.GenericOption(dns.edns.COOKIE,
- b'\x22\x11\x33\x44\x55\x66\x77\x88')]
- query = dns.message.make_query('www.example.org', 'A', options=opts)
+ opts = [dns.edns.GenericOption(dns.edns.COOKIE, b"\x22\x11\x33\x44\x55\x66\x77\x88")]
+ query = dns.message.make_query("www.example.org", "A", options=opts)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, 23) # BADCOOKIE
- self.assertTrue(any([opt.otype == dns.edns.COOKIE for
- opt in res.options]))
+ self.assertTrue(any([opt.otype == dns.edns.COOKIE for opt in res.options]))
def testOnlyClientCookieTCP(self):
- opts = [
- dns.edns.GenericOption(dns.edns.COOKIE,
- b'\x22\x11\x33\x44\x55\x66\x77\x88')]
- query = dns.message.make_query('www.example.org', 'A', options=opts)
+ opts = [dns.edns.GenericOption(dns.edns.COOKIE, b"\x22\x11\x33\x44\x55\x66\x77\x88")]
+ query = dns.message.make_query("www.example.org", "A", options=opts)
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertTrue(any(opt.otype == dns.edns.COOKIE for
- opt in res.options))
-
+ self.assertTrue(any(opt.otype == dns.edns.COOKIE for opt in res.options))
def testCorrectCookie(self):
opts = [self.getCookieFromServer()]
- query = dns.message.make_query('www.example.org', 'A', options=opts)
+ query = dns.message.make_query("www.example.org", "A", options=opts)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testBrokenCookie(self):
data = self.getCookieFromServer().to_wire()
# replace a byte in the client cookie
- data = data.replace(b'\x11', b'\x12')
+ data = data.replace(b"\x11", b"\x12")
opts = [dns.edns.GenericOption(dns.edns.COOKIE, data)]
- query = dns.message.make_query('www.example.org', 'A', options=opts)
+ query = dns.message.make_query("www.example.org", "A", options=opts)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, 23)
for opt in res.options:
return
self.fail()
+
class TestEdnsRandomCookies(TestEdnsCookies):
_config_template = """
launch={backend}
import socket
from authtests import AuthTest
+
class TestDirectDNSKEYSignature(AuthTest):
_config_template = """
launch={backend}
"""
_zones = {
- 'example.org': """
+ "example.org": """
example.org. 3600 IN SOA {soa}
example.org. 3600 IN NS ns1.example.org.
example.org. 3600 IN NS ns2.example.org.
def setUpClass(cls):
cls.setUpSockets()
cls.startResponders()
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
cls.createConfigDir(confdir)
cls.generateAllAuthConfig(confdir)
cls.startAuth(confdir, "0.0.0.0")
def testDNSKEYQuery(self):
"""Test to verify DNSKEY and RRSIG records are served correctly"""
- query = dns.message.make_query('example.org', 'DNSKEY', use_edns=True, want_dnssec=True)
+ query = dns.message.make_query("example.org", "DNSKEY", use_edns=True, want_dnssec=True)
res = self.sendUDPQuery(query)
# Ensure no error in response
self.assertTrue(dnskey_found, "DNSKEY record not found in the answer section")
# Validate RRSIG record for DNSKEY
- rrsig_found = any(rrset.rdtype == dns.rdatatype.RRSIG and rrset.covers == dns.rdatatype.DNSKEY and rrset[0].key_tag == 22273 for rrset in res.answer)
+ rrsig_found = any(
+ rrset.rdtype == dns.rdatatype.RRSIG and rrset.covers == dns.rdatatype.DNSKEY and rrset[0].key_tag == 22273
+ for rrset in res.answer
+ )
self.assertTrue(rrsig_found, "RRSIG for DNSKEY not found in the answer section")
def testDNSKEYQueryWithoutDNSSEC(self):
"""Test to ensure no RRSIG records are returned without the DNSSEC flag"""
- query = dns.message.make_query('example.org', 'DNSKEY', use_edns=True, want_dnssec=False)
+ query = dns.message.make_query("example.org", "DNSKEY", use_edns=True, want_dnssec=False)
res = self.sendUDPQuery(query)
# Ensure no error in response
class GSSTSIGBase(AuthTest):
- _backend = 'gsqlite3'
+ _backend = "gsqlite3"
_config_template_default = """
module-dir={PDNS_MODULE_DIR}
dnsupdate=yes
dnsupdate-require-tsig=no
"""
- _auth_env = {'KRB5_CONFIG' : './kerberos-client/krb5.conf',
- 'KRB5_KTNAME' : './kerberos-client/kt.keytab'
- }
+ _auth_env = {"KRB5_CONFIG": "./kerberos-client/krb5.conf", "KRB5_KTNAME": "./kerberos-client/kt.keytab"}
@classmethod
def secureZone(cls, confdir, zonename, key=None):
# all the other tests in that directory. Because of this, we
# need to perform an explicit create-zone, otherwise import-zone-key
# would fail.
- zone = '.' if zonename == 'ROOT' else zonename
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'create-zone',
- zone]
- print(' '.join(pdnsutilCmd))
+ zone = "." if zonename == "ROOT" else zonename
+ pdnsutilCmd = [os.environ["PDNSUTIL"], "--config-dir=%s" % confdir, "create-zone", zone]
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
super(GSSTSIGBase, cls).secureZone(confdir, zonename, key)
@classmethod
os.system("$PDNSUTIL --config-dir=configs/auth create-zone noacceptor.net")
os.system("$PDNSUTIL --config-dir=configs/auth create-zone wrongacceptor.net")
- os.system("$PDNSUTIL --config-dir=configs/auth replace-rrset example.net example.net SOA 3600 'ns1.example.net otto.example.net 2022010403 10800 3600 604800 3600'")
- os.system("$PDNSUTIL --config-dir=configs/auth replace-rrset noacceptor.net noacceptor.net SOA 3600 'ns1.noacceptor.net otto.example.net 2022010403 10800 3600 604800 3600'")
- os.system("$PDNSUTIL --config-dir=configs/auth replace-rrset wrongacceptor.net wrongacceptor.net SOA 3600 'ns1.wrongacceptor.net otto.example.net 2022010403 10800 3600 604800 3600'")
-
- os.system("$PDNSUTIL --config-dir=configs/auth set-meta example.net GSS-ACCEPTOR-PRINCIPAL DNS/ns1.example.net@EXAMPLE.COM")
- os.system("$PDNSUTIL --config-dir=configs/auth set-meta wrongacceptor.net GSS-ACCEPTOR-PRINCIPAL DNS/ns1.example.net@EXAMPLE.COM")
+ os.system(
+ "$PDNSUTIL --config-dir=configs/auth replace-rrset example.net example.net SOA 3600 'ns1.example.net otto.example.net 2022010403 10800 3600 604800 3600'"
+ )
+ os.system(
+ "$PDNSUTIL --config-dir=configs/auth replace-rrset noacceptor.net noacceptor.net SOA 3600 'ns1.noacceptor.net otto.example.net 2022010403 10800 3600 604800 3600'"
+ )
+ os.system(
+ "$PDNSUTIL --config-dir=configs/auth replace-rrset wrongacceptor.net wrongacceptor.net SOA 3600 'ns1.wrongacceptor.net otto.example.net 2022010403 10800 3600 604800 3600'"
+ )
+
+ os.system(
+ "$PDNSUTIL --config-dir=configs/auth set-meta example.net GSS-ACCEPTOR-PRINCIPAL DNS/ns1.example.net@EXAMPLE.COM"
+ )
+ os.system(
+ "$PDNSUTIL --config-dir=configs/auth set-meta wrongacceptor.net GSS-ACCEPTOR-PRINCIPAL DNS/ns1.example.net@EXAMPLE.COM"
+ )
os.system("$PDNSUTIL --config-dir=configs/auth set-meta example.net TSIG-ALLOW-DNSUPDATE testuser1@EXAMPLE.COM")
def kinit(self, user):
ret = os.system("$PDNSUTIL --config-dir=configs/auth list-zone %s | fgrep -q %s" % (zone, record))
self.assertNotEqual(ret, 0)
-class TestBasicGSSTSIG(GSSTSIGBase):
+class TestBasicGSSTSIG(GSSTSIGBase):
_config_template = """
launch=gsqlite3
gsqlite3-database=configs/auth/powerdns.sqlite
allow-dnsupdate-from=0.0.0.0/0
dnsupdate=yes
"""
+
def testAllowedUpdate(self):
- self.checkNotInDB('example.net', 'inserted1.example.net')
+ self.checkNotInDB("example.net", "inserted1.example.net")
self.kinit("testuser1")
self.nsupdate("add inserted1.example.net 10 A 1.2.3.1")
- self.checkInDB('example.net', '^inserted1.example.net.*10.*IN.*A.*1.2.3.1$')
+ self.checkInDB("example.net", "^inserted1.example.net.*10.*IN.*A.*1.2.3.1$")
def testDisallowedUpdate(self):
self.kinit("testuser2")
self.nsupdate("add inserted2.example.net 10 A 1.2.3.2", 2)
- self.checkNotInDB('example.net', 'inserted2.example.net')
+ self.checkNotInDB("example.net", "inserted2.example.net")
def testNoAcceptor(self):
self.kinit("testuser1")
self.nsupdate("add inserted3.noacceptor.net 10 A 1.2.3.3", 2)
- self.checkNotInDB('noacceptor.net', 'inserted3.noacceptor.net')
+ self.checkNotInDB("noacceptor.net", "inserted3.noacceptor.net")
def testWrongAcceptor(self):
self.kinit("testuser1")
self.nsupdate("add inserted4.wrongacceptor.net 10 A 1.2.3.4", 2)
- self.checkNotInDB('wrongacceptor.net', 'inserted4.wrongacceptor.net')
+ self.checkNotInDB("wrongacceptor.net", "inserted4.wrongacceptor.net")
-class TestLuaGSSTSIG(GSSTSIGBase):
+class TestLuaGSSTSIG(GSSTSIGBase):
_config_template = """
launch=gsqlite3
gsqlite3-database=configs/auth/powerdns.sqlite
dnsupdate=yes
lua-dnsupdate-policy-script=kerberos-client/update-policy.lua
"""
+
def testDisallowedByLuaUpdate(self):
self.kinit("testuser1")
self.nsupdate("add inserted10.example.net 10 A 1.2.3.10", 2)
- self.checkNotInDB('example.net', 'inserted10.example.net')
+ self.checkNotInDB("example.net", "inserted10.example.net")
def testAllowedByLuaUpdate(self):
self.kinit("testuser2")
self.nsupdate("add inserted11.example.net 10 A 1.2.3.11")
- self.checkInDB('example.net', '^inserted11.example.net.*10.*IN.*A.*1.2.3.11$')
-
+ self.checkInDB("example.net", "^inserted11.example.net.*10.*IN.*A.*1.2.3.11$")
def testNoAcceptor(self):
self.kinit("testuser1")
self.nsupdate("add inserted12.noacceptor.net 10 A 1.2.3.12", 2)
- self.checkNotInDB('noacceptor.net', 'inserted12.noacceptor.net')
+ self.checkNotInDB("noacceptor.net", "inserted12.noacceptor.net")
def testWrongAcceptor(self):
self.kinit("testuser1")
self.nsupdate("add inserted13.wrongacceptor.net 10 A 1.2.3.13", 2)
- self.checkNotInDB('wrongacceptor.net', 'inserted13.wrongacceptor.net')
+ self.checkNotInDB("wrongacceptor.net", "inserted13.wrongacceptor.net")
-class TestUnauthTSIG(GSSTSIGBase):
+class TestUnauthTSIG(GSSTSIGBase):
_config_template = """
launch=gsqlite3
gsqlite3-database=configs/auth/powerdns.sqlite
allow-dnsupdate-from=0.0.0.0/0
dnsupdate=yes
"""
+
def testNoAcceptor(self):
- self.checkNotInDB('noacceptor.net', 'inserted20.noacceptor.net')
+ self.checkNotInDB("noacceptor.net", "inserted20.noacceptor.net")
self.nsupdate("add inserted20.noacceptor.net 10 A 1.2.3.3", 0, True)
- self.checkInDB('noacceptor.net', 'inserted20.noacceptor.net')
+ self.checkInDB("noacceptor.net", "inserted20.noacceptor.net")
-class TestAuthTSIG(GSSTSIGBase):
+class TestAuthTSIG(GSSTSIGBase):
_config_template = """
launch=gsqlite3
gsqlite3-database=configs/auth/powerdns.sqlite
dnsupdate=yes
dnsupdate-require-tsig=yes
"""
+
def testNoAcceptor(self):
self.nsupdate("add inserted30.noacceptor.net 10 A 1.2.3.3", 2, True)
- self.checkNotInDB('noacceptor.net', 'inserted30.noacceptor.net')
+ self.checkNotInDB("noacceptor.net", "inserted30.noacceptor.net")
-class TestBasicRequiredGSSTSIG(GSSTSIGBase):
+class TestBasicRequiredGSSTSIG(GSSTSIGBase):
_config_template = """
launch=gsqlite3
gsqlite3-database=configs/auth/powerdns.sqlite
dnsupdate=yes
dnsupdate-require-tsig=yes
"""
+
def testAllowedUpdate(self):
- self.checkNotInDB('example.net', 'inserted40.example.net')
+ self.checkNotInDB("example.net", "inserted40.example.net")
self.kinit("testuser1")
self.nsupdate("add inserted40.example.net 10 A 1.2.3.1")
- self.checkInDB('example.net', '^inserted40.example.net.*10.*IN.*A.*1.2.3.1$')
+ self.checkInDB("example.net", "^inserted40.example.net.*10.*IN.*A.*1.2.3.1$")
def testDisallowedUpdate(self):
self.kinit("testuser2")
self.nsupdate("add inserted41.example.net 10 A 1.2.3.2", 2)
- self.checkNotInDB('example.net', 'inserted41.example.net')
+ self.checkNotInDB("example.net", "inserted41.example.net")
def testNoAcceptor(self):
self.kinit("testuser1")
self.nsupdate("add inserted42.noacceptor.net 10 A 1.2.3.3", 2)
- self.checkNotInDB('noacceptor.net', 'inserted42.noacceptor.net')
+ self.checkNotInDB("noacceptor.net", "inserted42.noacceptor.net")
def testWrongAcceptor(self):
self.kinit("testuser1")
self.nsupdate("add inserted43.wrongacceptor.net 10 A 1.2.3.4", 2)
- self.checkNotInDB('wrongacceptor.net', 'inserted43.wrongacceptor.net')
-
-
+ self.checkNotInDB("wrongacceptor.net", "inserted43.wrongacceptor.net")
from xfrserver.xfrserver import AXFRServer
zones = {
- 1: ["""
-$ORIGIN example.""","""
+ 1: [
+ """
+$ORIGIN example.""",
+ """
@ 86400 SOA foo bar 1 2 3 4 5
@ 4242 NS ns1.example.
@ 4242 NS ns2.example.
ns1.example. 4242 A 192.0.2.1
ns2.example. 4242 A 192.0.2.2
-"""],
- 2: ["""
-$ORIGIN example.""","""
+""",
+ ],
+ 2: [
+ """
+$ORIGIN example.""",
+ """
@ 86400 SOA foo bar 2 2 3 4 5
@ 4242 NS ns1.example.
@ 4242 NS ns2.example.
ns1.example. 4242 A 192.0.2.1
ns2.example. 4242 A 192.0.2.2
newrecord.example. 8484 A 192.0.2.42
-"""],
- 3: ["""
-$ORIGIN example.""","""
-@ 86400 SOA foo bar 3 2 3 4 5""","""
-@ 86400 SOA foo bar 2 2 3 4 5""","""
-@ 86400 SOA foo bar 3 2 3 4 5""","""
+""",
+ ],
+ 3: [
+ """
+$ORIGIN example.""",
+ """
+@ 86400 SOA foo bar 3 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 2 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 3 2 3 4 5""",
+ """
@ 4242 NS ns3.example.
-"""],
- 5: ["""
-$ORIGIN example.""","""
-@ 86400 SOA foo bar 5 2 3 4 5""","""
-@ 86400 SOA foo bar 3 2 3 4 5""","""
-@ 86400 SOA foo bar 4 2 3 4 5""","""
-@ 86400 SOA foo bar 4 2 3 4 5""","""
-@ 86400 SOA foo bar 5 2 3 4 5""","""
+""",
+ ],
+ 5: [
+ """
+$ORIGIN example.""",
+ """
+@ 86400 SOA foo bar 5 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 3 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 4 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 4 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 5 2 3 4 5""",
+ """
@ 4242 NS ns5.example.
-"""],
- 8: ["""
-$ORIGIN example.""","""
-@ 86400 SOA foo bar 8 2 3 4 5""","""
-@ 86400 SOA foo bar 5 2 3 4 5""","""
-@ 86400 SOA foo bar 6 2 3 4 5""","""
-@ 86400 SOA foo bar 6 2 3 4 5""","""
-@ 86400 SOA foo bar 7 2 3 4 5""","""
-@ 86400 SOA foo bar 7 2 3 4 5""","""
-@ 86400 SOA foo bar 8 2 3 4 5""","""
-"""]
-
-
+""",
+ ],
+ 8: [
+ """
+$ORIGIN example.""",
+ """
+@ 86400 SOA foo bar 8 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 5 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 6 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 6 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 7 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 7 2 3 4 5""",
+ """
+@ 86400 SOA foo bar 8 2 3 4 5""",
+ """
+""",
+ ],
}
xfrServerPort = 4244
xfrServer = AXFRServer(xfrServerPort, zones)
+
class TestIXFR(AuthTest):
- _backend = 'gsqlite3'
+ _backend = "gsqlite3"
_config_template = """
launch=gsqlite3
attempts = 0
while attempts < timeout:
- print('attempts=%s timeout=%s' % (attempts, timeout))
+ print("attempts=%s timeout=%s" % (attempts, timeout))
servedSerial = xfrServer.getServedSerial()
- print('servedSerial=%s' % servedSerial)
+ print("servedSerial=%s" % servedSerial)
if servedSerial > serial:
raise AssertionError("Expected serial %d, got %d" % (serial, servedSerial))
if servedSerial == serial:
attempts = attempts + 1
time.sleep(1)
- raise AssertionError("Waited %d seconds for the serial to be updated to %d but the last served serial is still %d" % (timeout, serial, servedSerial))
+ raise AssertionError(
+ "Waited %d seconds for the serial to be updated to %d but the last served serial is still %d"
+ % (timeout, serial, servedSerial)
+ )
def checkFullZone(self, serial, data=None):
global zones
zone = []
if not data:
data = zones[serial]
- for i in dns.zone.from_text('\n'.join(data), relativize=False).iterate_rdatasets():
+ for i in dns.zone.from_text("\n".join(data), relativize=False).iterate_rdatasets():
n, rds = i
- rrs=dns.rrset.RRset(n, rds.rdclass, rds.rdtype)
+ rrs = dns.rrset.RRset(n, rds.rdclass, rds.rdtype)
rrs.update(rds)
zone.append(rrs)
- expected =[[zone[0]], sorted(zone[1:], key=lambda rrset: (rrset.name, rrset.rdtype)), [zone[0]]] # AXFRs are SOA-wrapped
+ expected = [
+ [zone[0]],
+ sorted(zone[1:], key=lambda rrset: (rrset.name, rrset.rdtype)),
+ [zone[0]],
+ ] # AXFRs are SOA-wrapped
- query = dns.message.make_query('example.', 'AXFR')
+ query = dns.message.make_query("example.", "AXFR")
res = self.sendTCPQueryMultiResponse(query, count=len(expected))
answers = [r.answer for r in res]
answers[1].sort(key=lambda rrset: (rrset.name, rrset.rdtype))
soa1 = xfrServer._getSOAForSerial(fromserial)
soa2 = xfrServer._getSOAForSerial(toserial)
- newrecord = [r for r in xfrServer._getRecordsForSerial(toserial) if r.name==dns.name.from_text('newrecord.example.')]
- query = dns.message.make_query('example.', 'IXFR')
+ newrecord = [
+ r for r in xfrServer._getRecordsForSerial(toserial) if r.name == dns.name.from_text("newrecord.example.")
+ ]
+ query = dns.message.make_query("example.", "IXFR")
query.authority = [soa1]
expected = [[soa2], [soa1], [soa2], newrecord, [soa2]]
self.checkFullZone(2)
self.waitUntilCorrectSerialIsLoaded(3)
- self.checkFullZone(3, data=["""
-$ORIGIN example.""","""
+ self.checkFullZone(
+ 3,
+ data=[
+ """
+$ORIGIN example.""",
+ """
@ 86400 SOA foo bar 3 2 3 4 5
@ 4242 NS ns1.example.
@ 4242 NS ns2.example.
ns1.example. 4242 A 192.0.2.1
ns2.example. 4242 A 192.0.2.2
newrecord.example. 8484 A 192.0.2.42
-"""])
+""",
+ ],
+ )
self.waitUntilCorrectSerialIsLoaded(5)
- self.checkFullZone(5, data=["""
-$ORIGIN example.""","""
+ self.checkFullZone(
+ 5,
+ data=[
+ """
+$ORIGIN example.""",
+ """
@ 86400 SOA foo bar 5 2 3 4 5
@ 4242 NS ns1.example.
@ 4242 NS ns2.example.
ns1.example. 4242 A 192.0.2.1
ns2.example. 4242 A 192.0.2.2
newrecord.example. 8484 A 192.0.2.42
-"""])
-
+""",
+ ],
+ )
# _b_ because we expect post-XFR testing state
def test_b_UDP_SOA_existing(self):
- query = dns.message.make_query('example.', 'SOA')
+ query = dns.message.make_query("example.", "SOA")
expected = dns.message.make_response(query)
expected.answer.append(xfrServer._getSOAForSerial(5))
expected.flags |= dns.flags.AA
pos = pos + 1
def test_b_UDP_SOA_not_loaded(self):
- query = dns.message.make_query('example2.', 'SOA')
+ query = dns.message.make_query("example2.", "SOA")
expected = dns.message.make_response(query)
expected.set_rcode(dns.rcode.REFUSED)
self.assertEqual(expected, response)
def test_b_UDP_SOA_not_configured(self):
- query = dns.message.make_query('example3.', 'SOA')
+ query = dns.message.make_query("example3.", "SOA")
expected = dns.message.make_response(query)
expected.set_rcode(dns.rcode.REFUSED)
def test_d_XFR(self):
self.waitUntilCorrectSerialIsLoaded(8)
- self.checkFullZone(7, data=["""
-$ORIGIN example.""","""
+ self.checkFullZone(
+ 7,
+ data=[
+ """
+$ORIGIN example.""",
+ """
@ 86400 SOA foo bar 8 2 3 4 5
@ 4242 NS ns1.example.
@ 4242 NS ns2.example.
ns1.example. 4242 A 192.0.2.1
ns2.example. 4242 A 192.0.2.2
newrecord.example. 8484 A 192.0.2.42
-"""])
- ret = subprocess.check_output([os.environ['PDNSUTIL'],
- '--config-dir=configs/auth',
- 'list-zone', 'example'], stderr=subprocess.STDOUT)
- rets = ret.split(b'\n')
-
- self.assertEqual(1, sum(b'SOA' in l for l in rets))
+""",
+ ],
+ )
+ ret = subprocess.check_output(
+ [os.environ["PDNSUTIL"], "--config-dir=configs/auth", "list-zone", "example"], stderr=subprocess.STDOUT
+ )
+ rets = ret.split(b"\n")
+
+ self.assertEqual(1, sum(b"SOA" in l for l in rets))
webserver = None
+
class FakeHTTPServer(BaseHTTPRequestHandler):
def _set_headers(self, response_code=200):
self.send_response(response_code)
- self.send_header('Content-type', 'text/html')
+ self.send_header("Content-type", "text/html")
self.end_headers()
def do_GET(self):
- if self.path == '/404':
+ if self.path == "/404":
self._set_headers(404)
- self.wfile.write(bytes('this page does not exist', 'utf-8'))
+ self.wfile.write(bytes("this page does not exist", "utf-8"))
return
if self.path == "/check-headers":
if self.headers.get("my-header", "") != "myvalue":
self._set_headers(400)
- self.wfile.write(bytes('Wrong Header value!', 'utf-8'))
+ self.wfile.write(bytes("Wrong Header value!", "utf-8"))
return
self._set_headers()
- if self.path == '/ping.json':
- self.wfile.write(bytes('{"ping":"pong"}', 'utf-8'))
- if self.path == '/weight.txt':
- self.wfile.write(bytes('12', 'utf-8'))
+ if self.path == "/ping.json":
+ self.wfile.write(bytes('{"ping":"pong"}', "utf-8"))
+ if self.path == "/weight.txt":
+ self.wfile.write(bytes("12", "utf-8"))
else:
self.wfile.write(bytes("<html><body><h1>hi!</h1><h2>Programming in Lua !</h2></body></html>", "utf-8"))
def do_HEAD(self):
self._set_headers()
+
class BaseLuaTest(AuthTest):
_config_template = """
geoip-database-files=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb
"""
_zones = {
- 'example.org': """
+ "example.org": """
example.org. 3600 IN SOA {soa}
example.org. 3600 IN NS ns1.example.org.
example.org. 3600 IN NS ns2.example.org.
@classmethod
def startResponders(cls):
global webserver
- if webserver: return # it is already running
+ if webserver:
+ return # it is already running
- webserver = threading.Thread(name='HTTP Listener',
- target=cls.HTTPResponder,
- args=[8080]
- )
+ webserver = threading.Thread(name="HTTP Listener", target=cls.HTTPResponder, args=[8080])
webserver.setDaemon(True)
webserver.start()
@classmethod
def HTTPResponder(cls, port):
- server_address = ('', port)
+ server_address = ("", port)
httpd = HTTPServer(server_address, FakeHTTPServer)
httpd.serve_forever()
super(BaseLuaTest, cls).setUpClass()
- cls._web_rrsets = [dns.rrset.from_text('web1.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.101'.format(prefix=cls._PREFIX)),
- dns.rrset.from_text('web2.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.102'.format(prefix=cls._PREFIX)),
- dns.rrset.from_text('web3.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.103'.format(prefix=cls._PREFIX))
+ cls._web_rrsets = [
+ dns.rrset.from_text(
+ "web1.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.101".format(prefix=cls._PREFIX)
+ ),
+ dns.rrset.from_text(
+ "web2.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.102".format(prefix=cls._PREFIX)
+ ),
+ dns.rrset.from_text(
+ "web3.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.103".format(prefix=cls._PREFIX)
+ ),
]
-class TestLuaRecords(BaseLuaTest):
+class TestLuaRecords(BaseLuaTest):
def testPickRandom(self):
"""
Basic pickrandom() test with a set of A records
"""
- expected = [dns.rrset.from_text('rand.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.101'.format(prefix=self._PREFIX)),
- dns.rrset.from_text('rand.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.102'.format(prefix=self._PREFIX))]
- query = dns.message.make_query('rand.example.org', 'A')
+ expected = [
+ dns.rrset.from_text(
+ "rand.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.101".format(prefix=self._PREFIX)
+ ),
+ dns.rrset.from_text(
+ "rand.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.102".format(prefix=self._PREFIX)
+ ),
+ ]
+ query = dns.message.make_query("rand.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic pickrandom() test with a set of TXT records
"""
- expected = [dns.rrset.from_text('rand-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'bob'),
- dns.rrset.from_text('rand-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'alice')]
- query = dns.message.make_query('rand-txt.example.org', 'TXT')
+ expected = [
+ dns.rrset.from_text("rand-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "bob"),
+ dns.rrset.from_text("rand-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "alice"),
+ ]
+ query = dns.message.make_query("rand-txt.example.org", "TXT")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Test a bogus AAAA pickrandom() record with a set of v4 addr
"""
- query = dns.message.make_query('v6-bogus.rand.example.org', 'AAAA')
+ query = dns.message.make_query("v6-bogus.rand.example.org", "AAAA")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
"""
Test pickrandom() AAAA record
"""
- expected = [dns.rrset.from_text('v6.rand.example.org.', 0, dns.rdataclass.IN, 'AAAA',
- '2001:db8:a0b:12f0::1'),
- dns.rrset.from_text('v6.rand.example.org.', 0, dns.rdataclass.IN, 'AAAA',
- 'fe80::2a1:9bff:fe9b:f268')]
- query = dns.message.make_query('v6.rand.example.org', 'AAAA')
+ expected = [
+ dns.rrset.from_text("v6.rand.example.org.", 0, dns.rdataclass.IN, "AAAA", "2001:db8:a0b:12f0::1"),
+ dns.rrset.from_text("v6.rand.example.org.", 0, dns.rdataclass.IN, "AAAA", "fe80::2a1:9bff:fe9b:f268"),
+ ]
+ query = dns.message.make_query("v6.rand.example.org", "AAAA")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic pickrandom() test with an empty set
"""
- query = dns.message.make_query('empty.rand.example.org', 'A')
+ query = dns.message.make_query("empty.rand.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
"""
Test the selfweighted() function with a set of A records
"""
- expected = [dns.rrset.from_text('selfweighted.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.101'.format(prefix=self._PREFIX)),
- dns.rrset.from_text('selfweighted.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.102'.format(prefix=self._PREFIX))]
- query = dns.message.make_query('selfweighted.example.org', 'A')
+ expected = [
+ dns.rrset.from_text(
+ "selfweighted.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.101".format(prefix=self._PREFIX)
+ ),
+ dns.rrset.from_text(
+ "selfweighted.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.102".format(prefix=self._PREFIX)
+ ),
+ ]
+ query = dns.message.make_query("selfweighted.example.org", "A")
self.sendUDPQuery(query)
# wait for health checks to happen
"""
Basic pickrandomsample() test with a set of TXT records
"""
- expected = [dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'bob', 'alice'),
- dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'bob', 'john'),
- dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'alice', 'bob'),
- dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'alice', 'john'),
- dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'john', 'bob'),
- dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'john', 'alice')]
- query = dns.message.make_query('randn-txt.example.org', 'TXT')
+ expected = [
+ dns.rrset.from_text("randn-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "bob", "alice"),
+ dns.rrset.from_text("randn-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "bob", "john"),
+ dns.rrset.from_text("randn-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "alice", "bob"),
+ dns.rrset.from_text("randn-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "alice", "john"),
+ dns.rrset.from_text("randn-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "john", "bob"),
+ dns.rrset.from_text("randn-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "john", "alice"),
+ ]
+ query = dns.message.make_query("randn-txt.example.org", "TXT")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic pickwrandom() test with a set of A records
"""
- expected = [dns.rrset.from_text('wrand.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.103'.format(prefix=self._PREFIX)),
- dns.rrset.from_text('wrand.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.102'.format(prefix=self._PREFIX))]
- query = dns.message.make_query('wrand.example.org', 'A')
+ expected = [
+ dns.rrset.from_text(
+ "wrand.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.103".format(prefix=self._PREFIX)
+ ),
+ dns.rrset.from_text(
+ "wrand.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.102".format(prefix=self._PREFIX)
+ ),
+ ]
+ query = dns.message.make_query("wrand.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic pickwrandom() test with a set of TXT records
"""
- expected = [dns.rrset.from_text('wrand-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'bob'),
- dns.rrset.from_text('wrand-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'alice')]
- query = dns.message.make_query('wrand-txt.example.org', 'TXT')
+ expected = [
+ dns.rrset.from_text("wrand-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "bob"),
+ dns.rrset.from_text("wrand-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "alice"),
+ ]
+ query = dns.message.make_query("wrand-txt.example.org", "TXT")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic ifportup() test
"""
- query = dns.message.make_query('all.ifportup.example.org', 'A')
+ query = dns.message.make_query("all.ifportup.example.org", "A")
expected = [
- dns.rrset.from_text('all.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.101'.format(prefix=self._PREFIX)),
- dns.rrset.from_text('all.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.102'.format(prefix=self._PREFIX))]
+ dns.rrset.from_text(
+ "all.ifportup.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.101".format(prefix=self._PREFIX)
+ ),
+ dns.rrset.from_text(
+ "all.ifportup.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.102".format(prefix=self._PREFIX)
+ ),
+ ]
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic ifportup() test with some ports DOWN
"""
- query = dns.message.make_query('some.ifportup.example.org', 'A')
+ query = dns.message.make_query("some.ifportup.example.org", "A")
expected = [
- dns.rrset.from_text('some.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
- '192.168.42.21'),
- dns.rrset.from_text('some.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.102'.format(prefix=self._PREFIX))]
+ dns.rrset.from_text("some.ifportup.example.org.", 0, dns.rdataclass.IN, "A", "192.168.42.21"),
+ dns.rrset.from_text(
+ "some.ifportup.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.102".format(prefix=self._PREFIX)
+ ),
+ ]
# we first expect any of the IPs as no check has been performed yet
res = self.sendUDPQuery(query)
"""
Basic ifportup() test with some ports DOWN from multiple sets
"""
- query = dns.message.make_query('multi.ifportup.example.org', 'A')
+ query = dns.message.make_query("multi.ifportup.example.org", "A")
expected = [
- dns.rrset.from_text('multi.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
- '192.168.42.21'),
- dns.rrset.from_text('multi.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
- '192.168.42.23'),
- dns.rrset.from_text('multi.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.102'.format(prefix=self._PREFIX)),
- dns.rrset.from_text('multi.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.101'.format(prefix=self._PREFIX))
+ dns.rrset.from_text("multi.ifportup.example.org.", 0, dns.rdataclass.IN, "A", "192.168.42.21"),
+ dns.rrset.from_text("multi.ifportup.example.org.", 0, dns.rdataclass.IN, "A", "192.168.42.23"),
+ dns.rrset.from_text(
+ "multi.ifportup.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.102".format(prefix=self._PREFIX)
+ ),
+ dns.rrset.from_text(
+ "multi.ifportup.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.101".format(prefix=self._PREFIX)
+ ),
]
# we first expect any of the IPs as no check has been performed yet
"""
Basic ifportup() test with all ports DOWN
"""
- query = dns.message.make_query('none.ifportup.example.org', 'A')
+ query = dns.message.make_query("none.ifportup.example.org", "A")
expected = [
- dns.rrset.from_text('none.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
- '192.168.42.21'),
- dns.rrset.from_text('none.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
- '192.168.21.42'.format(prefix=self._PREFIX))]
+ dns.rrset.from_text("none.ifportup.example.org.", 0, dns.rdataclass.IN, "A", "192.168.42.21"),
+ dns.rrset.from_text(
+ "none.ifportup.example.org.", 0, dns.rdataclass.IN, "A", "192.168.21.42".format(prefix=self._PREFIX)
+ ),
+ ]
# we first expect any of the IPs as no check has been performed yet
res = self.sendUDPQuery(query)
"""
Basic ifportup() test with all ports DOWN, fallback to 'all' backup selector
"""
- name = 'all.noneup.ifportup.example.org.'
+ name = "all.noneup.ifportup.example.org."
query = dns.message.make_query(name, dns.rdatatype.A)
- expected = [dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, '192.168.42.21', '192.168.21.42')]
+ expected = [dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, "192.168.42.21", "192.168.21.42")]
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic ifportup() test with all ports DOWN, fallback to 'empty' backup selector
"""
- name = 'empty.noneup.ifportup.example.org.'
+ name = "empty.noneup.ifportup.example.org."
query = dns.message.make_query(name, dns.rdatatype.A)
# With backupSelector='empty', we always return NODATA when there are no healthy targets,
"""
Basic ifurlup() test
"""
- reachable = [
- '{prefix}.103'.format(prefix=self._PREFIX)
- ]
- unreachable = ['192.168.42.105']
+ reachable = ["{prefix}.103".format(prefix=self._PREFIX)]
+ unreachable = ["192.168.42.105"]
ips = reachable + unreachable
all_rrs = []
reachable_rrs = []
for ip in ips:
- rr = dns.rrset.from_text('usa.example.org.', 0, dns.rdataclass.IN, 'A', ip)
+ rr = dns.rrset.from_text("usa.example.org.", 0, dns.rdataclass.IN, "A", ip)
all_rrs.append(rr)
if ip in reachable:
reachable_rrs.append(rr)
- query = dns.message.make_query('usa.example.org', 'A')
+ query = dns.message.make_query("usa.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, all_rrs)
"""
Basic ifurlup() test, with non-default HTTP code
"""
- reachable = [
- '{prefix}.103'.format(prefix=self._PREFIX)
- ]
- unreachable = ['192.168.42.105']
+ reachable = ["{prefix}.103".format(prefix=self._PREFIX)]
+ unreachable = ["192.168.42.105"]
ips = reachable + unreachable
all_rrs = []
reachable_rrs = []
for ip in ips:
- rr = dns.rrset.from_text('usa-404.example.org.', 0, dns.rdataclass.IN, 'A', ip)
+ rr = dns.rrset.from_text("usa-404.example.org.", 0, dns.rdataclass.IN, "A", ip)
all_rrs.append(rr)
if ip in reachable:
reachable_rrs.append(rr)
- query = dns.message.make_query('usa-404.example.org', 'A')
+ query = dns.message.make_query("usa-404.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, all_rrs)
"""
Basic ifurlup() test with mutiple sets
"""
- reachable = [
- '{prefix}.103'.format(prefix=self._PREFIX)
- ]
- unreachable = ['192.168.42.101', '192.168.42.102', '192.168.42.105']
+ reachable = ["{prefix}.103".format(prefix=self._PREFIX)]
+ unreachable = ["192.168.42.101", "192.168.42.102", "192.168.42.105"]
ips = reachable + unreachable
all_rrs = []
reachable_rrs = []
for ip in ips:
- rr = dns.rrset.from_text('usa-ext.example.org.', 0, dns.rdataclass.IN, 'A', ip)
+ rr = dns.rrset.from_text("usa-ext.example.org.", 0, dns.rdataclass.IN, "A", ip)
all_rrs.append(rr)
if ip in reachable:
reachable_rrs.append(rr)
- query = dns.message.make_query('usa-ext.example.org', 'A')
+ query = dns.message.make_query("usa-ext.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, all_rrs)
self.assertAnyRRsetInAnswer(res, reachable_rrs)
def testIfurlextup(self):
- expected = [dns.rrset.from_text('ifurlextup.example.org.', 0, dns.rdataclass.IN, dns.rdatatype.A, '192.168.0.3')]
+ expected = [
+ dns.rrset.from_text("ifurlextup.example.org.", 0, dns.rdataclass.IN, dns.rdatatype.A, "192.168.0.3")
+ ]
- query = dns.message.make_query('ifurlextup.example.org', 'A')
+ query = dns.message.make_query("ifurlextup.example.org", "A")
self.sendUDPQuery(query)
# wait for health checks to happen
Basic ifurlup() test with the simplified list of ips
Also ensures the correct path is queried
"""
- reachable = [
- '{prefix}.101'.format(prefix=self._PREFIX)
- ]
- unreachable = ['192.168.42.101']
+ reachable = ["{prefix}.101".format(prefix=self._PREFIX)]
+ unreachable = ["192.168.42.101"]
ips = reachable + unreachable
all_rrs = []
reachable_rrs = []
for ip in ips:
- rr = dns.rrset.from_text('mix.ifurlup.example.org.', 0, dns.rdataclass.IN, 'A', ip)
+ rr = dns.rrset.from_text("mix.ifurlup.example.org.", 0, dns.rdataclass.IN, "A", ip)
all_rrs.append(rr)
if ip in reachable:
reachable_rrs.append(rr)
- query = dns.message.make_query('mix.ifurlup.example.org', 'A')
+ query = dns.message.make_query("mix.ifurlup.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, all_rrs)
"""
ifurlup() test where send headers.
"""
- reachable = [
- '{prefix}.102'.format(prefix=self._PREFIX)
- ]
- unreachable = ['192.168.42.105']
+ reachable = ["{prefix}.102".format(prefix=self._PREFIX)]
+ unreachable = ["192.168.42.105"]
ips = reachable + unreachable
all_rrs = []
reachable_rrs = []
for ip in ips:
- rr = dns.rrset.from_text('goodheaders.ifurlup.example.org.', 0, dns.rdataclass.IN, 'A', ip)
+ rr = dns.rrset.from_text("goodheaders.ifurlup.example.org.", 0, dns.rdataclass.IN, "A", ip)
all_rrs.append(rr)
if ip in reachable:
reachable_rrs.append(rr)
- query = dns.message.make_query('goodheaders.ifurlup.example.org', 'A')
+ query = dns.message.make_query("goodheaders.ifurlup.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, all_rrs)
"""
ifurlup() test where send headers, but the value is wrong
"""
- reachable = [
- '{prefix}.102'.format(prefix=self._PREFIX)
- ]
- unreachable = ['192.168.42.105']
+ reachable = ["{prefix}.102".format(prefix=self._PREFIX)]
+ unreachable = ["192.168.42.105"]
ips = reachable + unreachable
all_rrs = []
reachable_rrs = []
for ip in ips:
- rr = dns.rrset.from_text('badheaders.ifurlup.example.org.', 0, dns.rdataclass.IN, 'A', ip)
+ rr = dns.rrset.from_text("badheaders.ifurlup.example.org.", 0, dns.rdataclass.IN, "A", ip)
all_rrs.append(rr)
if ip in reachable:
reachable_rrs.append(rr)
- query = dns.message.make_query('badheaders.ifurlup.example.org', 'A')
+ query = dns.message.make_query("badheaders.ifurlup.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, all_rrs)
"""
Basic latlon() test
"""
- name = 'latlon.geo.example.org.'
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.0', 24)
- query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
- expected = dns.rrset.from_text(name, 0,
- dns.rdataclass.IN, 'TXT',
- '"47.913000 -122.304200"')
+ name = "latlon.geo.example.org."
+ ecso = clientsubnetoption.ClientSubnetOption("1.2.3.0", 24)
+ query = dns.message.make_query(name, "TXT", "IN", use_edns=True, payload=4096, options=[ecso])
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "TXT", '"47.913000 -122.304200"')
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic latlonloc() test
"""
- name = 'latlonloc.geo.example.org.'
- expected = dns.rrset.from_text(name, 0,dns.rdataclass.IN, 'TXT',
- '"47 54 46.8 N 122 18 15.12 W 0.00m 1.00m 10000.00m 10.00m"')
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.0', 24)
- query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "latlonloc.geo.example.org."
+ expected = dns.rrset.from_text(
+ name, 0, dns.rdataclass.IN, "TXT", '"47 54 46.8 N 122 18 15.12 W 0.00m 1.00m 10000.00m 10.00m"'
+ )
+ ecso = clientsubnetoption.ClientSubnetOption("1.2.3.0", 24)
+ query = dns.message.make_query(name, "TXT", "IN", use_edns=True, payload=4096, options=[ecso])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Ensure errors coming from LUA wildcards are reported
"""
- query = dns.message.make_query('failure.magic.example.org', 'A')
+ query = dns.message.make_query("failure.magic.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
"""
Basic closestMagic() test
"""
- name = 'www-balanced.example.org.'
- cname = '1-1-1-3.17-1-2-4.1-2-3-5.magic.example.org.'
- queries = [
- ('1.1.1.0', 24, '1.1.1.3'),
- ('1.2.3.0', 24, '1.2.3.5'),
- ('17.1.0.0', 16, '17.1.2.4')
- ]
+ name = "www-balanced.example.org."
+ cname = "1-1-1-3.17-1-2-4.1-2-3-5.magic.example.org."
+ queries = [("1.1.1.0", 24, "1.1.1.3"), ("1.2.3.0", 24, "1.2.3.5"), ("17.1.0.0", 16, "17.1.2.4")]
- for (subnet, mask, ip) in queries:
+ for subnet, mask, ip in queries:
ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(query)
response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.CNAME, cname))
- response.answer.append(dns.rrset.from_text(cname, 0, dns.rdataclass.IN, 'A', ip))
+ response.answer.append(dns.rrset.from_text(cname, 0, dns.rdataclass.IN, "A", ip))
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic asnum() test
"""
- queries = [
- ('1.1.1.0', 24, '"true"'),
- ('1.2.3.0', 24, '"false"'),
- ('17.1.0.0', 16, '"false"')
- ]
- name = 'asnum.geo.example.org.'
- for (subnet, mask, txt) in queries:
+ queries = [("1.1.1.0", 24, '"true"'), ("1.2.3.0", 24, '"false"'), ("17.1.0.0", 16, '"false"')]
+ name = "asnum.geo.example.org."
+ for subnet, mask, txt in queries:
ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
- query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
+ query = dns.message.make_query(name, "TXT", "IN", use_edns=True, payload=4096, options=[ecso])
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "TXT", txt)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic country() test
"""
- queries = [
- ('1.1.1.0', 24, '"false"'),
- ('1.2.3.0', 24, '"true"'),
- ('17.1.0.0', 16, '"false"')
- ]
- name = 'country.geo.example.org.'
- for (subnet, mask, txt) in queries:
+ queries = [("1.1.1.0", 24, '"false"'), ("1.2.3.0", 24, '"true"'), ("17.1.0.0", 16, '"false"')]
+ name = "country.geo.example.org."
+ for subnet, mask, txt in queries:
ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
- query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
+ query = dns.message.make_query(name, "TXT", "IN", use_edns=True, payload=4096, options=[ecso])
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "TXT", txt)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic countryCode() test
"""
- queries = [
- ('1.1.1.0', 24, '"au"'),
- ('1.2.3.0', 24, '"us"'),
- ('17.1.0.0', 16, '"--"')
- ]
- name = 'country-code.geo.example.org.'
- for (subnet, mask, txt) in queries:
+ queries = [("1.1.1.0", 24, '"au"'), ("1.2.3.0", 24, '"us"'), ("17.1.0.0", 16, '"--"')]
+ name = "country-code.geo.example.org."
+ for subnet, mask, txt in queries:
ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
- query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
+ query = dns.message.make_query(name, "TXT", "IN", use_edns=True, payload=4096, options=[ecso])
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "TXT", txt)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic region() test
"""
- queries = [
- ('1.1.1.0', 24, '"false"'),
- ('1.2.3.0', 24, '"true"'),
- ('17.1.0.0', 16, '"false"')
- ]
- name = 'region.geo.example.org.'
- for (subnet, mask, txt) in queries:
+ queries = [("1.1.1.0", 24, '"false"'), ("1.2.3.0", 24, '"true"'), ("17.1.0.0", 16, '"false"')]
+ name = "region.geo.example.org."
+ for subnet, mask, txt in queries:
ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
- query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
+ query = dns.message.make_query(name, "TXT", "IN", use_edns=True, payload=4096, options=[ecso])
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "TXT", txt)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic regionCode() test
"""
- queries = [
- ('1.1.1.0', 24, '"--"'),
- ('1.2.3.0', 24, '"ca"'),
- ('17.1.0.0', 16, '"--"')
- ]
- name = 'region-code.geo.example.org.'
- for (subnet, mask, txt) in queries:
+ queries = [("1.1.1.0", 24, '"--"'), ("1.2.3.0", 24, '"ca"'), ("17.1.0.0", 16, '"--"')]
+ name = "region-code.geo.example.org."
+ for subnet, mask, txt in queries:
ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
- query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
+ query = dns.message.make_query(name, "TXT", "IN", use_edns=True, payload=4096, options=[ecso])
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "TXT", txt)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic continent() test
"""
- queries = [
- ('1.1.1.0', 24, '"false"'),
- ('1.2.3.0', 24, '"true"'),
- ('17.1.0.0', 16, '"false"')
- ]
- name = 'continent.geo.example.org.'
- for (subnet, mask, txt) in queries:
+ queries = [("1.1.1.0", 24, '"false"'), ("1.2.3.0", 24, '"true"'), ("17.1.0.0", 16, '"false"')]
+ name = "continent.geo.example.org."
+ for subnet, mask, txt in queries:
ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
- query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
+ query = dns.message.make_query(name, "TXT", "IN", use_edns=True, payload=4096, options=[ecso])
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "TXT", txt)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic continentCode() test
"""
- queries = [
- ('1.1.1.0', 24, '"oc"'),
- ('1.2.3.0', 24, '"na"'),
- ('17.1.0.0', 16, '"--"')
- ]
- name = 'continent-code.geo.example.org.'
- for (subnet, mask, txt) in queries:
+ queries = [("1.1.1.0", 24, '"oc"'), ("1.2.3.0", 24, '"na"'), ("17.1.0.0", 16, '"--"')]
+ name = "continent-code.geo.example.org."
+ for subnet, mask, txt in queries:
ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
- query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
+ query = dns.message.make_query(name, "TXT", "IN", use_edns=True, payload=4096, options=[ecso])
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "TXT", txt)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic pickclosest() test
"""
- queries = [
- ('1.1.1.0', 24, '1.1.1.2'),
- ('1.2.3.0', 24, '1.2.3.4'),
- ('17.1.0.0', 16, '1.1.1.2')
- ]
- name = 'closest.geo.example.org.'
- for (subnet, mask, ip) in queries:
+ queries = [("1.1.1.0", 24, "1.1.1.2"), ("1.2.3.0", 24, "1.2.3.4"), ("17.1.0.0", 16, "1.1.1.2")]
+ name = "closest.geo.example.org."
+ for subnet, mask, ip in queries:
ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', ip)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", ip)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Basic all() test
"""
- expected = [dns.rrset.from_text('all.example.org.', 0, dns.rdataclass.IN, dns.rdatatype.A, '1.2.3.4', '4.3.2.1')]
- query = dns.message.make_query('all.example.org.', 'A')
+ expected = [
+ dns.rrset.from_text("all.example.org.", 0, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4", "4.3.2.1")
+ ]
+ query = dns.message.make_query("all.example.org.", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
queries = [
{
- 'expected': dns.rrset.from_text('true.netmask.example.org.', 0,
- dns.rdataclass.IN, 'TXT',
- '"true"'),
- 'query': dns.message.make_query('true.netmask.example.org', 'TXT')
+ "expected": dns.rrset.from_text("true.netmask.example.org.", 0, dns.rdataclass.IN, "TXT", '"true"'),
+ "query": dns.message.make_query("true.netmask.example.org", "TXT"),
},
{
- 'expected': dns.rrset.from_text('false.netmask.example.org.', 0,
- dns.rdataclass.IN, 'TXT',
- '"false"'),
- 'query': dns.message.make_query('false.netmask.example.org', 'TXT')
- }
+ "expected": dns.rrset.from_text("false.netmask.example.org.", 0, dns.rdataclass.IN, "TXT", '"false"'),
+ "query": dns.message.make_query("false.netmask.example.org", "TXT"),
+ },
]
- for query in queries :
- res = self.sendUDPQuery(query['query'])
+ for query in queries:
+ res = self.sendUDPQuery(query["query"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertRRsetInAnswer(res, query['expected'])
+ self.assertRRsetInAnswer(res, query["expected"])
def testView(self):
"""
"""
queries = [
{
- 'expected': dns.rrset.from_text('view.example.org.', 0,
- dns.rdataclass.IN, 'A',
- '{prefix}.54'.format(prefix=self._PREFIX)),
- 'query': dns.message.make_query('view.example.org', 'A')
+ "expected": dns.rrset.from_text(
+ "view.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.54".format(prefix=self._PREFIX)
+ ),
+ "query": dns.message.make_query("view.example.org", "A"),
},
{
- 'expected': dns.rrset.from_text('txt.view.example.org.', 0,
- dns.rdataclass.IN, 'TXT',
- '"else"'),
- 'query': dns.message.make_query('txt.view.example.org', 'TXT')
- }
+ "expected": dns.rrset.from_text("txt.view.example.org.", 0, dns.rdataclass.IN, "TXT", '"else"'),
+ "query": dns.message.make_query("txt.view.example.org", "TXT"),
+ },
]
- for query in queries :
- res = self.sendUDPQuery(query['query'])
+ for query in queries:
+ res = self.sendUDPQuery(query["query"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertRRsetInAnswer(res, query['expected'])
+ self.assertRRsetInAnswer(res, query["expected"])
def testViewNoMatch(self):
"""
view() test where no netmask match
"""
- query = dns.message.make_query('none.view.example.org', 'A')
+ query = dns.message.make_query("none.view.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
Basic pickwhashed() and pickchashed() test with a set of A records
As the `bestwho` is hashed, we should always get the same answer
"""
- for qname in ['whashed.example.org.', 'chashed.example.org.']:
- expected = [dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '1.2.3.4'),
- dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '4.3.2.1')]
- query = dns.message.make_query(qname, 'A')
+ for qname in ["whashed.example.org.", "chashed.example.org."]:
+ expected = [
+ dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "1.2.3.4"),
+ dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "4.3.2.1"),
+ ]
+ query = dns.message.make_query(qname, "A")
first = self.sendUDPQuery(query)
self.assertRcodeEqual(first, dns.rcode.NOERROR)
queries = [
{
- 'query': dns.message.make_query('test.namehashed.example.org', 'A'),
- 'expected': dns.rrset.from_text('test.namehashed.example.org.', 0,
- dns.rdataclass.IN, 'A',
- '1.2.3.4'),
+ "query": dns.message.make_query("test.namehashed.example.org", "A"),
+ "expected": dns.rrset.from_text("test.namehashed.example.org.", 0, dns.rdataclass.IN, "A", "1.2.3.4"),
},
{
- 'query': dns.message.make_query('test2.namehashed.example.org', 'A'),
- 'expected': dns.rrset.from_text('test2.namehashed.example.org.', 0,
- dns.rdataclass.IN, 'A',
- '4.3.2.1'),
- }
+ "query": dns.message.make_query("test2.namehashed.example.org", "A"),
+ "expected": dns.rrset.from_text("test2.namehashed.example.org.", 0, dns.rdataclass.IN, "A", "4.3.2.1"),
+ },
]
- for query in queries :
- res = self.sendUDPQuery(query['query'])
+ for query in queries:
+ res = self.sendUDPQuery(query["query"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertRRsetInAnswer(res, query['expected'])
+ self.assertRRsetInAnswer(res, query["expected"])
def testCWHashedTxt(self):
"""
Basic pickwhashed() test with a set of TXT records
As the `bestwho` is hashed, we should always get the same answer
"""
- for qname in ['whashed-txt.example.org.', 'chashed-txt.example.org.']:
- expected = [dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'TXT', 'bob'),
- dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'TXT', 'alice')]
- query = dns.message.make_query(qname,'TXT')
+ for qname in ["whashed-txt.example.org.", "chashed-txt.example.org."]:
+ expected = [
+ dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "TXT", "bob"),
+ dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "TXT", "alice"),
+ ]
+ query = dns.message.make_query(qname, "TXT")
first = self.sendUDPQuery(query)
self.assertRcodeEqual(first, dns.rcode.NOERROR)
Basic pickhashed() test with a set of A records
As the `bestwho` is hashed, we should always get the same answer
"""
- expected = [dns.rrset.from_text('hashed.example.org.', 0, dns.rdataclass.IN, 'A', '1.2.3.4'),
- dns.rrset.from_text('hashed.example.org.', 0, dns.rdataclass.IN, 'A', '4.3.2.1')]
- query = dns.message.make_query('hashed.example.org', 'A')
+ expected = [
+ dns.rrset.from_text("hashed.example.org.", 0, dns.rdataclass.IN, "A", "1.2.3.4"),
+ dns.rrset.from_text("hashed.example.org.", 0, dns.rdataclass.IN, "A", "4.3.2.1"),
+ ]
+ query = dns.message.make_query("hashed.example.org", "A")
first = self.sendUDPQuery(query)
self.assertRcodeEqual(first, dns.rcode.NOERROR)
Basic pickhashed() test with a set of AAAA records
As the `bestwho` is hashed, we should always get the same answer
"""
- expected = [dns.rrset.from_text('hashed-v6.example.org.', 0, dns.rdataclass.IN, 'AAAA', '2001:db8:a0b:12f0::1'),
- dns.rrset.from_text('hashed-v6.example.org.', 0, dns.rdataclass.IN, 'AAAA', 'fe80::2a1:9bff:fe9b:f268')]
- query = dns.message.make_query('hashed-v6.example.org', 'AAAA')
+ expected = [
+ dns.rrset.from_text("hashed-v6.example.org.", 0, dns.rdataclass.IN, "AAAA", "2001:db8:a0b:12f0::1"),
+ dns.rrset.from_text("hashed-v6.example.org.", 0, dns.rdataclass.IN, "AAAA", "fe80::2a1:9bff:fe9b:f268"),
+ ]
+ query = dns.message.make_query("hashed-v6.example.org", "AAAA")
first = self.sendUDPQuery(query)
self.assertRcodeEqual(first, dns.rcode.NOERROR)
Basic pickhashed() test with a set of TXT records
As the `bestwho` is hashed, we should always get the same answer
"""
- expected = [dns.rrset.from_text('hashed-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'bob'),
- dns.rrset.from_text('hashed-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'alice')]
- query = dns.message.make_query('hashed-txt.example.org', 'TXT')
+ expected = [
+ dns.rrset.from_text("hashed-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "bob"),
+ dns.rrset.from_text("hashed-txt.example.org.", 0, dns.rdataclass.IN, "TXT", "alice"),
+ ]
+ query = dns.message.make_query("hashed-txt.example.org", "TXT")
first = self.sendUDPQuery(query)
self.assertRcodeEqual(first, dns.rcode.NOERROR)
Test that pickwhashed() does not accept zero weights
"""
- query = dns.message.make_query('whashedzero.example.org', 'A')
+ query = dns.message.make_query("whashedzero.example.org", "A")
response = self.sendUDPQuery(query)
self.assertRcodeEqual(response, dns.rcode.SERVFAIL)
Test that pickwhashed() does not accept negative weights
"""
- query = dns.message.make_query('whashednegative.example.org', 'A')
+ query = dns.message.make_query("whashednegative.example.org", "A")
response = self.sendUDPQuery(query)
self.assertRcodeEqual(response, dns.rcode.SERVFAIL)
"""
Test if LUA scripts are aborted if script execution takes too long
"""
- query = dns.message.make_query('timeout.example.org', 'A')
+ query = dns.message.make_query("timeout.example.org", "A")
first = self.sendUDPQuery(query)
self.assertRcodeEqual(first, dns.rcode.SERVFAIL)
-
def testA(self):
"""
Test A query against `any`
"""
- name = 'any.example.org.'
+ name = "any.example.org."
- query = dns.message.make_query(name, 'A')
+ query = dns.message.make_query(name, "A")
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'))
+ response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"))
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
Test ANY query against `any`
"""
- name = 'any.example.org.'
+ name = "any.example.org."
- query = dns.message.make_query(name, 'ANY')
+ query = dns.message.make_query(name, "ANY")
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'))
- response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', '"hello there"'))
+ response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"))
+ response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, "TXT", '"hello there"'))
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Test newCAFromRaw() function
"""
- name = 'newcafromraw.example.org.'
+ name = "newcafromraw.example.org."
- query = dns.message.make_query(name, 'A')
+ query = dns.message.make_query(name, "A")
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, '65.66.67.68'))
+ response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, "65.66.67.68"))
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(res.answer, response.answer)
- query = dns.message.make_query(name, 'AAAA')
+ query = dns.message.make_query(name, "AAAA")
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.AAAA, '4142:4344:3032:3033:3430:3530:3630:3730'))
+ response.answer.append(
+ dns.rrset.from_text(
+ name, 0, dns.rdataclass.IN, dns.rdatatype.AAAA, "4142:4344:3032:3033:3430:3530:3630:3730"
+ )
+ )
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Test resolve() function
"""
- name = 'resolve.example.org.'
+ name = "resolve.example.org."
- query = dns.message.make_query(name, 'A')
+ query = dns.message.make_query(name, "A")
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1'))
+ response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1"))
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Test filterForward() function with empty fallback
"""
- name = 'filterforwardempty.example.org.'
+ name = "filterforwardempty.example.org."
- query = dns.message.make_query(name, 'A')
+ query = dns.message.make_query(name, "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testCreateForwardAndReverse(self):
expected = {
- ".createforward.example.org." : (dns.rdatatype.A, {
- "1.2.3.4": "1.2.3.4",
- "1.2.3.4.static": "1.2.3.4",
- "1.2.3.4.5.6": "1.2.3.4",
- "invalid.1.2.3.4": "0.0.0.0",
- "invalid": "0.0.0.0",
- "1-2-3-4": "1.2.3.4",
- "1-2-3-4.foo": "1.2.3.4",
- "1-2-3-4.foo.bar": "1.2.3.4",
- "1-2-3-4.foo.bar.baz": "0.0.0.0",
- "1-2-3-4.foo.bar.baz.quux": "0.0.0.0",
- "ip-1-2-3-4": "1.2.3.4",
- "ip-is-here-for-you-1-2-3-4": "1.2.3.4",
- "40414243": "64.65.66.67",
- "p40414243": "64.65.66.67",
- "ip40414243": "64.65.66.67",
- "ipp40414243": "64.65.66.67",
- "ip4041424": "0.0.0.0",
- "ip-441424": "0.0.0.0",
- "ip-5abcdef": "0.0.0.0",
- "some-text-with-more-than-five-dashes": "0.0.0.0",
- "host64-22-33-44": "64.22.33.44",
- "2.2.2.2": "0.0.0.0" # filtered
- }),
- ".createreverse.example.org." : (dns.rdatatype.PTR, {
- "4.3.2.1": "1-2-3-4.example.com.",
- "10.10.10.10": "quad10.example.com.", # exception
- # error: values not in the 0..255 range
- "256.384.512.640": "error."
- }),
- ".createforward6.example.org." : (dns.rdatatype.AAAA, {
- "2001--db8" : "2001::db8",
- "20010002000300040005000600070db8" : "2001:2:3:4:5:6:7:db8",
- "blabla20010002000300040005000600070db8" : "2001:2:3:4:5:6:7:db8",
- "4000-db8--1" : "fe80::1", # filtered, with fallback address override
- "l1.l2.l3.l4.l5.l6.l7.l8" : "fe80::1"
- }),
- ".createreverse6.example.org." : (dns.rdatatype.PTR, {
- "8.b.d.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2" : "2001--db8.example.com.",
- "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2" : "example.example.com.", # exception
- # error: fewer than 32 labels (including ".createreverse6.example.org.")
- "8.b.d.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2" : "unknown.",
- # error: I and O instead of 1 and 0 in the would-be address
- "O.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.b.c.A.2.F.E.9.C.0.0.3.0.0.2.I" : "error."
- })
+ ".createforward.example.org.": (
+ dns.rdatatype.A,
+ {
+ "1.2.3.4": "1.2.3.4",
+ "1.2.3.4.static": "1.2.3.4",
+ "1.2.3.4.5.6": "1.2.3.4",
+ "invalid.1.2.3.4": "0.0.0.0",
+ "invalid": "0.0.0.0",
+ "1-2-3-4": "1.2.3.4",
+ "1-2-3-4.foo": "1.2.3.4",
+ "1-2-3-4.foo.bar": "1.2.3.4",
+ "1-2-3-4.foo.bar.baz": "0.0.0.0",
+ "1-2-3-4.foo.bar.baz.quux": "0.0.0.0",
+ "ip-1-2-3-4": "1.2.3.4",
+ "ip-is-here-for-you-1-2-3-4": "1.2.3.4",
+ "40414243": "64.65.66.67",
+ "p40414243": "64.65.66.67",
+ "ip40414243": "64.65.66.67",
+ "ipp40414243": "64.65.66.67",
+ "ip4041424": "0.0.0.0",
+ "ip-441424": "0.0.0.0",
+ "ip-5abcdef": "0.0.0.0",
+ "some-text-with-more-than-five-dashes": "0.0.0.0",
+ "host64-22-33-44": "64.22.33.44",
+ "2.2.2.2": "0.0.0.0", # filtered
+ },
+ ),
+ ".createreverse.example.org.": (
+ dns.rdatatype.PTR,
+ {
+ "4.3.2.1": "1-2-3-4.example.com.",
+ "10.10.10.10": "quad10.example.com.", # exception
+ # error: values not in the 0..255 range
+ "256.384.512.640": "error.",
+ },
+ ),
+ ".createforward6.example.org.": (
+ dns.rdatatype.AAAA,
+ {
+ "2001--db8": "2001::db8",
+ "20010002000300040005000600070db8": "2001:2:3:4:5:6:7:db8",
+ "blabla20010002000300040005000600070db8": "2001:2:3:4:5:6:7:db8",
+ "4000-db8--1": "fe80::1", # filtered, with fallback address override
+ "l1.l2.l3.l4.l5.l6.l7.l8": "fe80::1",
+ },
+ ),
+ ".createreverse6.example.org.": (
+ dns.rdatatype.PTR,
+ {
+ "8.b.d.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2": "2001--db8.example.com.",
+ "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2": "example.example.com.", # exception
+ # error: fewer than 32 labels (including ".createreverse6.example.org.")
+ "8.b.d.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2": "unknown.",
+ # error: I and O instead of 1 and 0 in the would-be address
+ "O.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.b.c.A.2.F.E.9.C.0.0.3.0.0.2.I": "error.",
+ },
+ ),
}
for suffix, v in expected.items():
query = dns.message.make_query(name, qtype)
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(
- name, 0, dns.rdataclass.IN, qtype, target))
+ response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, qtype, target))
res = self.sendUDPQuery(query)
print(res)
Fix #7524
"""
expected = {
- ".no-filter.createforward6.example.org." : (dns.rdatatype.AAAA, {
- "0--0" : "::",
- "0--1" : "::1",
- "0aa--0" : "aa::",
- "0aa--1" : "aa::1",
- "2001--0" : "2001::",
- "a-b--c" : "a:b::c",
- "a--b-c" : "a::b:c"
- }),
- ".createreverse6.example.org." : (dns.rdatatype.PTR, {
- "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0" : "0--0.example.com.",
- "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0" : "0--1.example.com.",
- "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.a.0.0" : "0aa--0.example.com.",
- "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.a.0.0" : "0aa--1.example.com.",
- "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2" : "2001--0.example.com.",
- "c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.b.0.0.0.a.0.0.0" : "a-b--c.example.com.",
- "c.0.0.0.b.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0" : "a--b-c.example.com."
- })
+ ".no-filter.createforward6.example.org.": (
+ dns.rdatatype.AAAA,
+ {
+ "0--0": "::",
+ "0--1": "::1",
+ "0aa--0": "aa::",
+ "0aa--1": "aa::1",
+ "2001--0": "2001::",
+ "a-b--c": "a:b::c",
+ "a--b-c": "a::b:c",
+ },
+ ),
+ ".createreverse6.example.org.": (
+ dns.rdatatype.PTR,
+ {
+ "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0": "0--0.example.com.",
+ "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0": "0--1.example.com.",
+ "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.a.0.0": "0aa--0.example.com.",
+ "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.a.0.0": "0aa--1.example.com.",
+ "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2": "2001--0.example.com.",
+ "c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.b.0.0.0.a.0.0.0": "a-b--c.example.com.",
+ "c.0.0.0.b.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0": "a--b-c.example.com.",
+ },
+ ),
}
for suffix, v in expected.items():
query = dns.message.make_query(name, qtype)
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(
- name, 0, dns.rdataclass.IN, qtype, target))
+ response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, qtype, target))
res = self.sendUDPQuery(query)
print(res)
"""
Helper function for shared/non-shared testing
"""
- name = 'counter.example.org.'
+ name = "counter.example.org."
- query = dns.message.make_query(name, 'TXT')
+ query = dns.message.make_query(name, "TXT")
responses = []
sender = self.sendTCPQuery if tcp else self.sendUDPQuery
res = sender(query)
responses.append(res.answer[0][0])
- return(responses)
+ return responses
def testCounter(self):
"""
Test dblookup() function
"""
- name = 'dblookup.example.org.'
+ name = "dblookup.example.org."
- query = dns.message.make_query(name, 'A')
+ query = dns.message.make_query(name, "A")
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.5'))
+ response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.5"))
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Test TXT query for whitespace
"""
- name = 'whitespace.example.org.'
+ name = "whitespace.example.org."
- query = dns.message.make_query(name, 'TXT')
+ query = dns.message.make_query(name, "TXT")
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.TXT, '"foo bar"' if expectws else '"foobar"'))
+ response.answer.append(
+ dns.rrset.from_text(
+ name, 0, dns.rdataclass.IN, dns.rdatatype.TXT, '"foo bar"' if expectws else '"foobar"'
+ )
+ )
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
Test GeoIPQueryAttribute enum
"""
- name = 'geoipqueryattribute.example.org.'
+ name = "geoipqueryattribute.example.org."
- query = dns.message.make_query(name, 'TXT')
+ query = dns.message.make_query(name, "TXT")
response = dns.message.make_response(query)
self.assertEqual(len(resUDP), 50)
self.assertEqual(len(resTCP), 50)
+
class TestLuaRecordsNoWhiteSpace(TestLuaRecords):
_config_template = """
geoip-database-files=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb
def testWhitespace(self):
return TestLuaRecords.testWhitespace(self, False)
+
class TestLuaRecordsSlowTimeouts(BaseLuaTest):
- # This configuration is similar to BaseLuaTest, but the health check
- # interval is increased to 5 seconds.
+ # This configuration is similar to BaseLuaTest, but the health check
+ # interval is increased to 5 seconds.
_config_template = """
geoip-database-files=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb
edns-subnet-processing=yes
"""
Simple ifurlup() test with minimumFailures option set.
"""
- reachable = [
- '{prefix}.103'.format(prefix=self._PREFIX)
- ]
- unreachable = ['192.168.42.105']
+ reachable = ["{prefix}.103".format(prefix=self._PREFIX)]
+ unreachable = ["192.168.42.105"]
ips = reachable + unreachable
all_rrs = []
reachable_rrs = []
unreachable_rrs = []
for ip in ips:
- rr = dns.rrset.from_text('usa-unreachable.example.org.', 0, dns.rdataclass.IN, 'A', ip)
+ rr = dns.rrset.from_text("usa-unreachable.example.org.", 0, dns.rdataclass.IN, "A", ip)
all_rrs.append(rr)
if ip in reachable:
reachable_rrs.append(rr)
else:
unreachable_rrs.append(rr)
- query = dns.message.make_query('usa-unreachable.example.org', 'A')
+ query = dns.message.make_query("usa-unreachable.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, all_rrs)
"""
Simple ifurlup() test with interval option set.
"""
- reachable = [
- '{prefix}.103'.format(prefix=self._PREFIX)
- ]
- unreachable = ['192.168.42.105']
+ reachable = ["{prefix}.103".format(prefix=self._PREFIX)]
+ unreachable = ["192.168.42.105"]
ips = reachable + unreachable
all_rrs = []
reachable_rrs = []
unreachable_rrs = []
for ip in ips:
- rr = dns.rrset.from_text('usa-slowcheck.example.org.', 0, dns.rdataclass.IN, 'A', ip)
+ rr = dns.rrset.from_text("usa-slowcheck.example.org.", 0, dns.rdataclass.IN, "A", ip)
all_rrs.append(rr)
if ip in reachable:
reachable_rrs.append(rr)
else:
unreachable_rrs.append(rr)
- query = dns.message.make_query('usa-slowcheck.example.org', 'A')
+ query = dns.message.make_query("usa-slowcheck.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, all_rrs)
"""
Simple ifurlup() test with failOnIncompleteCheck option set.
"""
- ips = ['192.168.42.105']
+ ips = ["192.168.42.105"]
all_rrs = []
for ip in ips:
- rr = dns.rrset.from_text('usa-failincomplete.example.org.', 0, dns.rdataclass.IN, 'A', ip)
+ rr = dns.rrset.from_text("usa-failincomplete.example.org.", 0, dns.rdataclass.IN, "A", ip)
all_rrs.append(rr)
- query = dns.message.make_query('usa-failincomplete.example.org', 'A')
+ query = dns.message.make_query("usa-failincomplete.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, all_rrs)
+
class TestLuaRecordsExecLimit(BaseLuaTest):
- # This configuration is similar to BaseLuaTest, but the exec limit is
- # set to a very low value.
+ # This configuration is similar to BaseLuaTest, but the exec limit is
+ # set to a very low value.
_config_template = """
geoip-database-files=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb
edns-subnet-processing=yes
"""
Test A query against `any`, failing due to exec-limit
"""
- name = 'any.example.org.'
+ name = "any.example.org."
- query = dns.message.make_query(name, 'A')
+ query = dns.message.make_query(name, "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
-if __name__ == '__main__':
+
+if __name__ == "__main__":
unittest.main()
exit(0)
from authtests import AuthTest
+
class TestLuaRecordsLMDB(AuthTest):
- _backend = 'lmdb'
+ _backend = "lmdb"
_config_template = """
launch=lmdb
"""
_zones = {
- 'example.org': """
+ "example.org": """
example.org. 3600 IN SOA {soa}
example.org. 3600 IN NS ns1.example.org.
example.org. 3600 IN NS ns2.example.org.
Basic pickrandom() test with a set of A records, with a bit of lua inclusion
"""
- expected = [dns.rrset.from_text('nested-lua.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.101'.format(prefix=self._PREFIX)),
- dns.rrset.from_text('nested-lua.example.org.', 0, dns.rdataclass.IN, 'A',
- '{prefix}.102'.format(prefix=self._PREFIX))]
+ expected = [
+ dns.rrset.from_text(
+ "nested-lua.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.101".format(prefix=self._PREFIX)
+ ),
+ dns.rrset.from_text(
+ "nested-lua.example.org.", 0, dns.rdataclass.IN, "A", "{prefix}.102".format(prefix=self._PREFIX)
+ ),
+ ]
- query = dns.message.make_query('nested-lua.example.org', 'A')
+ query = dns.message.make_query("nested-lua.example.org", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected)
-if __name__ == '__main__':
+
+if __name__ == "__main__":
unittest.main()
exit(0)
from authtests import AuthTest
from proxyprotocol import ProxyProtocol
+
class TestProxyProtocolLuaRecords(AuthTest):
_config_template = """
launch={backend}
"""
_zones = {
- 'example.org': """
+ "example.org": """
example.org. 3600 IN SOA {soa}
example.org. 3600 IN NS ns1.example.org.
example.org. 3600 IN NS ns2.example.org.
"""
See if LUA who picks up the inner address from the PROXY protocol
"""
-
+
for testWithECS in True, False:
# first test with an unproxied query - should get ignored
options = []
- expectedText = '192.0.2.1/192.0.2.1'
+ expectedText = "192.0.2.1/192.0.2.1"
if testWithECS:
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.5', 32)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.5", 32)
options.append(ecso)
- expectedText = '192.0.2.1/192.0.2.5'
+ expectedText = "192.0.2.1/192.0.2.5"
- query = dns.message.make_query('myip.example.org', 'TXT', 'IN', use_edns=testWithECS, options=options, payload=512)
+ query = dns.message.make_query(
+ "myip.example.org", "TXT", "IN", use_edns=testWithECS, options=options, payload=512
+ )
res = self.sendUDPQuery(query)
- self.assertEqual(res, None) # query was ignored correctly
-
+ self.assertEqual(res, None) # query was ignored correctly
# now send a proxied query
queryPayload = query.to_wire()
if data:
res = dns.message.from_wire(data)
- expected = [dns.rrset.from_text('myip.example.org.', 0, dns.rdataclass.IN, 'TXT', expectedText)]
+ expected = [dns.rrset.from_text("myip.example.org.", 0, dns.rdataclass.IN, "TXT", expectedText)]
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(res.answer, expected)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(res.answer, expected)
+
class TestProxyProtocolNOTIFY(AuthTest):
_config_template = """
launch={backend}
secondary
"""
- _secondary_zones = { 'example.org': '192.0.2.1',
- 'example.com': '192.0.2.2'
- }
+ _secondary_zones = {"example.org": "192.0.2.1", "example.com": "192.0.2.2"}
_zones = {}
@classmethod
def generateAuthZone(cls, confdir, zonename, zonecontent):
try:
- os.unlink(os.path.join(confdir, '%s.zone' % zonename))
+ os.unlink(os.path.join(confdir, "%s.zone" % zonename))
except Exception:
pass
@classmethod
def generateAuthConfig(cls, confdir):
super(TestProxyProtocolNOTIFY, cls).generateAuthConfig(confdir)
- if cls._backend == 'lmdb':
+ if cls._backend == "lmdb":
for zonename in cls._secondary_zones:
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'create-secondary-zone',
- zonename,
- cls._secondary_zones[zonename]]
-
- print(' '.join(pdnsutilCmd))
+ pdnsutilCmd = [
+ os.environ["PDNSUTIL"],
+ "--config-dir=%s" % confdir,
+ "create-secondary-zone",
+ zonename,
+ cls._secondary_zones[zonename],
+ ]
+
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
@classmethod
def generateAuthNamedConf(cls, confdir, zones):
- with open(os.path.join(confdir, 'named.conf'), 'w') as namedconf:
- namedconf.write("""
+ with open(os.path.join(confdir, "named.conf"), "w") as namedconf:
+ namedconf.write(
+ """
options {
directory "%s";
-};""" % confdir)
+};"""
+ % confdir
+ )
for zonename in cls._secondary_zones:
- zone = '.' if zonename == 'ROOT' else zonename
+ zone = "." if zonename == "ROOT" else zonename
- namedconf.write("""
+ namedconf.write(
+ """
zone "%s" {
type secondary;
file "%s.zone";
masters { %s; };
- };""" % (zone, zonename, cls._secondary_zones[zone]))
-
+ };"""
+ % (zone, zonename, cls._secondary_zones[zone])
+ )
@classmethod
def setUpClass(cls):
Check that NOTIFY is properly accepted/rejected based on the PROXY header inner address
"""
- query = dns.message.make_query('example.org', 'SOA')
+ query = dns.message.make_query("example.org", "SOA")
query.set_opcode(dns.opcode.NOTIFY)
queryPayload = query.to_wire()
- for task in ('192.0.2.1', dns.rcode.NOERROR), ('192.0.2.2', dns.rcode.REFUSED):
+ for task in ("192.0.2.1", dns.rcode.NOERROR), ("192.0.2.2", dns.rcode.REFUSED):
ip, expectedrcode = task
ppPayload = ProxyProtocol.getPayload(False, False, False, ip, "10.1.2.3", 12345, 53, [])
Check that AXFR is properly accepted/rejected based on the PROXY header inner address
"""
- query = dns.message.make_query('example.org', 'AXFR')
+ query = dns.message.make_query("example.org", "AXFR")
queryPayload = query.to_wire()
- for task in ('192.0.2.1', dns.rcode.NOTAUTH), ('127.0.0.1', dns.rcode.NOTAUTH), ('192.0.2.53', dns.rcode.NOERROR):
+ for task in (
+ ("192.0.2.1", dns.rcode.NOTAUTH),
+ ("127.0.0.1", dns.rcode.NOTAUTH),
+ ("192.0.2.53", dns.rcode.NOERROR),
+ ):
ip, expectedrcode = task
ppPayload = ProxyProtocol.getPayload(False, True, False, ip, "10.1.2.3", 12345, 53, [])
)
def impl_cname_only_test(self, qname, target):
- expected_cname = dns.rrset.from_text(
- qname, 0, dns.rdataclass.IN, "CNAME", target
- )
+ expected_cname = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "CNAME", target)
query = dns.message.make_query(qname, "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.impl_cname_only_test("cname-otherzone.example.org.", "target.example.com.")
def testCNAMESubZone(self):
- self.impl_cname_only_test(
- "cname-subzone.example.org.", "target.subzone.example.org."
- )
+ self.impl_cname_only_test("cname-subzone.example.org.", "target.subzone.example.org.")
class TestCrossZoneResolveOn(CrossZoneResolveBase):
)
def impl_cname_and_target_test(self, qname, target, target_ip):
- expected_cname = dns.rrset.from_text(
- qname, 0, dns.rdataclass.IN, "CNAME", target
- )
- expected_target = dns.rrset.from_text(
- target, 0, dns.rdataclass.IN, "A", target_ip
- )
+ expected_cname = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "CNAME", target)
+ expected_target = dns.rrset.from_text(target, 0, dns.rdataclass.IN, "A", target_ip)
query = dns.message.make_query(qname, "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
import os
import subprocess
+
class SVCBRecordsBase(AuthTest):
_config_template = """
svc-autohints
"""
_zones = {
- 'example.org': """
+ "example.org": """
example.org. 3600 IN SOA {soa}
example.org. 3600 IN NS ns1.example.org.
example.org. 3600 IN NS ns2.example.org.
}
def impl_testWithoutAlias(self):
- query = dns.message.make_query('www.example.org', 'HTTPS')
+ query = dns.message.make_query("www.example.org", "HTTPS")
res = self.sendUDPQuery(query)
expected_ans = dns.rrset.from_text(
- 'www.example.org.', 3600, dns.rdataclass.IN, 'HTTPS',
- '1 . ipv4hint="192.0.2.80" ipv6hint="2001:db8::80"'
+ "www.example.org.", 3600, dns.rdataclass.IN, "HTTPS", '1 . ipv4hint="192.0.2.80" ipv6hint="2001:db8::80"'
)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected_ans)
"""
Ensure additional processing happens for HTTPS AliasMode
"""
- query = dns.message.make_query('example.org', 'HTTPS')
+ query = dns.message.make_query("example.org", "HTTPS")
res = self.sendUDPQuery(query)
expected_addl = dns.rrset.from_text(
- 'www.example.org.', 3600, dns.rdataclass.IN, 'HTTPS',
- '1 . ipv4hint="192.0.2.80" ipv6hint="2001:db8::80"'
- )
- expected_ans = dns.rrset.from_text(
- 'example.org.', 3600, dns.rdataclass.IN, 'HTTPS',
- '0 www.example.org.'
+ "www.example.org.", 3600, dns.rdataclass.IN, "HTTPS", '1 . ipv4hint="192.0.2.80" ipv6hint="2001:db8::80"'
)
+ expected_ans = dns.rrset.from_text("example.org.", 3600, dns.rdataclass.IN, "HTTPS", "0 www.example.org.")
print(res.answer)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected_ans)
"""
Ensure PowerDNS removes the ipv4hint if there's no A record
"""
- query = dns.message.make_query('no-a.example.org', 'HTTPS')
+ query = dns.message.make_query("no-a.example.org", "HTTPS")
res = self.sendUDPQuery(query)
expected_ans = dns.rrset.from_text(
- 'no-a.example.org.', 3600, dns.rdataclass.IN, 'HTTPS',
- '1 . ipv6hint="2001:db8::81"'
+ "no-a.example.org.", 3600, dns.rdataclass.IN, "HTTPS", '1 . ipv6hint="2001:db8::81"'
)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected_ans)
"""
Ensure PowerDNS removes the ipv6hint if there's no AAAA record
"""
- query = dns.message.make_query('no-aaaa.example.org', 'HTTPS')
+ query = dns.message.make_query("no-aaaa.example.org", "HTTPS")
res = self.sendUDPQuery(query)
expected_ans = dns.rrset.from_text(
- 'no-aaaa.example.org.', 3600, dns.rdataclass.IN, 'HTTPS',
- '1 . ipv4hint="192.0.2.81"'
+ "no-aaaa.example.org.", 3600, dns.rdataclass.IN, "HTTPS", '1 . ipv4hint="192.0.2.81"'
)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected_ans)
"""
Ensure we send the actual hints, not generated ones
"""
- query = dns.message.make_query('no-auto.example.org', 'HTTPS')
+ query = dns.message.make_query("no-auto.example.org", "HTTPS")
res = self.sendUDPQuery(query)
expected_ans = dns.rrset.from_text(
- 'no-auto.example.org.', 3600, dns.rdataclass.IN, 'HTTPS',
- '1 . ipv4hint="192.0.2.81" ipv6hint="2001:db8::81"'
+ "no-auto.example.org.",
+ 3600,
+ dns.rdataclass.IN,
+ "HTTPS",
+ '1 . ipv4hint="192.0.2.81" ipv6hint="2001:db8::81"',
)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
print(res)
"""
Ensure we send a generated A hint, but keep the existing AAAA hint
"""
- query = dns.message.make_query('auto-a.example.org', 'HTTPS')
+ query = dns.message.make_query("auto-a.example.org", "HTTPS")
res = self.sendUDPQuery(query)
expected_ans = dns.rrset.from_text(
- 'auto-a.example.org.', 3600, dns.rdataclass.IN, 'HTTPS',
- '1 . ipv4hint="192.0.2.80" ipv6hint="2001:db8::81"'
+ "auto-a.example.org.", 3600, dns.rdataclass.IN, "HTTPS", '1 . ipv4hint="192.0.2.80" ipv6hint="2001:db8::81"'
)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
print(res)
"""
Ensure we send a generated AAAA hint, but keep the existing A hint
"""
- query = dns.message.make_query('auto-aaaa.example.org', 'HTTPS')
+ query = dns.message.make_query("auto-aaaa.example.org", "HTTPS")
res = self.sendUDPQuery(query)
expected_ans = dns.rrset.from_text(
- 'auto-aaaa.example.org.', 3600, dns.rdataclass.IN, 'HTTPS',
- '1 . ipv4hint="192.0.2.81" ipv6hint="2001:db8::80"'
+ "auto-aaaa.example.org.",
+ 3600,
+ dns.rdataclass.IN,
+ "HTTPS",
+ '1 . ipv4hint="192.0.2.81" ipv6hint="2001:db8::80"',
)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
print(res)
self.assertRRsetInAnswer(res, expected_ans)
self.assertEqual(len(res.additional), 2)
+
class TestSVCBRecordsBind(SVCBRecordsBase):
_backend = "bind"
"""
self.impl_testAutoAAAA()
+
class TestSVCBRecordsLMDB(SVCBRecordsBase):
- _backend='lmdb'
+ _backend = "lmdb"
_config_template = (
SVCBRecordsBase._config_template
cls.generateAuthConfig(confdir)
for zonename, zonecontent in cls._zones.items():
- cls.generateAuthZone(confdir,
- zonename,
- zonecontent)
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'load-zone',
- zonename,
- os.path.join(confdir, '%s.zone' % zonename)]
-
- print(' '.join(pdnsutilCmd))
+ cls.generateAuthZone(confdir, zonename, zonecontent)
+ pdnsutilCmd = [
+ os.environ["PDNSUTIL"],
+ "--config-dir=%s" % confdir,
+ "load-zone",
+ zonename,
+ os.path.join(confdir, "%s.zone" % zonename),
+ ]
+
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
def testWithoutAlias(self):
self.impl_testWithoutAlias()
from authtests import AuthTest
-class BadXFRServer(object):
+class BadXFRServer(object):
def __init__(self, port):
self._currentSerial = 0
self._targetSerial = 1
self._serverPort = port
- listener = threading.Thread(name='XFR Listener', target=self._listener, args=[])
+ listener = threading.Thread(name="XFR Listener", target=self._listener, args=[])
listener.setDaemon(True)
listener.start()
if newSerial == self._currentSerial or newSerial == self._targetSerial:
return False
- #if newSerial != self._currentSerial + 1:
+ # if newSerial != self._currentSerial + 1:
# raise AssertionError("Asking the XFR server to serve serial %d, already serving %d" % (newSerial, self._currentSerial))
self._targetSerial = newSerial
print("moveToSerial %d" % newSerial, file=sys.stderr)
if message.question[0].rdtype == dns.rdatatype.AXFR:
if self._currentSerial != 0:
- print('Received an AXFR query but IXFR expected because the current serial is %d' % (self._currentSerial))
+ print(
+ "Received an AXFR query but IXFR expected because the current serial is %d" % (self._currentSerial)
+ )
return (None, self._currentSerial)
newSerial = self._targetSerial
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('a.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("a.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif message.question[0].rdtype == dns.rdatatype.IXFR:
oldSerial = message.authority[0][0].serial
newSerial = self._targetSerial
if newSerial == 2:
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % oldSerial,
+ ),
# no deletion
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('b.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("b.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ ]
elif newSerial == 3:
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('a.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("a.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ ]
response.answer = records
return (newSerial, response)
message = dns.message.from_wire(data)
if len(message.question) != 1:
- print('Invalid query, qdcount is %d' % (len(message.question)), file=sys.stderr)
+ print("Invalid query, qdcount is %d" % (len(message.question)), file=sys.stderr)
break
if not message.question[0].rdtype in [dns.rdatatype.AXFR, dns.rdatatype.IXFR]:
- print('Invalid query, qtype is %d' % (message.question.rdtype), file=sys.stderr)
+ print("Invalid query, qtype is %d" % (message.question.rdtype), file=sys.stderr)
break
print(message, file=sys.stderr)
(serial, answer) = self._getAnswer(message)
if not answer:
- print('Unable to get a response for %s %d' % (message.question[0].name, message.question[0].rdtype), file=sys.stderr)
+ print(
+ "Unable to get a response for %s %d" % (message.question[0].name, message.question[0].rdtype),
+ file=sys.stderr,
+ )
break
wire = answer.to_wire()
try:
(conn, _) = sock.accept()
print("New connection", file=sys.stderr)
- thread = threading.Thread(name='IXFR Connection Handler',
- target=self._connectionHandler,
- args=[conn])
+ thread = threading.Thread(name="IXFR Connection Handler", target=self._connectionHandler, args=[conn])
thread.setDaemon(True)
thread.start()
except socket.error as e:
- print('Error in IXFR socket: %s' % str(e))
+ print("Error in IXFR socket: %s" % str(e))
sock.close()
+
badxfrServerPort = 4251
badxfrServer = BadXFRServer(badxfrServerPort)
+
class XFRIncompleteAuthTest(AuthTest):
"""
This test makes sure that we correctly detect incomplete RPZ zones via AXFR then IXFR
global badxfrServerPort
- _backend = 'gsqlite3'
+ _backend = "gsqlite3"
_config_template = """
launch=gsqlite3
@classmethod
def setUpClass(cls):
super(XFRIncompleteAuthTest, cls).setUpClass()
- os.system("$PDNSUTIL --config-dir=configs/auth create-secondary-zone zone.rpz. 127.0.0.1:%s" % (badxfrServerPort,))
+ os.system(
+ "$PDNSUTIL --config-dir=configs/auth create-secondary-zone zone.rpz. 127.0.0.1:%s" % (badxfrServerPort,)
+ )
os.system("$PDNSUTIL --config-dir=configs/auth set-meta zone.rpz. IXFR 1")
-
+
def waitUntilCorrectSerialIsLoaded(self, serial, timeout=20):
global badxfrServer
if currentSerial > serial:
raise AssertionError("Expected serial %d, got %d" % (serial, currentSerial))
if currentSerial == serial:
- badxfrServer.moveToSerial(serial+1)
+ badxfrServer.moveToSerial(serial + 1)
return
attempts = attempts + 1
time.sleep(1)
- raise AssertionError("Waited %d seconds for the serial to be updated to %d but the serial is still %d" % (timeout, serial, currentSerial))
+ raise AssertionError(
+ "Waited %d seconds for the serial to be updated to %d but the serial is still %d"
+ % (timeout, serial, currentSerial)
+ )
def checkZone(self):
- query = dns.message.make_query('zone.rpz.', 'SOA')
- res = self.sendUDPQuery(query) # , count=len(expected))
-
- expected = [dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. 1 3600 3600 3600 1')]
+ query = dns.message.make_query("zone.rpz.", "SOA")
+ res = self.sendUDPQuery(query) # , count=len(expected))
+
+ expected = [
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. 1 3600 3600 3600 1",
+ )
+ ]
self.assertEqual(res.answer, expected)
def doRetrieve(self):
import requests
from authtests import AuthTest
+
class TestBasic(AuthTest):
_config_template = """
launch = {backend}
super(TestBasic, cls).setUpClass()
def test_basic(self):
- r = requests.get('http://127.0.0.1:8053')
+ r = requests.get("http://127.0.0.1:8053")
self.assertEqual(r.status_code, 200)
+
class TestDualStack(AuthTest):
_config_template = """
launch = {backend}
super(TestDualStack, cls).setUpClass()
def test_ds(self):
- r = requests.get('http://127.0.0.1:8053')
+ r = requests.get("http://127.0.0.1:8053")
self.assertEqual(r.status_code, 200)
+
class TestDualStackBackwardsCompat(AuthTest):
_config_template = """
launch = {backend}
"""
def test_ds_compat(self):
- r = requests.get('http://127.0.0.1:8053')
+ r = requests.get("http://127.0.0.1:8053")
self.assertEqual(r.status_code, 200)
+
class TestUnauthorized(AuthTest):
_config_template = """
launch = {backend}
def test_unauthorized(self):
try:
- requests.get('http://127.0.0.1:8053')
+ requests.get("http://127.0.0.1:8053")
self.fail()
except requests.exceptions.ConnectionError:
pass
+
class TestUnauthorizedDualStack(AuthTest):
_config_template = """
launch = {backend}
def test_unauthorized(self):
try:
- requests.get('http://127.0.0.1:8053')
+ requests.get("http://127.0.0.1:8053")
self.fail()
except requests.exceptions.ConnectionError:
pass
-
import socket
import struct
+
class ProxyProtocol(object):
- MAGIC = b'\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A'
+ MAGIC = b"\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a"
# Header is magic + versioncommand (1) + family (1) + content length (2)
HEADER_SIZE = len(MAGIC) + 1 + 1 + 2
PORT_SIZE = 2
if len(data) < self.HEADER_SIZE:
return False
- if data[:len(self.MAGIC)] != self.MAGIC:
+ if data[: len(self.MAGIC)] != self.MAGIC:
return False
- value = struct.unpack('!B', bytes(bytearray([data[12]])))[0]
+ value = struct.unpack("!B", bytes(bytearray([data[12]])))[0]
self.version = value >> 4
if self.version != 0x02:
return False
if self.command == 0x00:
self.local = True
elif self.command == 0x01:
- value = struct.unpack('!B', bytes(bytearray([data[13]])))[0]
+ value = struct.unpack("!B", bytes(bytearray([data[13]])))[0]
self.family = value >> 4
if self.family == 0x01:
self.addrSize = 4
value = None
if self.family == 0x01:
- value = socket.inet_ntop(socket.AF_INET, data[self.offset:self.offset + self.addrSize])
+ value = socket.inet_ntop(socket.AF_INET, data[self.offset : self.offset + self.addrSize])
else:
- value = socket.inet_ntop(socket.AF_INET6, data[self.offset:self.offset + self.addrSize])
+ value = socket.inet_ntop(socket.AF_INET6, data[self.offset : self.offset + self.addrSize])
self.offset = self.offset + self.addrSize
return value
if len(data) < (self.consumed() + self.PORT_SIZE):
return False
- value = struct.unpack('!H', data[self.offset:self.offset + self.PORT_SIZE])[0]
+ value = struct.unpack("!H", data[self.offset : self.offset + self.PORT_SIZE])[0]
self.offset = self.offset + self.PORT_SIZE
return value
while remaining >= 3:
valueType = struct.unpack("!B", bytes(bytearray([data[self.offset]])))[0]
self.offset = self.offset + 1
- valueLen = struct.unpack("!H", data[self.offset:self.offset+2])[0]
+ valueLen = struct.unpack("!H", data[self.offset : self.offset + 2])[0]
self.offset = self.offset + 2
remaining = remaining - 3
if valueLen > 0:
if valueLen > remaining:
return False
- self.values.append([valueType, data[self.offset:self.offset+valueLen]])
+ self.values.append([valueType, data[self.offset : self.offset + valueLen]])
self.offset = self.offset + valueLen
remaining = remaining - valueLen
else:
command = 0x01
- value = struct.pack('!B', (version << 4) + command)
+ value = struct.pack("!B", (version << 4) + command)
payload = payload + value
addrSize = 0
family = 0x02
addrSize = 16
- value = struct.pack('!B', (family << 4) + protocol)
+ value = struct.pack("!B", (family << 4) + protocol)
payload = payload + value
contentSize = 0
if not local:
- contentSize = contentSize + addrSize * 2 + cls.PORT_SIZE *2
+ contentSize = contentSize + addrSize * 2 + cls.PORT_SIZE * 2
valuesSize = 0
for value in values:
contentSize = contentSize + valuesSize
- value = struct.pack('!H', contentSize)
- payload = payload + value
+ value = struct.pack("!H", contentSize)
+ payload = payload + value
if not local:
if family == 0x01:
payload = payload + value
value = socket.inet_pton(af, destination)
payload = payload + value
- value = struct.pack('!H', sourcePort)
+ value = struct.pack("!H", sourcePort)
payload = payload + value
- value = struct.pack('!H', destinationPort)
+ value = struct.pack("!H", destinationPort)
payload = payload + value
for value in values:
- valueType = struct.pack('!B', value[0])
- valueLen = struct.pack('!H', len(value[1]))
+ valueType = struct.pack("!B", value[0])
+ valueLen = struct.pack("!H", len(value[1]))
payload = payload + valueType + valueLen + value[1]
return payload
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-""" Class to implement draft-ietf-dnsop-edns-client-subnet (previously known as
+"""Class to implement draft-ietf-dnsop-edns-client-subnet (previously known as
draft-vandergaast-edns-client-subnet.
The contained class supports both IPv4 and IPv6 addresses.
Requirements:
dnspython (http://www.dnspython.org/)
"""
+
from __future__ import print_function
from __future__ import division
n = socket.inet_pton(family, ip)
if family == socket.AF_INET6:
f = FAMILY_IPV6
- hi, lo = struct.unpack('!QQ', n)
+ hi, lo = struct.unpack("!QQ", n)
ip = hi << 64 | lo
elif family == socket.AF_INET:
f = FAMILY_IPV4
- ip = struct.unpack('!L', n)[0]
+ ip = struct.unpack("!L", n)[0]
except Exception:
pass
ip = self.ip >> bits - self.mask
- if (self.mask % 8 != 0):
+ if self.mask % 8 != 0:
ip = ip << 8 - (self.mask % 8)
return ip
def is_draft(self):
- """" Determines whether this instance is using the draft option code """
+ """ " Determines whether this instance is using the draft option code"""
return self.option == DRAFT_OPTION_CODE
def to_wire(self, file=None):
mask_bits = self.mask
if mask_bits % 8 != 0:
- mask_bits += 8 - (self.mask % 8)
+ mask_bits += 8 - (self.mask % 8)
if self.family == FAMILY_IPV4:
test = struct.pack("!L", ip)
elif self.family == FAMILY_IPV6:
- test = struct.pack("!QQ", ip >> 64, ip & (2 ** 64 - 1))
- test = test[-(mask_bits // 8):]
+ test = struct.pack("!QQ", ip >> 64, ip & (2**64 - 1))
+ test = test[-(mask_bits // 8) :]
format = "!HBB%ds" % (mask_bits // 8)
data = struct.pack(format, self.family, self.mask, self.scope, test)
An instance of ClientSubnetOption based on the ENDS packet
"""
- data = wire[current:current + olen]
+ data = wire[current : current + olen]
(family, mask, scope) = struct.unpack("!HBB", data[:4])
c_mask = mask
ip = struct.unpack_from("!%ds" % (c_mask // 8), data, 4)[0]
- if (family == FAMILY_IPV4):
- ip = ip + b'\0' * ((32 - c_mask) // 8)
+ if family == FAMILY_IPV4:
+ ip = ip + b"\0" * ((32 - c_mask) // 8)
ip = socket.inet_ntop(socket.AF_INET, ip)
- elif (family == FAMILY_IPV6):
- ip = ip + b'\0' * ((128 - c_mask) // 8)
+ elif family == FAMILY_IPV6:
+ ip = ip + b"\0" * ((128 - c_mask) // 8)
ip = socket.inet_ntop(socket.AF_INET6, ip)
else:
raise Exception("Returned a family other then IPv4 or IPv6")
# needed in 2.0.0..
@classmethod
def from_wire_parser(cls, otype, parser):
- family, src, scope = parser.get_struct('!HBB')
+ family, src, scope = parser.get_struct("!HBB")
addrlen = int(math.ceil(src / 8.0))
prefix = parser.get_bytes(addrlen)
if family == 1:
pad = 4 - addrlen
- addr = dns.ipv4.inet_ntoa(prefix + b'\x00' * pad)
+ addr = dns.ipv4.inet_ntoa(prefix + b"\x00" * pad)
elif family == 2:
pad = 16 - addrlen
- addr = dns.ipv6.inet_ntoa(prefix + b'\x00' * pad)
+ addr = dns.ipv6.inet_ntoa(prefix + b"\x00" * pad)
else:
- raise ValueError('unsupported family')
+ raise ValueError("unsupported family")
return cls(addr, src, scope, otype)
def __repr__(self):
if self.family == FAMILY_IPV4:
- ip = socket.inet_ntop(socket.AF_INET, struct.pack('!L', self.ip))
+ ip = socket.inet_ntop(socket.AF_INET, struct.pack("!L", self.ip))
elif self.family == FAMILY_IPV6:
- ip = socket.inet_ntop(socket.AF_INET6,
- struct.pack('!QQ',
- self.ip >> 64,
- self.ip & (2 ** 64 - 1)))
-
- return "%s(%s, %s, %s)" % (
- self.__class__.__name__,
- ip,
- self.mask,
- self.scope
- )
+ ip = socket.inet_ntop(socket.AF_INET6, struct.pack("!QQ", self.ip >> 64, self.ip & (2**64 - 1)))
+
+ return "%s(%s, %s, %s)" % (self.__class__.__name__, ip, self.mask, self.scope)
def to_text(self):
return self.__repr__()
print("Found ClientSubnetOption...", end=None, file=sys.stderr)
if not cso.family == options.family:
error = True
- print("\nFailed: returned family (%d) is different from the passed family (%d)" % (options.family, cso.family), file=sys.stderr)
+ print(
+ "\nFailed: returned family (%d) is different from the passed family (%d)"
+ % (options.family, cso.family),
+ file=sys.stderr,
+ )
if not cso.calculate_ip() == options.calculate_ip():
error = True
- print("\nFailed: returned ip (%s) is different from the passed ip (%s)." % (options.calculate_ip(), cso.calculate_ip()), file=sys.stderr)
+ print(
+ "\nFailed: returned ip (%s) is different from the passed ip (%s)."
+ % (options.calculate_ip(), cso.calculate_ip()),
+ file=sys.stderr,
+ )
if not options.mask == cso.mask:
error = True
- print("\nFailed: returned mask bits (%d) is different from the passed mask bits (%d)" % (options.mask, cso.mask), file=sys.stderr)
+ print(
+ "\nFailed: returned mask bits (%d) is different from the passed mask bits (%d)"
+ % (options.mask, cso.mask),
+ file=sys.stderr,
+ )
if not options.scope != 0:
print("\nWarning: scope indicates edns-clientsubnet data is not used", file=sys.stderr)
if options.is_draft():
else:
print("Failed: No ClientSubnetOption returned", file=sys.stderr)
- parser = argparse.ArgumentParser(description='draft-vandergaast-edns-client-subnet-01 tester')
- parser.add_argument('nameserver', help='The nameserver to test')
- parser.add_argument('rr', help='DNS record that should return an EDNS enabled response')
- parser.add_argument('-s', '--subnet', help='Specifies an IP to pass as the client subnet.', default='192.0.2.0')
- parser.add_argument('-m', '--mask', type=int, help='CIDR mask to use for subnet')
- parser.add_argument('--timeout', type=int, help='Set the timeout for query to TIMEOUT seconds, default=10', default=10)
- parser.add_argument('-t', '--type', help='DNS query type, default=A', default='A')
+ parser = argparse.ArgumentParser(description="draft-vandergaast-edns-client-subnet-01 tester")
+ parser.add_argument("nameserver", help="The nameserver to test")
+ parser.add_argument("rr", help="DNS record that should return an EDNS enabled response")
+ parser.add_argument("-s", "--subnet", help="Specifies an IP to pass as the client subnet.", default="192.0.2.0")
+ parser.add_argument("-m", "--mask", type=int, help="CIDR mask to use for subnet")
+ parser.add_argument(
+ "--timeout", type=int, help="Set the timeout for query to TIMEOUT seconds, default=10", default=10
+ )
+ parser.add_argument("-t", "--type", help="DNS query type, default=A", default="A")
args = parser.parse_args()
if not args.mask:
- if ':' in args.subnet:
+ if ":" in args.subnet:
args.mask = 48
else:
args.mask = 24
import dns.message
import dns.query
+
class CookiesOption(dns.edns.Option):
- """Implementation of draft-ietf-dnsop-cookies-09.
- """
+ """Implementation of draft-ietf-dnsop-cookies-09."""
def __init__(self, client, server):
super(CookiesOption, self).__init__(10)
if len(client) != 8:
- raise Exception('invalid client cookie length')
+ raise Exception("invalid client cookie length")
if server is not None and len(server) != 0 and (len(server) < 8 or len(server) > 32):
- raise Exception('invalid server cookie length')
+ raise Exception("invalid server cookie length")
self.client = client
self.server = server
An instance of CookiesOption based on the EDNS packet
"""
- data = wire[current:current + olen]
+ data = wire[current : current + olen]
if len(data) != 8 and (len(data) < 16 or len(data) > 40):
- raise Exception('Invalid EDNS Cookies option')
+ raise Exception("Invalid EDNS Cookies option")
client = data[:8]
if len(data) > 8:
data = parser.get_remaining()
if len(data) != 8 and (len(data) < 16 or len(data) > 40):
- raise Exception('Invalid EDNS Cookies option')
+ raise Exception("Invalid EDNS Cookies option")
client = data[:8]
if len(data) > 8:
return cls(client, server)
def __repr__(self):
- return '%s(%s, %s)' % (
- self.__class__.__name__,
- self.client,
- self.server
- )
+ return "%s(%s, %s)" % (self.__class__.__name__, self.client, self.server)
def to_text(self):
return self.__repr__()
import binascii
from builtins import bytes
+
class DNSCryptResolverCertificate(object):
- DNSCRYPT_CERT_MAGIC = b'\x44\x4e\x53\x43'
- DNSCRYPT_ES_VERSION = b'\x00\x01'
- DNSCRYPT_PROTOCOL_MIN_VERSION = b'\x00\x00'
+ DNSCRYPT_CERT_MAGIC = b"\x44\x4e\x53\x43"
+ DNSCRYPT_ES_VERSION = b"\x00\x01"
+ DNSCRYPT_PROTOCOL_MIN_VERSION = b"\x00\x00"
def __init__(self, serial, validFrom, validUntil, publicKey, clientMagic):
self.serial = serial
esVersion = binary[4:6]
protocolMinVersion = binary[6:8]
- if certMagic != DNSCryptResolverCertificate.DNSCRYPT_CERT_MAGIC or esVersion != DNSCryptResolverCertificate.DNSCRYPT_ES_VERSION or protocolMinVersion != DNSCryptResolverCertificate.DNSCRYPT_PROTOCOL_MIN_VERSION:
+ if (
+ certMagic != DNSCryptResolverCertificate.DNSCRYPT_CERT_MAGIC
+ or esVersion != DNSCryptResolverCertificate.DNSCRYPT_ES_VERSION
+ or protocolMinVersion != DNSCryptResolverCertificate.DNSCRYPT_PROTOCOL_MIN_VERSION
+ ):
raise Exception("Invalid binary certificate")
orig = libnacl.crypto_sign_open(binary[8:124], providerFP)
validUntil = struct.unpack_from("!I", orig[48:52])[0]
return DNSCryptResolverCertificate(serial, validFrom, validUntil, resolverPK, clientMagic)
+
class DNSCryptClient(object):
DNSCRYPT_NONCE_SIZE = 24
DNSCRYPT_MAC_SIZE = 16
DNSCRYPT_PADDED_BLOCK_SIZE = 64
DNSCRYPT_MIN_UDP_LENGTH = 256
- DNSCRYPT_RESOLVER_MAGIC = b'\x72\x36\x66\x6e\x76\x57\x6a\x38'
+ DNSCRYPT_RESOLVER_MAGIC = b"\x72\x36\x66\x6e\x76\x57\x6a\x38"
@staticmethod
def _addrToSocketType(addr):
def __init__(self, providerName, providerFingerprint, resolverAddress, resolverPort=443, timeout=2):
self._providerName = providerName
- self._providerFingerprint = binascii.unhexlify(providerFingerprint.lower().replace(':', ''))
+ self._providerFingerprint = binascii.unhexlify(providerFingerprint.lower().replace(":", ""))
self._resolverAddress = resolverAddress
self._resolverPort = resolverPort
self._resolverCertificates = []
@staticmethod
def _generateNonce():
nonce = libnacl.utils.rand_nonce()
- return nonce[:int(DNSCryptClient.DNSCRYPT_NONCE_SIZE / 2)]
+ return nonce[: int(DNSCryptClient.DNSCRYPT_NONCE_SIZE / 2)]
def _encryptQuery(self, queryContent, resolverCert, nonce, tcp=False):
header = resolverCert.clientMagic + self._publicKey + nonce
paddingSize += self.DNSCRYPT_MIN_UDP_LENGTH - requiredSize
requiredSize = self.DNSCRYPT_MIN_UDP_LENGTH
- padding = b'\x80'
+ padding = b"\x80"
idx = 0
while idx < (paddingSize - 1):
- padding = padding + b'\x00'
+ padding = padding + b"\x00"
idx += 1
data = queryContent + padding
- nonce = nonce + (b'\x00'*int(self.DNSCRYPT_NONCE_SIZE / 2))
+ nonce = nonce + (b"\x00" * int(self.DNSCRYPT_NONCE_SIZE / 2))
box = libnacl.crypto_box(data, nonce, resolverCert.publicKey, self._privateKey)
return header + box
raise Exception("Invalid encrypted response: bad resolver magic")
nonce = encryptedResponse[8:32]
- if nonce[0:int(self.DNSCRYPT_NONCE_SIZE / 2)] != clientNonce:
+ if nonce[0 : int(self.DNSCRYPT_NONCE_SIZE / 2)] != clientNonce:
raise Exception("Invalid encrypted response: bad nonce")
cleartext = libnacl.crypto_box_open(encryptedResponse[32:], nonce, resolverCert.publicKey, self._privateKey)
idx -= 1
- return cleartext[:idx+1]
+ return cleartext[: idx + 1]
def query(self, queryContent, tcp=False):
_maintenanceWaitTime = 2
+
def waitForMaintenanceToRun():
time.sleep(_maintenanceWaitTime)
-class DynBlocksTest(DNSDistTest):
+class DynBlocksTest(DNSDistTest):
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKey = 'apisecret'
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
+ _webServerBasicAuthPassword = "secret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKey = "apisecret"
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
_dynBlockQPS = 10
_dynBlockANYQPS = 10
_dynBlockPeriod = 2
# this needs to be greater than maintenanceWaitTime
_dynBlockDuration = _maintenanceWaitTime + 2
- _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _config_params = ["_dynBlockQPS", "_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
def doTestDynBlockViaAPI(self, ipRange, reason, minSeconds, maxSeconds, minBlocks, maxBlocks, ebpf=False):
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/jsonstat?command=dynblocklist'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/jsonstat?command=dynblocklist"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertIn(ipRange, content)
values = content[ipRange]
- for key in ['reason', 'seconds', 'blocks', 'action', 'ebpf']:
+ for key in ["reason", "seconds", "blocks", "action", "ebpf"]:
self.assertIn(key, values)
- self.assertEqual(values['reason'], reason)
- self.assertGreaterEqual(values['seconds'], minSeconds)
- self.assertLessEqual(values['seconds'], maxSeconds)
- self.assertGreaterEqual(values['blocks'], minBlocks)
- self.assertLessEqual(values['blocks'], maxBlocks)
- self.assertEqual(values['ebpf'], True if ebpf else False)
+ self.assertEqual(values["reason"], reason)
+ self.assertGreaterEqual(values["seconds"], minSeconds)
+ self.assertLessEqual(values["seconds"], maxSeconds)
+ self.assertGreaterEqual(values["blocks"], minBlocks)
+ self.assertLessEqual(values["blocks"], maxBlocks)
+ self.assertEqual(values["ebpf"], True if ebpf else False)
def doTestQRate(self, name, testViaAPI=True, ebpf=False):
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
allowed = 0
self.assertEqual(receivedResponse, None)
if testViaAPI:
- self.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', 1, self._dynBlockDuration, (sent-allowed)+1, (sent-allowed)+1, ebpf)
+ self.doTestDynBlockViaAPI(
+ "127.0.0.1/32",
+ "Exceeded query rate",
+ 1,
+ self._dynBlockDuration,
+ (sent - allowed) + 1,
+ (sent - allowed) + 1,
+ ebpf,
+ )
# wait until we are not blocked anymore
time.sleep(self._dynBlockDuration + self._dynBlockPeriod)
self.assertEqual(response, receivedResponse)
def doTestQTypeRate(self, name):
- query = dns.message.make_query(name, 'ANY', 'IN')
+ query = dns.message.make_query(name, "ANY", "IN")
response = dns.message.make_response(query)
blockedResponse = dns.message.make_response(query)
blockedResponse.set_rcode(dns.rcode.REFUSED)
self.assertEqual(response, receivedResponse)
def doTestQRateRCode(self, name, rcode):
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(rcode)
self.assertEqual(response, receivedResponse)
def doTestResponseByteRate(self, name, dynBlockBytesPerSecond):
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text_list(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- ['192.0.2.1', '192.0.2.2', '192.0.2.3', '192.0.2.4']))
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1'))
+ response.answer.append(
+ dns.rrset.from_text_list(
+ name, 60, dns.rdataclass.IN, dns.rdatatype.A, ["192.0.2.1", "192.0.2.2", "192.0.2.3", "192.0.2.4"]
+ )
+ )
+ response.answer.append(dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1"))
allowed = 0
sent = 0
self.assertGreaterEqual(allowed, dynBlockBytesPerSecond)
print(self.sendConsoleCommand("showDynBlocks()"))
- print(self.sendConsoleCommand("grepq(\"\")"))
+ print(self.sendConsoleCommand('grepq("")'))
print(time.time())
if allowed == sent:
waitForMaintenanceToRun()
print(self.sendConsoleCommand("showDynBlocks()"))
- print(self.sendConsoleCommand("grepq(\"\")"))
+ print(self.sendConsoleCommand('grepq("")'))
print(time.time())
# we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
self.assertEqual(receivedResponse, None)
print(self.sendConsoleCommand("showDynBlocks()"))
- print(self.sendConsoleCommand("grepq(\"\")"))
+ print(self.sendConsoleCommand('grepq("")'))
print(time.time())
# wait until we are not blocked anymore
time.sleep(self._dynBlockDuration + self._dynBlockPeriod)
print(self.sendConsoleCommand("showDynBlocks()"))
- print(self.sendConsoleCommand("grepq(\"\")"))
+ print(self.sendConsoleCommand('grepq("")'))
print(time.time())
# this one should succeed
self.assertEqual(response, receivedResponse)
def doTestRCodeRate(self, name, rcode):
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(rcode)
def doTestRCodeRatioViaProtocol(self, name, rcode, noerrorcount, rcodecount, method, cached=False):
sender = getattr(self, method)
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
- rcodeQuery = dns.message.make_query('rcode-' + name, 'A', 'IN')
+ rcodeQuery = dns.message.make_query("rcode-" + name, "A", "IN")
expectedResponse = dns.message.make_response(rcodeQuery)
expectedResponse.set_rcode(rcode)
firstQuery = True
# start with normal responses
- for _ in range(noerrorcount-1):
+ for _ in range(noerrorcount - 1):
if cached and not firstQuery:
# should be a cache hit
(receivedQuery, receivedResponse) = sender(query, response=None, useQueue=False)
self.doTestRCodeRatioViaProtocol(name, rcode, noerrorcount, rcodecount, "sendTCPQuery")
def doTestCacheMissRatio(self, name, cacheHits, cacheMisses):
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
for idx in range(cacheMisses):
- query = dns.message.make_query(str(idx) + '.' + name, 'A', 'IN')
+ query = dns.message.make_query(str(idx) + "." + name, "A", "IN")
response = dns.message.make_response(query)
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
# let's clear the response queue
self.clearToResponderQueue()
- query = dns.message.make_query('0.' + name, 'A', 'IN')
+ query = dns.message.make_query("0." + name, "A", "IN")
response = dns.message.make_response(query)
response.answer.append(rrset)
for _ in range(cacheHits):
time.sleep(self._dynBlockDuration + self._dynBlockPeriod)
# this one should succeed
- query = dns.message.make_query(str(cacheMisses + 1) + name, 'A', 'IN')
+ query = dns.message.make_query(str(cacheMisses + 1) + name, "A", "IN")
response = dns.message.make_response(query)
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
from dnsdisttests import DNSDistTest
-@unittest.skipIf('SKIP_DOH_TESTS' in os.environ, 'DNS over HTTPS tests are disabled')
+@unittest.skipIf("SKIP_DOH_TESTS" in os.environ, "DNS over HTTPS tests are disabled")
class DNSDistDOHTest(DNSDistTest):
-
def getHeaderValue(self, name):
for header in self._response_headers.decode().splitlines(False):
- values = header.split(':')
+ values = header.split(":")
key = values[0]
if key.lower() == name.lower():
return values[1].strip()
def setUpClass(cls):
# for some reason, @unittest.skipIf() is not applied to derived classes with some versions of Python
- if 'SKIP_DOH_TESTS' in os.environ:
- raise unittest.SkipTest('DNS over HTTPS tests are disabled')
+ if "SKIP_DOH_TESTS" in os.environ:
+ raise unittest.SkipTest("DNS over HTTPS tests are disabled")
cls.startResponders()
cls.startDNSDist()
# Python2/3 compatibility hacks
try:
- from queue import Queue
+ from queue import Queue
except ImportError:
- from Queue import Queue
+ from Queue import Queue
try:
- range = xrange
+ range = xrange
except NameError:
- pass
+ pass
+
def getWorkerID():
- if not 'PYTEST_XDIST_WORKER' in os.environ:
- return 0
- workerName = os.environ['PYTEST_XDIST_WORKER']
+ if not "PYTEST_XDIST_WORKER" in os.environ:
+ return 0
+ workerName = os.environ["PYTEST_XDIST_WORKER"]
return int(workerName[2:])
+
workerPorts = {}
+
def pickAvailablePort():
global workerPorts
workerID = getWorkerID()
if workerID in workerPorts:
- port = workerPorts[workerID] + 1
+ port = workerPorts[workerID] + 1
else:
- port = 11000 + (workerID * 1000)
+ port = 11000 + (workerID * 1000)
workerPorts[workerID] = port
return port
+
class ResponderDropAction(object):
"""
An object to indicate a drop action shall be taken
"""
+
pass
+
class DNSDistTest(AssertEqualDNSMessageMixin, unittest.TestCase):
"""
Set up a dnsdist instance and responder threads.
from dnsdist on a separate queue, allowing the tests to check
that the queries sent from dnsdist were as expected.
"""
+
_dnsDistListeningAddr = "127.0.0.1"
_toResponderQueue = Queue()
_fromResponderQueue = Queue()
_responsesCounter = {}
_config_template = """
"""
- _config_params = ['_testServerPort']
+ _config_params = ["_testServerPort"]
_yaml_config_template = None
_yaml_config_params = []
- _acl = ['127.0.0.1/32']
+ _acl = ["127.0.0.1/32"]
_consoleKey = None
- _healthCheckName = 'a.root-servers.net.'
+ _healthCheckName = "a.root-servers.net."
_healthCheckCounter = 0
_answerUnexpected = True
_checkConfigExpectedOutput = None
return
except Exception as err:
if err.errno != errno.ECONNREFUSED:
- print(f'Error occurred: {try_number} {err}', file=sys.stderr)
+ print(f"Error occurred: {try_number} {err}", file=sys.stderr)
time.sleep(0.1)
- # We assume the dnsdist instance does not listen. That's fine.
+
+ # We assume the dnsdist instance does not listen. That's fine.
@classmethod
def startResponders(cls):
print("Launching responders..")
cls._testServerPort = pickAvailablePort()
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls.waitForTCPSocket("127.0.0.1", cls._testServerPort);
+ cls.waitForTCPSocket("127.0.0.1", cls._testServerPort)
@classmethod
def startDNSDist(cls):
print("Launching dnsdist..")
if cls._yaml_config_template:
- if 'SKIP_YAML_TESTS' in os.environ:
- raise unittest.SkipTest('YAML tests are disabled')
+ if "SKIP_YAML_TESTS" in os.environ:
+ raise unittest.SkipTest("YAML tests are disabled")
params = tuple([getattr(cls, param) for param in cls._yaml_config_params])
- confFile = os.path.join('configs', 'dnsdist_%s.yml' % (cls.__name__))
- with open(confFile, 'w') as conf:
+ confFile = os.path.join("configs", "dnsdist_%s.yml" % (cls.__name__))
+ with open(confFile, "w") as conf:
conf.write(cls._yaml_config_template % params)
conf.write("\nsecurity_polling:\n suffix: ''\n")
params = tuple([getattr(cls, param) for param in cls._config_params])
print(params)
- extension = 'lua' if cls._yaml_config_template else 'conf'
- luaConfFile = os.path.join('configs', 'dnsdist_%s.%s' % (cls.__name__, extension))
+ extension = "lua" if cls._yaml_config_template else "conf"
+ luaConfFile = os.path.join("configs", "dnsdist_%s.%s" % (cls.__name__, extension))
if not cls._yaml_config_template:
- confFile = luaConfFile
-
- if len(cls._config_template.strip(' \n\t')) > 0:
- with open(luaConfFile, 'w') as conf:
- conf.write("-- Autogenerated by dnsdisttests.py\n")
- conf.write(f"-- dnsdist will listen on {cls._dnsDistPort}\n")
- conf.write(cls._config_template % params)
- if not cls._yaml_config_template:
- conf.write("\n")
- conf.write("setSecurityPollSuffix('')")
+ confFile = luaConfFile
+
+ if len(cls._config_template.strip(" \n\t")) > 0:
+ with open(luaConfFile, "w") as conf:
+ conf.write("-- Autogenerated by dnsdisttests.py\n")
+ conf.write(f"-- dnsdist will listen on {cls._dnsDistPort}\n")
+ conf.write(cls._config_template % params)
+ if not cls._yaml_config_template:
+ conf.write("\n")
+ conf.write("setSecurityPollSuffix('')")
else:
- try:
- os.unlink(luaConfFile)
- except OSError:
- pass
+ try:
+ os.unlink(luaConfFile)
+ except OSError:
+ pass
if cls._skipListeningOnCL:
- dnsdistcmd = [os.environ['DNSDISTBIN'], '--supervised', '-C', confFile ]
+ dnsdistcmd = [os.environ["DNSDISTBIN"], "--supervised", "-C", confFile]
else:
- dnsdistcmd = [os.environ['DNSDISTBIN'], '--supervised', '-C', confFile,
- '-l', '%s:%d' % (cls._dnsDistListeningAddr, cls._dnsDistPort) ]
+ dnsdistcmd = [
+ os.environ["DNSDISTBIN"],
+ "--supervised",
+ "-C",
+ confFile,
+ "-l",
+ "%s:%d" % (cls._dnsDistListeningAddr, cls._dnsDistPort),
+ ]
if cls._verboseMode:
- dnsdistcmd.append('-v')
+ dnsdistcmd.append("-v")
- dnsdistcmd.append('--structured-logging')
- dnsdistcmd.append('true' if cls._enableStructuredLoggingOnCL else 'false')
+ dnsdistcmd.append("--structured-logging")
+ dnsdistcmd.append("true" if cls._enableStructuredLoggingOnCL else "false")
if cls._sudoMode:
- preserve_env_values = ['LD_LIBRARY_PATH', 'LLVM_PROFILE_FILE']
+ preserve_env_values = ["LD_LIBRARY_PATH", "LLVM_PROFILE_FILE"]
for value in preserve_env_values:
if value in os.environ:
- dnsdistcmd.insert(0, value + '=' + os.environ[value])
- dnsdistcmd.insert(0, 'sudo')
+ dnsdistcmd.insert(0, value + "=" + os.environ[value])
+ dnsdistcmd.insert(0, "sudo")
for acl in cls._acl:
- dnsdistcmd.extend(['--acl', acl])
- print(' '.join(dnsdistcmd))
+ dnsdistcmd.extend(["--acl", acl])
+ print(" ".join(dnsdistcmd))
# validate config with --check-config, which sets client=true, possibly exposing bugs.
- testcmd = dnsdistcmd + ['--check-config']
+ testcmd = dnsdistcmd + ["--check-config"]
try:
output = subprocess.check_output(testcmd, stderr=subprocess.STDOUT, close_fds=True)
except subprocess.CalledProcessError as exc:
- raise AssertionError('dnsdist --check-config failed (%d): %s' % (exc.returncode, exc.output))
+ raise AssertionError("dnsdist --check-config failed (%d): %s" % (exc.returncode, exc.output))
if cls._checkConfigExpectedOutputPrefix is not None:
if not output.startswith(cls._checkConfigExpectedOutputPrefix):
- raise AssertionError('dnsdist --check-config failed: %s (expected prefix %s)' % (output, cls._checkConfigExpectedOutputPrefix))
+ raise AssertionError(
+ "dnsdist --check-config failed: %s (expected prefix %s)"
+ % (output, cls._checkConfigExpectedOutputPrefix)
+ )
else:
if cls._checkConfigExpectedOutput is not None:
expectedOutput = cls._checkConfigExpectedOutput
if not cls._verboseMode and output != expectedOutput:
- raise AssertionError('dnsdist --check-config failed: %s (expected %s)' % (output, expectedOutput))
+ raise AssertionError("dnsdist --check-config failed: %s (expected %s)" % (output, expectedOutput))
elif not cls._verboseMode:
if cls._enableStructuredLoggingOnCL:
expectedPrefix = b'msg="Configuration OK" subsystem="setup" level="0" prio="Info" ts="'
if not output.startswith(expectedPrefix):
- raise AssertionError('dnsdist --check-config failed: %s (expected prefix %s)' % (output, expectedPrefix))
+ raise AssertionError(
+ "dnsdist --check-config failed: %s (expected prefix %s)" % (output, expectedPrefix)
+ )
else:
- expectedOutput = ('Configuration \'%s\' OK!\n' % (confFile)).encode()
+ expectedOutput = ("Configuration '%s' OK!\n" % (confFile)).encode()
if output != expectedOutput:
- raise AssertionError('dnsdist --check-config failed: %s (expected %s)' % (output, expectedOutput))
+ raise AssertionError(
+ "dnsdist --check-config failed: %s (expected %s)" % (output, expectedOutput)
+ )
- logFile = os.path.join('configs', 'dnsdist_%s.log' % (cls.__name__))
- with open(logFile, 'w') as fdLog:
- cls._dnsdist = subprocess.Popen(dnsdistcmd, close_fds=True, stdout=fdLog, stderr=fdLog)
+ logFile = os.path.join("configs", "dnsdist_%s.log" % (cls.__name__))
+ with open(logFile, "w") as fdLog:
+ cls._dnsdist = subprocess.Popen(dnsdistcmd, close_fds=True, stdout=fdLog, stderr=fdLog)
if cls._alternateListeningAddr and cls._alternateListeningPort:
cls.waitForTCPSocket(cls._alternateListeningAddr, cls._alternateListeningPort)
if cls._dnsdist.poll() is not None:
print(f"\n*** startDNSDist log for {logFile} ***")
- with open(logFile, 'r') as fdLog:
+ with open(logFile, "r") as fdLog:
print(fdLog.read())
print(f"*** End startDNSDist log for {logFile} ***")
- raise AssertionError('%s failed (%d)' % (dnsdistcmd, cls._dnsdist.returncode))
+ raise AssertionError("%s failed (%d)" % (dnsdistcmd, cls._dnsdist.returncode))
time.sleep(cls._extraStartupSleep)
@classmethod
p.kill()
p.wait()
if p.returncode != 0:
- if p.returncode < 0:
- raise AssertionError('Process was killed by signal %d' % (-p.returncode))
- else:
- raise AssertionError('Process exited with return code %d' % (p.returncode))
+ if p.returncode < 0:
+ raise AssertionError("Process was killed by signal %d" % (-p.returncode))
+ else:
+ raise AssertionError("Process exited with return code %d" % (p.returncode))
except OSError as e:
# There is a race-condition with the poll() and
# kill() statements, when the process is dead on the
toQueue.put(request, True, cls._queueTimeout)
response = fromQueue.get(True, cls._queueTimeout)
if response:
- response = copy.copy(response)
- response.id = request.id
+ response = copy.copy(response)
+ response.id = request.id
if synthesize is not None:
- response = dns.message.make_response(request)
- response.set_rcode(synthesize)
+ response = dns.message.make_response(request)
+ response.set_rcode(synthesize)
if not response:
if cls._answerUnexpected:
sock.settimeout(0.5)
while True:
try:
- data, addr = sock.recvfrom(4096)
+ data, addr = sock.recvfrom(4096)
except socket.timeout:
- if cls._backgroundThreads.get(threading.get_native_id(), False) == False:
- del cls._backgroundThreads[threading.get_native_id()]
- break
- else:
- continue
+ if cls._backgroundThreads.get(threading.get_native_id(), False) == False:
+ del cls._backgroundThreads[threading.get_native_id()]
+ break
+ else:
+ continue
forceRcode = None
try:
request = dns.message.from_wire(data, ignore_trailing=ignoreTrailing)
except dns.message.TrailingJunk as e:
- print('trailing data exception in UDPResponder')
+ print("trailing data exception in UDPResponder")
if trailingDataResponse is False or forceRcode is True:
raise
print("UDP query with trailing data, synthesizing response")
wire = None
if callback:
- wire = callback(request)
+ wire = callback(request)
else:
- if request.edns > 1:
- forceRcode = dns.rcode.BADVERS
- response = cls._getResponse(request, fromQueue, toQueue, synthesize=forceRcode)
- if response:
- wire = response.to_wire()
+ if request.edns > 1:
+ forceRcode = dns.rcode.BADVERS
+ response = cls._getResponse(request, fromQueue, toQueue, synthesize=forceRcode)
+ if response:
+ wire = response.to_wire()
if not wire:
- continue
+ continue
elif isinstance(wire, ResponderDropAction):
- continue
+ continue
sock.sendto(wire, addr)
sock.close()
@classmethod
- def handleTCPConnection(cls, conn, fromQueue, toQueue, trailingDataResponse=False, multipleResponses=False, callback=None, partialWrite=False):
- ignoreTrailing = trailingDataResponse is True
- try:
- data = conn.recv(2)
- except Exception as err:
- data = None
- print(f'Error while reading query size in TCP responder thread {err=}, {type(err)=}')
- if not data:
- conn.close()
- return
-
- (datalen,) = struct.unpack("!H", data)
- data = conn.recv(datalen)
- forceRcode = None
- try:
- request = dns.message.from_wire(data, ignore_trailing=ignoreTrailing)
- except dns.message.TrailingJunk as e:
- if trailingDataResponse is False or forceRcode is True:
- raise
- print("TCP query with trailing data, synthesizing response")
- request = dns.message.from_wire(data, ignore_trailing=True)
- forceRcode = trailingDataResponse
-
- if callback:
- wire = callback(request)
- else:
- if request.edns > 1:
- forceRcode = dns.rcode.BADVERS
- response = cls._getResponse(request, fromQueue, toQueue, synthesize=forceRcode)
- if response:
- wire = response.to_wire(max_size=65535)
-
- if not wire:
- conn.close()
- return
- elif isinstance(wire, ResponderDropAction):
- return
-
- wireLen = struct.pack("!H", len(wire))
- if partialWrite:
- for b in wireLen:
- conn.send(bytes([b]))
- time.sleep(0.5)
- else:
- conn.send(wireLen)
- conn.send(wire)
-
- while multipleResponses:
- # do not block, and stop as soon as the queue is empty, either the next response is already here or we are done
- # otherwise we might read responses intended for the next connection
- if fromQueue.empty():
- break
-
- response = fromQueue.get(False)
- if not response:
- break
+ def handleTCPConnection(
+ cls,
+ conn,
+ fromQueue,
+ toQueue,
+ trailingDataResponse=False,
+ multipleResponses=False,
+ callback=None,
+ partialWrite=False,
+ ):
+ ignoreTrailing = trailingDataResponse is True
+ try:
+ data = conn.recv(2)
+ except Exception as err:
+ data = None
+ print(f"Error while reading query size in TCP responder thread {err=}, {type(err)=}")
+ if not data:
+ conn.close()
+ return
- response = copy.copy(response)
- response.id = request.id
- wire = response.to_wire(max_size=65535)
+ (datalen,) = struct.unpack("!H", data)
+ data = conn.recv(datalen)
+ forceRcode = None
try:
- conn.send(struct.pack("!H", len(wire)))
- conn.send(wire)
- except socket.error as e:
- # some of the tests are going to close
- # the connection on us, just deal with it
- break
+ request = dns.message.from_wire(data, ignore_trailing=ignoreTrailing)
+ except dns.message.TrailingJunk as e:
+ if trailingDataResponse is False or forceRcode is True:
+ raise
+ print("TCP query with trailing data, synthesizing response")
+ request = dns.message.from_wire(data, ignore_trailing=True)
+ forceRcode = trailingDataResponse
- conn.close()
+ if callback:
+ wire = callback(request)
+ else:
+ if request.edns > 1:
+ forceRcode = dns.rcode.BADVERS
+ response = cls._getResponse(request, fromQueue, toQueue, synthesize=forceRcode)
+ if response:
+ wire = response.to_wire(max_size=65535)
+
+ if not wire:
+ conn.close()
+ return
+ elif isinstance(wire, ResponderDropAction):
+ return
+
+ wireLen = struct.pack("!H", len(wire))
+ if partialWrite:
+ for b in wireLen:
+ conn.send(bytes([b]))
+ time.sleep(0.5)
+ else:
+ conn.send(wireLen)
+ conn.send(wire)
+
+ while multipleResponses:
+ # do not block, and stop as soon as the queue is empty, either the next response is already here or we are done
+ # otherwise we might read responses intended for the next connection
+ if fromQueue.empty():
+ break
+
+ response = fromQueue.get(False)
+ if not response:
+ break
+
+ response = copy.copy(response)
+ response.id = request.id
+ wire = response.to_wire(max_size=65535)
+ try:
+ conn.send(struct.pack("!H", len(wire)))
+ conn.send(wire)
+ except socket.error as e:
+ # some of the tests are going to close
+ # the connection on us, just deal with it
+ break
+
+ conn.close()
@classmethod
- def TCPResponder(cls, port, fromQueue, toQueue, trailingDataResponse=False, multipleResponses=False, callback=None, tlsContext=None, multipleConnections=False, listeningAddr='127.0.0.1', partialWrite=False):
+ def TCPResponder(
+ cls,
+ port,
+ fromQueue,
+ toQueue,
+ trailingDataResponse=False,
+ multipleResponses=False,
+ callback=None,
+ tlsContext=None,
+ multipleConnections=False,
+ listeningAddr="127.0.0.1",
+ partialWrite=False,
+ ):
cls._backgroundThreads[threading.get_native_id()] = True
# trailingDataResponse=True means "ignore trailing data".
# Other values are either False (meaning "raise an exception")
sock.listen(100)
sock.settimeout(0.5)
if tlsContext:
- sock = tlsContext.wrap_socket(sock, server_side=True)
+ sock = tlsContext.wrap_socket(sock, server_side=True)
while True:
try:
- (conn, _) = sock.accept()
+ (conn, _) = sock.accept()
except ssl.SSLError:
- continue
+ continue
except ConnectionResetError:
- continue
- except socket.timeout:
- if cls._backgroundThreads.get(threading.get_native_id(), False) == False:
- del cls._backgroundThreads[threading.get_native_id()]
- break
- else:
continue
+ except socket.timeout:
+ if cls._backgroundThreads.get(threading.get_native_id(), False) == False:
+ del cls._backgroundThreads[threading.get_native_id()]
+ break
+ else:
+ continue
conn.settimeout(5.0)
if multipleConnections:
- thread = threading.Thread(name='TCP Connection Handler',
- target=cls.handleTCPConnection,
- args=[conn, fromQueue, toQueue, trailingDataResponse, multipleResponses, callback, partialWrite])
- thread.daemon = True
- thread.start()
+ thread = threading.Thread(
+ name="TCP Connection Handler",
+ target=cls.handleTCPConnection,
+ args=[conn, fromQueue, toQueue, trailingDataResponse, multipleResponses, callback, partialWrite],
+ )
+ thread.daemon = True
+ thread.start()
else:
- cls.handleTCPConnection(conn, fromQueue, toQueue, trailingDataResponse, multipleResponses, callback, partialWrite)
+ cls.handleTCPConnection(
+ conn, fromQueue, toQueue, trailingDataResponse, multipleResponses, callback, partialWrite
+ )
sock.close()
@classmethod
- def handleDoHConnection(cls, config, conn, fromQueue, toQueue, trailingDataResponse, multipleResponses, callback, tlsContext, useProxyProtocol):
+ def handleDoHConnection(
+ cls,
+ config,
+ conn,
+ fromQueue,
+ toQueue,
+ trailingDataResponse,
+ multipleResponses,
+ callback,
+ tlsContext,
+ useProxyProtocol,
+ ):
ignoreTrailing = trailingDataResponse is True
try:
- h2conn = h2.connection.H2Connection(config=config)
- h2conn.initiate_connection()
- conn.sendall(h2conn.data_to_send())
+ h2conn = h2.connection.H2Connection(config=config)
+ h2conn.initiate_connection()
+ conn.sendall(h2conn.data_to_send())
except ssl.SSLEOFError as e:
- print("Unexpected EOF: %s" % (e))
- return
+ print("Unexpected EOF: %s" % (e))
+ return
except Exception as err:
- print(f'Unexpected exception in DoH responder thread (connection init) {err=}, {type(err)=}')
- return
+ print(f"Unexpected exception in DoH responder thread (connection init) {err=}, {type(err)=}")
+ return
dnsData = {}
proxy = ProxyProtocol()
header = conn.recv(proxy.HEADER_SIZE)
if not header:
- print('unable to get header')
+ print("unable to get header")
conn.close()
return
if not proxy.parseHeader(header):
- print('unable to parse header')
+ print("unable to parse header")
print(header)
conn.close()
return
proxyContent = conn.recv(proxy.contentLen)
if not proxyContent:
- print('unable to get content')
+ print("unable to get content")
conn.close()
return
requestHeaders = None
while True:
try:
- data = conn.recv(65535)
+ data = conn.recv(65535)
except Exception as err:
- data = None
- print(f'Unexpected exception in DoH responder thread {err=}, {type(err)=}')
+ data = None
+ print(f"Unexpected exception in DoH responder thread {err=}, {type(err)=}")
if not data:
break
if isinstance(event, h2.events.DataReceived):
h2conn.acknowledge_received_data(event.flow_controlled_length, event.stream_id)
if not event.stream_id in dnsData:
- dnsData[event.stream_id] = b''
+ dnsData[event.stream_id] = b""
dnsData[event.stream_id] = dnsData[event.stream_id] + (event.data)
if event.stream_ended:
forceRcode = None
break
headers = [
- (':status', str(status)),
- ('content-length', str(len(wire))),
- ('content-type', 'application/dns-message'),
+ (":status", str(status)),
+ ("content-length", str(len(wire))),
+ ("content-type", "application/dns-message"),
]
h2conn.send_headers(stream_id=event.stream_id, headers=headers)
h2conn.send_data(stream_id=event.stream_id, data=wire, end_stream=True)
conn.close()
@classmethod
- def DOHResponder(cls, port, fromQueue, toQueue, trailingDataResponse=False, multipleResponses=False, callback=None, tlsContext=None, useProxyProtocol=False):
+ def DOHResponder(
+ cls,
+ port,
+ fromQueue,
+ toQueue,
+ trailingDataResponse=False,
+ multipleResponses=False,
+ callback=None,
+ tlsContext=None,
+ useProxyProtocol=False,
+ ):
cls._backgroundThreads[threading.get_native_id()] = True
# trailingDataResponse=True means "ignore trailing data".
# Other values are either False (meaning "raise an exception")
except ssl.SSLError:
continue
except ConnectionResetError:
- continue
+ continue
except socket.timeout:
if cls._backgroundThreads.get(threading.get_native_id(), False) == False:
del cls._backgroundThreads[threading.get_native_id()]
continue
conn.settimeout(5.0)
- thread = threading.Thread(name='DoH Connection Handler',
- target=cls.handleDoHConnection,
- args=[config, conn, fromQueue, toQueue, trailingDataResponse, multipleResponses, callback, tlsContext, useProxyProtocol])
+ thread = threading.Thread(
+ name="DoH Connection Handler",
+ target=cls.handleDoHConnection,
+ args=[
+ config,
+ conn,
+ fromQueue,
+ toQueue,
+ trailingDataResponse,
+ multipleResponses,
+ callback,
+ tlsContext,
+ useProxyProtocol,
+ ],
+ )
thread.daemon = True
thread.start()
sock.settimeout(timeout)
if not port:
- port = cls._dnsDistPort
+ port = cls._dnsDistPort
sock.connect((cls._dnsDistListeningAddr, port))
return sock
sock.settimeout(timeout)
# 2.7.9+
- if hasattr(ssl, 'create_default_context'):
+ if hasattr(ssl, "create_default_context"):
if not sslctx:
sslctx = ssl.create_default_context(cafile=caCert)
- if len(alpn)> 0 and hasattr(sslctx, 'set_alpn_protocols'):
+ if len(alpn) > 0 and hasattr(sslctx, "set_alpn_protocols"):
sslctx.set_alpn_protocols(alpn)
sslsock = sslctx.wrap_socket(sock, server_hostname=serverName, session=session)
else:
conn = cls.openTLSConnection(port, serverName, caFile, timeout=timeout)
cls.sendTCPQueryOverConnection(conn, query, response=response, timeout=timeout)
if useQueue:
- return cls.recvTCPResponseOverConnection(conn, useQueue=useQueue, timeout=timeout)
+ return cls.recvTCPResponseOverConnection(conn, useQueue=useQueue, timeout=timeout)
return None, cls.recvTCPResponseOverConnection(conn, useQueue=useQueue, timeout=timeout)
@classmethod
print(receivedQuery)
receivedQuery = cls._fromResponderQueue.get(True, timeout)
else:
- print("queue is empty")
+ print("queue is empty")
return (receivedQuery, message)
@classmethod
def _encryptConsole(cls, command, nonce):
- command = command.encode('UTF-8')
+ command = command.encode("UTF-8")
if cls._consoleKey is None:
return command
return libnacl.crypto_secretbox(command, nonce, cls._consoleKey)
result = command
else:
result = libnacl.crypto_secretbox_open(command, nonce, cls._consoleKey)
- return result.decode('UTF-8')
+ return result.decode("UTF-8")
@classmethod
def sendConsoleCommand(cls, command, timeout=5.0, IPv6=False):
sock.send(ourNonce)
theirNonce = sock.recv(len(ourNonce))
if len(theirNonce) != len(ourNonce):
- print("Received a nonce of size %d, expecting %d, console command will not be sent!" % (len(theirNonce), len(ourNonce)))
+ print(
+ "Received a nonce of size %d, expecting %d, console command will not be sent!"
+ % (len(theirNonce), len(ourNonce))
+ )
if len(theirNonce) == 0:
- raise socket.error("Got EOF while reading a nonce of size %d, console command will not be sent!" % (len(ourNonce)))
+ raise socket.error(
+ "Got EOF while reading a nonce of size %d, console command will not be sent!" % (len(ourNonce))
+ )
return None
halfNonceSize = int(len(ourNonce) / 2)
@staticmethod
def generateNewCertificateAndKey(filePrefix):
# generate and sign a new cert
- cmd = ['openssl', 'req', '-new', '-newkey', 'rsa:2048', '-nodes', '-keyout', filePrefix + '.key', '-out', filePrefix + '.csr', '-config', 'configServer.conf']
+ cmd = [
+ "openssl",
+ "req",
+ "-new",
+ "-newkey",
+ "rsa:2048",
+ "-nodes",
+ "-keyout",
+ filePrefix + ".key",
+ "-out",
+ filePrefix + ".csr",
+ "-config",
+ "configServer.conf",
+ ]
try:
- process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
- process.communicate(input='')
+ process = subprocess.Popen(
+ cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True
+ )
+ process.communicate(input="")
except subprocess.CalledProcessError as exc:
- raise AssertionError('openssl req failed (%d): %s' % (exc.returncode, exc.output))
- cmd = ['openssl', 'x509', '-req', '-days', '1', '-CA', 'ca.pem', '-CAkey', 'ca.key', '-CAcreateserial', '-in', filePrefix + '.csr', '-out', filePrefix + '.pem', '-extfile', 'configServer.conf', '-extensions', 'v3_req']
+ raise AssertionError("openssl req failed (%d): %s" % (exc.returncode, exc.output))
+ cmd = [
+ "openssl",
+ "x509",
+ "-req",
+ "-days",
+ "1",
+ "-CA",
+ "ca.pem",
+ "-CAkey",
+ "ca.key",
+ "-CAcreateserial",
+ "-in",
+ filePrefix + ".csr",
+ "-out",
+ filePrefix + ".pem",
+ "-extfile",
+ "configServer.conf",
+ "-extensions",
+ "v3_req",
+ ]
try:
- process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
- process.communicate(input='')
+ process = subprocess.Popen(
+ cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True
+ )
+ process.communicate(input="")
except subprocess.CalledProcessError as exc:
- raise AssertionError('openssl x509 failed (%d): %s' % (exc.returncode, exc.output))
+ raise AssertionError("openssl x509 failed (%d): %s" % (exc.returncode, exc.output))
- with open(filePrefix + '.chain', 'w') as outFile:
- for inFileName in [filePrefix + '.pem', 'ca.pem']:
+ with open(filePrefix + ".chain", "w") as outFile:
+ for inFileName in [filePrefix + ".pem", "ca.pem"]:
with open(inFileName) as inFile:
outFile.write(inFile.read())
- cmd = ['openssl', 'pkcs12', '-export', '-passout', 'pass:passw0rd', '-clcerts', '-in', filePrefix + '.pem', '-CAfile', 'ca.pem', '-inkey', filePrefix + '.key', '-out', filePrefix + '.p12']
+ cmd = [
+ "openssl",
+ "pkcs12",
+ "-export",
+ "-passout",
+ "pass:passw0rd",
+ "-clcerts",
+ "-in",
+ filePrefix + ".pem",
+ "-CAfile",
+ "ca.pem",
+ "-inkey",
+ filePrefix + ".key",
+ "-out",
+ filePrefix + ".p12",
+ ]
try:
- process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
- process.communicate(input='')
+ process = subprocess.Popen(
+ cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True
+ )
+ process.communicate(input="")
except subprocess.CalledProcessError as exc:
- raise AssertionError('openssl pkcs12 failed (%d): %s' % (exc.returncode, exc.output))
-
- def checkMessageProxyProtocol(self, receivedProxyPayload, source, destination, isTCP, values=None, v6=False, sourcePort=None, destinationPort=None):
+ raise AssertionError("openssl pkcs12 failed (%d): %s" % (exc.returncode, exc.output))
+
+ def checkMessageProxyProtocol(
+ self,
+ receivedProxyPayload,
+ source,
+ destination,
+ isTCP,
+ values=None,
+ v6=False,
+ sourcePort=None,
+ destinationPort=None,
+ ):
if values is None:
values = []
proxy = ProxyProtocol()
wire = query
else:
wire = query.to_wire()
- param = base64.urlsafe_b64encode(wire).decode('UTF8').rstrip('=')
+ param = base64.urlsafe_b64encode(wire).decode("UTF8").rstrip("=")
return baseurl + "?dns=" + param
@classmethod
conn = pycurl.Curl()
conn.setopt(pycurl.HTTP_VERSION, pycurl.CURL_HTTP_VERSION_2)
- conn.setopt(pycurl.HTTPHEADER, ["Content-type: application/dns-message",
- "Accept: application/dns-message"])
+ conn.setopt(pycurl.HTTPHEADER, ["Content-type: application/dns-message", "Accept: application/dns-message"])
if timeout:
- conn.setopt(pycurl.TIMEOUT_MS, int(timeout*1000))
+ conn.setopt(pycurl.TIMEOUT_MS, int(timeout * 1000))
return conn
@classmethod
- def sendDOHQuery(cls, port, servername, baseurl, query, response=None, timeout=2.0, caFile=None, useQueue=True, rawQuery=False, rawResponse=False, customHeaders=[], useHTTPS=True, fromQueue=None, toQueue=None, conn=None):
+ def sendDOHQuery(
+ cls,
+ port,
+ servername,
+ baseurl,
+ query,
+ response=None,
+ timeout=2.0,
+ caFile=None,
+ useQueue=True,
+ rawQuery=False,
+ rawResponse=False,
+ customHeaders=[],
+ useHTTPS=True,
+ fromQueue=None,
+ toQueue=None,
+ conn=None,
+ ):
url = cls.getDOHGetURL(baseurl, query, rawQuery)
if not conn:
conn.setopt(pycurl.CAINFO, caFile)
response_headers = BytesIO()
- #conn.setopt(pycurl.VERBOSE, True)
+ # conn.setopt(pycurl.VERBOSE, True)
conn.setopt(pycurl.URL, url)
conn.setopt(pycurl.RESOLVE, ["%s:%d:%s" % (servername, port, cls._dnsDistListeningAddr)])
receivedQuery = None
message = None
- cls._response_headers = ''
+ cls._response_headers = ""
data = conn.perform_rb()
cls._rcode = conn.getinfo(pycurl.RESPONSE_CODE)
if cls._rcode == 200 and not rawResponse:
return (receivedQuery, message)
@classmethod
- def sendDOHPostQuery(cls, port, servername, baseurl, query, response=None, timeout=2.0, caFile=None, useQueue=True, rawQuery=False, rawResponse=False, customHeaders=[], useHTTPS=True):
+ def sendDOHPostQuery(
+ cls,
+ port,
+ servername,
+ baseurl,
+ query,
+ response=None,
+ timeout=2.0,
+ caFile=None,
+ useQueue=True,
+ rawQuery=False,
+ rawResponse=False,
+ customHeaders=[],
+ useHTTPS=True,
+ ):
url = baseurl
conn = cls.openDOHConnection(port, caFile=caFile, timeout=timeout)
response_headers = BytesIO()
- #conn.setopt(pycurl.VERBOSE, True)
+ # conn.setopt(pycurl.VERBOSE, True)
conn.setopt(pycurl.URL, url)
conn.setopt(pycurl.RESOLVE, ["%s:%d:%s" % (servername, port, cls._dnsDistListeningAddr)])
# this means "really do HTTP/2, not HTTP/1 with Upgrade headers"
receivedQuery = None
message = None
- cls._response_headers = ''
+ cls._response_headers = ""
data = conn.perform_rb()
cls._rcode = conn.getinfo(pycurl.RESPONSE_CODE)
if cls._rcode == 200 and not rawResponse:
return (receivedQuery, message)
def sendDOHQueryWrapper(self, query, response, useQueue=True, timeout=2, serverName=None):
- return self.sendDOHQuery(self._dohServerPort, self._serverName if not serverName else serverName, self._dohBaseURL, query, response=response, caFile=self._caCert, useQueue=useQueue, timeout=timeout)
+ return self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName if not serverName else serverName,
+ self._dohBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ useQueue=useQueue,
+ timeout=timeout,
+ )
def sendDOHWithNGHTTP2QueryWrapper(self, query, response, useQueue=True, timeout=2, serverName=None):
- return self.sendDOHQuery(self._dohWithNGHTTP2ServerPort, self._serverName if not serverName else serverName, self._dohWithNGHTTP2BaseURL, query, response=response, caFile=self._caCert, useQueue=useQueue, timeout=timeout)
+ return self.sendDOHQuery(
+ self._dohWithNGHTTP2ServerPort,
+ self._serverName if not serverName else serverName,
+ self._dohWithNGHTTP2BaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ useQueue=useQueue,
+ timeout=timeout,
+ )
def sendDOTQueryWrapper(self, query, response, useQueue=True, timeout=2, serverName=None):
- return self.sendDOTQuery(self._tlsServerPort, self._serverName if not serverName else serverName, query, response, self._caCert, useQueue=useQueue, timeout=timeout)
+ return self.sendDOTQuery(
+ self._tlsServerPort,
+ self._serverName if not serverName else serverName,
+ query,
+ response,
+ self._caCert,
+ useQueue=useQueue,
+ timeout=timeout,
+ )
def sendDOQQueryWrapper(self, query, response, useQueue=True, timeout=2, serverName=None, passExceptions=False):
- return self.sendDOQQuery(self._doqServerPort, query, response=response, caFile=self._caCert, useQueue=useQueue, serverName=self._serverName if not serverName else serverName, timeout=timeout, passExceptions=passExceptions)
+ return self.sendDOQQuery(
+ self._doqServerPort,
+ query,
+ response=response,
+ caFile=self._caCert,
+ useQueue=useQueue,
+ serverName=self._serverName if not serverName else serverName,
+ timeout=timeout,
+ passExceptions=passExceptions,
+ )
def sendDOH3QueryWrapper(self, query, response, useQueue=True, timeout=2, serverName=None, passExceptions=False):
- return self.sendDOH3Query(self._doh3ServerPort, self._dohBaseURL, query, response=response, caFile=self._caCert, useQueue=useQueue, serverName=self._serverName if not serverName else serverName, timeout=timeout, passExceptions=passExceptions)
+ return self.sendDOH3Query(
+ self._doh3ServerPort,
+ self._dohBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ useQueue=useQueue,
+ serverName=self._serverName if not serverName else serverName,
+ timeout=timeout,
+ passExceptions=passExceptions,
+ )
@classmethod
def getDOQConnection(cls, port, caFile=None, source=None, source_port=0):
- manager = dns.quic.SyncQuicManager(
- verify_mode=caFile
- )
+ manager = dns.quic.SyncQuicManager(verify_mode=caFile)
return manager.connect(cls._dnsDistListeningAddr, port, source, source_port)
@classmethod
- def sendDOQQuery(cls, port, query, response=None, timeout=2.0, caFile=None, useQueue=True, rawQuery=False, fromQueue=None, toQueue=None, connection=None, serverName=None, passExceptions=False):
+ def sendDOQQuery(
+ cls,
+ port,
+ query,
+ response=None,
+ timeout=2.0,
+ caFile=None,
+ useQueue=True,
+ rawQuery=False,
+ fromQueue=None,
+ toQueue=None,
+ connection=None,
+ serverName=None,
+ passExceptions=False,
+ ):
if response:
if toQueue:
cls._toResponderQueue.put(response, True, timeout)
try:
- (message, _) = doqclient.quic_query(query, cls._dnsDistListeningAddr, timeout, port, verify=caFile, server_hostname=serverName)
+ (message, _) = doqclient.quic_query(
+ query, cls._dnsDistListeningAddr, timeout, port, verify=caFile, server_hostname=serverName
+ )
except doqclient.StreamResetError as e:
if passExceptions:
raise
return (receivedQuery, message)
@classmethod
- def sendDOH3Query(cls, port, baseurl, query, response=None, timeout=2.0, caFile=None, useQueue=True, rawQuery=False, fromQueue=None, toQueue=None, connection=None, serverName=None, post=False, customHeaders=None, rawResponse=False, passExceptions=False):
+ def sendDOH3Query(
+ cls,
+ port,
+ baseurl,
+ query,
+ response=None,
+ timeout=2.0,
+ caFile=None,
+ useQueue=True,
+ rawQuery=False,
+ fromQueue=None,
+ toQueue=None,
+ connection=None,
+ serverName=None,
+ post=False,
+ customHeaders=None,
+ rawResponse=False,
+ passExceptions=False,
+ ):
if response:
if toQueue:
cls._toResponderQueue.put(response, True, timeout)
if rawResponse:
- return doh3_query(query, cls._dnsDistListeningAddr, baseurl, timeout, port, verify=caFile, server_hostname=serverName, post=post, additional_headers=customHeaders, raw_response=rawResponse)
+ return doh3_query(
+ query,
+ cls._dnsDistListeningAddr,
+ baseurl,
+ timeout,
+ port,
+ verify=caFile,
+ server_hostname=serverName,
+ post=post,
+ additional_headers=customHeaders,
+ raw_response=rawResponse,
+ )
try:
- message = doh3_query(query, cls._dnsDistListeningAddr, baseurl, timeout, port, verify=caFile, server_hostname=serverName, post=post, additional_headers=customHeaders, raw_response=rawResponse)
+ message = doh3_query(
+ query,
+ cls._dnsDistListeningAddr,
+ baseurl,
+ timeout,
+ port,
+ verify=caFile,
+ server_hostname=serverName,
+ post=post,
+ additional_headers=customHeaders,
+ raw_response=rawResponse,
+ )
except doqclient.StreamResetError as e:
- if passExceptions:
+ if passExceptions:
raise
- return (None, None)
+ return (None, None)
receivedQuery = None
HttpConnection = Union[H0Connection, H3Connection]
+
class URL:
def __init__(self, url: str) -> None:
parsed = urlparse(url)
self.method = method
self.url = url
+
class HttpClient(QuicConnectionProtocol):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
"""
Perform a GET request.
"""
- return await self._request(
- HttpRequest(method="GET", url=URL(url), headers=headers)
- )
+ return await self._request(HttpRequest(method="GET", url=URL(url), headers=headers))
- async def post(
- self, url: str, data: bytes, headers: Optional[Dict] = None
- ) -> Deque[H3Event]:
+ async def post(self, url: str, data: bytes, headers: Optional[Dict] = None) -> Deque[H3Event]:
"""
Perform a POST request.
"""
- return await self._request(
- HttpRequest(method="POST", url=URL(url), content=data, headers=headers)
- )
-
+ return await self._request(HttpRequest(method="POST", url=URL(url), content=data, headers=headers))
def http_event_received(self, event: H3Event) -> None:
if isinstance(event, (HeadersReceived, DataReceived)):
end_stream=not request.content,
)
if request.content:
- self._http.send_data(
- stream_id=stream_id, data=request.content, end_stream=True
- )
+ self._http.send_data(stream_id=stream_id, data=request.content, end_stream=True)
waiter = self._loop.create_future()
self._request_events[stream_id] = deque()
url = baseurl
if not post:
- url = "{}?dns={}".format(baseurl, base64.urlsafe_b64encode(query.to_wire()).decode('UTF8').rstrip('='))
+ url = "{}?dns={}".format(baseurl, base64.urlsafe_b64encode(query.to_wire()).decode("UTF8").rstrip("="))
async with connect(
host,
port,
try:
async with async_timeout.timeout(timeout):
-
answer = await perform_http_request(
client=client,
url=url,
return answer
except asyncio.TimeoutError as e:
- return (e,{})
-
-
-def doh3_query(query, host, baseurl, timeout=2, port=853, verify=None, server_hostname=None, post=False, additional_headers=None, raw_response=False):
+ return (e, {})
+
+
+def doh3_query(
+ query,
+ host,
+ baseurl,
+ timeout=2,
+ port=853,
+ verify=None,
+ server_hostname=None,
+ post=False,
+ additional_headers=None,
+ raw_response=False,
+):
configuration = QuicConfiguration(alpn_protocols=H3_ALPN, is_client=True, server_name=server_hostname)
if verify:
configuration.load_verify_locations(verify)
timeout=timeout,
create_protocol=HttpClient,
post=post,
- additional_headers=additional_headers
+ additional_headers=additional_headers,
)
)
- if (isinstance(result, StreamReset)):
+ if isinstance(result, StreamReset):
raise StreamResetError(result.error_code)
- if (isinstance(result, asyncio.TimeoutError)):
+ if isinstance(result, asyncio.TimeoutError):
raise TimeoutError()
if raw_response:
return (result, headers)
from aioquic.quic.configuration import QuicConfiguration
from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset
+
class DnsClientProtocol(QuicConnectionProtocol):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._ack_waiter = None
waiter.set_result(event)
+
class BogusDnsClientProtocol(DnsClientProtocol):
def pack(self, data):
# serialize query
port: int,
query: dns.message,
timeout: float,
- create_protocol=DnsClientProtocol
+ create_protocol=DnsClientProtocol,
) -> None:
print("Connecting to {}:{}".format(host, port))
async with connect(
except asyncio.TimeoutError as e:
return (e, None)
+
class StreamResetError(Exception):
def __init__(self, error, message="Stream reset by peer"):
self.error = error
super().__init__(message)
-def quic_query(query, host='127.0.0.1', timeout=2, port=853, verify=None, server_hostname=None):
+
+def quic_query(query, host="127.0.0.1", timeout=2, port=853, verify=None, server_hostname=None):
configuration = QuicConfiguration(alpn_protocols=["doq"], is_client=True, server_name=server_hostname)
if verify:
configuration.load_verify_locations(verify)
port=port,
query=query,
timeout=timeout,
- create_protocol=DnsClientProtocol
+ create_protocol=DnsClientProtocol,
)
)
- if (isinstance(result, StreamReset)):
+ if isinstance(result, StreamReset):
raise StreamResetError(result.error_code)
- if (isinstance(result, asyncio.TimeoutError)):
+ if isinstance(result, asyncio.TimeoutError):
raise TimeoutError()
return (result, serial)
-def quic_bogus_query(query, host='127.0.0.1', timeout=2, port=853, verify=None, server_hostname=None):
+
+def quic_bogus_query(query, host="127.0.0.1", timeout=2, port=853, verify=None, server_hostname=None):
configuration = QuicConfiguration(alpn_protocols=["doq"], is_client=True, server_name=server_hostname)
if verify:
configuration.load_verify_locations(verify)
port=port,
query=query,
timeout=timeout,
- create_protocol=BogusDnsClientProtocol
+ create_protocol=BogusDnsClientProtocol,
)
)
- if (isinstance(result, StreamReset)):
+ if isinstance(result, StreamReset):
raise StreamResetError(result.error_code)
- if (isinstance(result, asyncio.TimeoutError)):
+ if isinstance(result, asyncio.TimeoutError):
raise TimeoutError()
return result
from proxyprotocol import ProxyProtocol
+
def ProxyProtocolUDPResponder(port, fromQueue, toQueue):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
if proxy.local:
# likely a healthcheck
- data = data[proxy.HEADER_SIZE:]
+ data = data[proxy.HEADER_SIZE :]
request = dns.message.from_wire(data)
response = dns.message.make_response(request)
wire = response.to_wire()
continue
- payload = data[:(proxy.HEADER_SIZE + proxy.contentLen)]
- dnsData = data[(proxy.HEADER_SIZE + proxy.contentLen):]
+ payload = data[: (proxy.HEADER_SIZE + proxy.contentLen)]
+ dnsData = data[(proxy.HEADER_SIZE + proxy.contentLen) :]
toQueue.put([payload, dnsData], True, 2.0)
# computing the correct ID for the response
request = dns.message.from_wire(dnsData)
sock.sendto(response.to_wire(), addr)
sock.settimeout(None)
+
def ProxyProtocolTCPResponder(port, fromQueue, toQueue):
# be aware that this responder will not accept a new connection
# until the last one has been closed. This is done on purpose to
payload = header + proxyContent
while True:
- try:
- data = conn.recv(2)
- except socket.timeout:
- data = None
+ try:
+ data = conn.recv(2)
+ except socket.timeout:
+ data = None
- if not data:
- conn.close()
- break
+ if not data:
+ conn.close()
+ break
- (datalen,) = struct.unpack("!H", data)
- data = conn.recv(datalen)
+ (datalen,) = struct.unpack("!H", data)
+ data = conn.recv(datalen)
- toQueue.put([payload, data], True, 2.0)
+ toQueue.put([payload, data], True, 2.0)
- response = copy.deepcopy(fromQueue.get(True, 2.0))
- if not response:
- conn.close()
- break
+ response = copy.deepcopy(fromQueue.get(True, 2.0))
+ if not response:
+ conn.close()
+ break
- # computing the correct ID for the response
- request = dns.message.from_wire(data)
- response.id = request.id
+ # computing the correct ID for the response
+ request = dns.message.from_wire(data)
+ response.id = request.id
- wire = response.to_wire()
- conn.send(struct.pack("!H", len(wire)))
- conn.send(wire)
+ wire = response.to_wire()
+ conn.send(struct.pack("!H", len(wire)))
+ conn.send(wire)
conn.close()
import dns
from doqclient import StreamResetError
-class QUICTests(object):
+class QUICTests(object):
def testQUICSimple(self):
"""
QUIC: Simple query
"""
- name = 'simple.doq.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple.doq.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendQUICQuery(query, response=response)
self.assertTrue(receivedQuery)
"""
QUIC: Test multiple queries using the same connection
"""
- name = 'simple.doq.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple.doq.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
connection = self.getQUICConnection()
"""
QUIC: Dropped query
"""
- name = 'drop.doq.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "drop.doq.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
try:
(_, receivedResponse) = self.sendQUICQuery(query, response=None, useQueue=False, passExceptions=True)
self.fail()
except StreamResetError as e:
- self.assertEqual(e.error, 5);
+ self.assertEqual(e.error, 5)
def testRefused(self):
"""
QUIC: Refused
"""
- name = 'refused.doq.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "refused.doq.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
"""
QUIC: Spoofed
"""
- name = 'spoof.doq.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "spoof.doq.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.4')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
expectedResponse.answer.append(rrset)
(_, receivedResponse) = self.sendQUICQuery(query, response=None, useQueue=False)
"""
QUIC: No backend
"""
- name = 'no-backend.doq.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "no-backend.doq.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
try:
(_, receivedResponse) = self.sendQUICQuery(query, response=None, useQueue=False, passExceptions=True)
self.fail()
- except StreamResetError as e :
- self.assertEqual(e.error, 5);
+ except StreamResetError as e:
+ self.assertEqual(e.error, 5)
-class QUICACLTests(object):
+class QUICACLTests(object):
def testDropped(self):
"""
QUIC: Dropped query because of ACL
"""
- name = 'acl.doq.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "acl.doq.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
dropped = False
try:
(_, receivedResponse) = self.sendQUICQuery(query, response=None, useQueue=False, passExceptions=True)
self.fail()
except StreamResetError as e:
- self.assertEqual(e.error, 5);
+ self.assertEqual(e.error, 5)
dropped = True
self.assertTrue(dropped)
+
class QUICWithCacheTests(object):
def testCached(self):
"""
QUIC Cache: Served from cache
"""
numberOfQueries = 10
- name = 'cached.quic.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "cached.quic.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
query.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
self.assertEqual(total, 1)
-class QUICGetLocalAddressOnAnyBindTests(object):
+class QUICGetLocalAddressOnAnyBindTests(object):
def testGetLocalAddressOnAnyBind(self):
"""
QUIC: Return CNAME containing the local address for an ANY bind
"""
- name = 'local-address-any.quic.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "local-address-any.quic.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'address-was-127-0-0-1.local-address-any.advanced.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.CNAME,
+ "address-was-127-0-0-1.local-address-any.advanced.tests.powerdns.com.",
+ )
response.answer.append(rrset)
(_, receivedResponse) = self.sendQUICQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
-class QUICXFRTests(object):
+class QUICXFRTests(object):
def testXFR(self):
"""
QUIC: XFR
"""
- name = 'xfr.doq.tests.powerdns.com.'
+ name = "xfr.doq.tests.powerdns.com."
for xfrType in [dns.rdatatype.AXFR, dns.rdatatype.IXFR]:
- query = dns.message.make_query(name, xfrType, 'IN')
+ query = dns.message.make_query(name, xfrType, "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NOTIMP)
import os
import paddingoption
+
class RandomPaddingOption(paddingoption.PaddingOption):
- """Implementation of rfc7830 using random bytes in the payload.
- """
+ """Implementation of rfc7830 using random bytes in the payload."""
def __init__(self, numberOfBytes):
super(RandomPaddingOption, self).__init__(12)
import time
from dnsdisttests import DNSDistTest, pickAvailablePort
+
class APITestsBase(DNSDistTest):
__test__ = False
_webTimeout = 5.0
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKey = 'apisecret'
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
- _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _webServerBasicAuthPassword = "secret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKey = "apisecret"
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
+ _config_params = [
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setACL({"127.0.0.1/32", "::1/128"})
newServer{address="127.0.0.1:%d", pool={'', 'mypool'}}
webserver("127.0.0.1:%d")
setWebserverConfig({password="%s", apiKey="%s"})
"""
- _expectedMetrics = ['responses', 'servfail-responses', 'queries', 'acl-drops',
- 'frontend-noerror', 'frontend-nxdomain', 'frontend-servfail',
- 'rule-drop', 'rule-nxdomain', 'rule-refused', 'self-answered', 'downstream-timeouts',
- 'downstream-send-errors', 'trunc-failures', 'no-policy', 'latency0-1',
- 'latency1-10', 'latency10-50', 'latency50-100', 'latency100-1000',
- 'latency-slow', 'latency-sum', 'latency-count', 'latency-avg100', 'latency-avg1000',
- 'latency-avg10000', 'latency-avg1000000', 'latency-tcp-avg100', 'latency-tcp-avg1000',
- 'latency-tcp-avg10000', 'latency-tcp-avg1000000', 'latency-dot-avg100', 'latency-dot-avg1000',
- 'latency-dot-avg10000', 'latency-dot-avg1000000', 'latency-doh-avg100', 'latency-doh-avg1000',
- 'latency-doh-avg10000', 'latency-doh-avg1000000', 'latency-doq-avg100', 'latency-doq-avg1000',
- 'latency-doq-avg10000', 'latency-doq-avg1000000', 'latency-doh3-avg100', 'latency-doh3-avg1000',
- 'latency-doh3-avg10000', 'latency-doh3-avg1000000','uptime', 'real-memory-usage', 'noncompliant-queries',
- 'noncompliant-responses', 'rdqueries', 'empty-queries', 'cache-hits',
- 'cache-misses', 'cpu-iowait', 'cpu-steal', 'cpu-sys-msec', 'cpu-user-msec', 'fd-usage', 'dyn-blocked',
- 'dyn-block-nmg-size', 'rule-servfail', 'rule-truncated', 'security-status',
- 'udp-in-csum-errors', 'udp-in-errors', 'udp-noport-errors', 'udp-recvbuf-errors', 'udp-sndbuf-errors',
- 'udp6-in-errors', 'udp6-recvbuf-errors', 'udp6-sndbuf-errors', 'udp6-noport-errors', 'udp6-in-csum-errors',
- 'doh-query-pipe-full', 'doh-response-pipe-full', 'doq-response-pipe-full', 'doh3-response-pipe-full', 'proxy-protocol-invalid', 'tcp-listen-overflows',
- 'outgoing-doh-query-pipe-full', 'tcp-query-pipe-full', 'tcp-cross-protocol-query-pipe-full',
- 'tcp-cross-protocol-response-pipe-full']
+ _expectedMetrics = [
+ "responses",
+ "servfail-responses",
+ "queries",
+ "acl-drops",
+ "frontend-noerror",
+ "frontend-nxdomain",
+ "frontend-servfail",
+ "rule-drop",
+ "rule-nxdomain",
+ "rule-refused",
+ "self-answered",
+ "downstream-timeouts",
+ "downstream-send-errors",
+ "trunc-failures",
+ "no-policy",
+ "latency0-1",
+ "latency1-10",
+ "latency10-50",
+ "latency50-100",
+ "latency100-1000",
+ "latency-slow",
+ "latency-sum",
+ "latency-count",
+ "latency-avg100",
+ "latency-avg1000",
+ "latency-avg10000",
+ "latency-avg1000000",
+ "latency-tcp-avg100",
+ "latency-tcp-avg1000",
+ "latency-tcp-avg10000",
+ "latency-tcp-avg1000000",
+ "latency-dot-avg100",
+ "latency-dot-avg1000",
+ "latency-dot-avg10000",
+ "latency-dot-avg1000000",
+ "latency-doh-avg100",
+ "latency-doh-avg1000",
+ "latency-doh-avg10000",
+ "latency-doh-avg1000000",
+ "latency-doq-avg100",
+ "latency-doq-avg1000",
+ "latency-doq-avg10000",
+ "latency-doq-avg1000000",
+ "latency-doh3-avg100",
+ "latency-doh3-avg1000",
+ "latency-doh3-avg10000",
+ "latency-doh3-avg1000000",
+ "uptime",
+ "real-memory-usage",
+ "noncompliant-queries",
+ "noncompliant-responses",
+ "rdqueries",
+ "empty-queries",
+ "cache-hits",
+ "cache-misses",
+ "cpu-iowait",
+ "cpu-steal",
+ "cpu-sys-msec",
+ "cpu-user-msec",
+ "fd-usage",
+ "dyn-blocked",
+ "dyn-block-nmg-size",
+ "rule-servfail",
+ "rule-truncated",
+ "security-status",
+ "udp-in-csum-errors",
+ "udp-in-errors",
+ "udp-noport-errors",
+ "udp-recvbuf-errors",
+ "udp-sndbuf-errors",
+ "udp6-in-errors",
+ "udp6-recvbuf-errors",
+ "udp6-sndbuf-errors",
+ "udp6-noport-errors",
+ "udp6-in-csum-errors",
+ "doh-query-pipe-full",
+ "doh-response-pipe-full",
+ "doq-response-pipe-full",
+ "doh3-response-pipe-full",
+ "proxy-protocol-invalid",
+ "tcp-listen-overflows",
+ "outgoing-doh-query-pipe-full",
+ "tcp-query-pipe-full",
+ "tcp-cross-protocol-query-pipe-full",
+ "tcp-cross-protocol-response-pipe-full",
+ ]
_verboseMode = True
@classmethod
cls.startResponders()
cls.startDNSDist()
cls.setUpSockets()
- cls.waitForTCPSocket('127.0.0.1', cls._webServerPort)
+ cls.waitForTCPSocket("127.0.0.1", cls._webServerPort)
print("Launching tests..")
-class TestAPIBasics(APITestsBase):
+class TestAPIBasics(APITestsBase):
# paths accessible using the API key only
- _apiOnlyPaths = ['/api/v1/servers/localhost/config', '/api/v1/servers/localhost/config/allow-from', '/api/v1/servers/localhost/statistics']
+ _apiOnlyPaths = [
+ "/api/v1/servers/localhost/config",
+ "/api/v1/servers/localhost/config/allow-from",
+ "/api/v1/servers/localhost/statistics",
+ ]
# paths accessible using an API key or basic auth
- _statsPaths = [ '/jsonstat?command=stats', '/jsonstat?command=dynblocklist', '/api/v1/servers/localhost']
+ _statsPaths = ["/jsonstat?command=stats", "/jsonstat?command=dynblocklist", "/api/v1/servers/localhost"]
# paths accessible using basic auth only (list not exhaustive)
- _basicOnlyPaths = ['/', '/index.html']
+ _basicOnlyPaths = ["/", "/index.html"]
__test__ = True
def testBasicAuth(self):
API: Basic Authentication
"""
for path in self._basicOnlyPaths + self._statsPaths:
- url = 'http://127.0.0.1:' + str(self._webServerPort) + path
- r = requests.get(url, auth=('whatever', "evilsecret"), timeout=self._webTimeout)
+ url = "http://127.0.0.1:" + str(self._webServerPort) + path
+ r = requests.get(url, auth=("whatever", "evilsecret"), timeout=self._webTimeout)
self.assertEqual(r.status_code, 401)
- r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ r = requests.get(url, auth=("whatever", self._webServerBasicAuthPassword), timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
"""
API: X-Api-Key
"""
- headers = {'x-api-key': self._webServerAPIKey}
+ headers = {"x-api-key": self._webServerAPIKey}
for path in self._apiOnlyPaths + self._statsPaths:
- url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+ url = "http://127.0.0.1:" + str(self._webServerPort) + path
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
"""
API: Wrong X-Api-Key
"""
- headers = {'x-api-key': "evilapikey"}
+ headers = {"x-api-key": "evilapikey"}
for path in self._apiOnlyPaths + self._statsPaths:
- url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+ url = "http://127.0.0.1:" + str(self._webServerPort) + path
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertEqual(r.status_code, 401)
"""
API: Basic Authentication Only
"""
- headers = {'x-api-key': self._webServerAPIKey}
+ headers = {"x-api-key": self._webServerAPIKey}
for path in self._basicOnlyPaths:
- url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+ url = "http://127.0.0.1:" + str(self._webServerPort) + path
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertEqual(r.status_code, 401)
API: API Key Only
"""
for path in self._apiOnlyPaths:
- url = 'http://127.0.0.1:' + str(self._webServerPort) + path
- r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ url = "http://127.0.0.1:" + str(self._webServerPort) + path
+ r = requests.get(url, auth=("whatever", self._webServerBasicAuthPassword), timeout=self._webTimeout)
self.assertEqual(r.status_code, 401)
def testServersLocalhost(self):
"""
API: /api/v1/servers/localhost
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertEqual(content['daemon_type'], 'dnsdist')
+ self.assertEqual(content["daemon_type"], "dnsdist")
- rule_groups = ['response-rules', 'cache-hit-response-rules', 'self-answered-response-rules', 'rules']
- for key in ['version', 'acl', 'local', 'servers', 'frontends', 'pools'] + rule_groups:
+ rule_groups = ["response-rules", "cache-hit-response-rules", "self-answered-response-rules", "rules"]
+ for key in ["version", "acl", "local", "servers", "frontends", "pools"] + rule_groups:
self.assertIn(key, content)
for rule_group in rule_groups:
for rule in content[rule_group]:
- for key in ['id', 'creationOrder', 'matches', 'rule', 'action', 'uuid']:
+ for key in ["id", "creationOrder", "matches", "rule", "action", "uuid"]:
self.assertIn(key, rule)
- for key in ['id', 'creationOrder', 'matches']:
+ for key in ["id", "creationOrder", "matches"]:
self.assertGreaterEqual(rule[key], 0)
- for server in content['servers']:
- for key in ['id', 'latency', 'name', 'weight', 'outstanding', 'qpsLimit',
- 'reuseds', 'state', 'address', 'pools', 'qps', 'queries', 'order', 'sendErrors',
- 'dropRate', 'responses', 'nonCompliantResponses', 'tcpDiedSendingQuery', 'tcpDiedReadingResponse',
- 'tcpGaveUp', 'tcpReadTimeouts', 'tcpWriteTimeouts', 'tcpCurrentConnections',
- 'tcpNewConnections', 'tcpReusedConnections', 'tlsResumptions', 'tcpAvgQueriesPerConnection',
- 'tcpAvgConnectionDuration', 'tcpLatency', 'protocol', 'healthCheckFailures', 'healthCheckFailuresParsing', 'healthCheckFailuresTimeout', 'healthCheckFailuresNetwork', 'healthCheckFailuresMismatch', 'healthCheckFailuresInvalid']:
+ for server in content["servers"]:
+ for key in [
+ "id",
+ "latency",
+ "name",
+ "weight",
+ "outstanding",
+ "qpsLimit",
+ "reuseds",
+ "state",
+ "address",
+ "pools",
+ "qps",
+ "queries",
+ "order",
+ "sendErrors",
+ "dropRate",
+ "responses",
+ "nonCompliantResponses",
+ "tcpDiedSendingQuery",
+ "tcpDiedReadingResponse",
+ "tcpGaveUp",
+ "tcpReadTimeouts",
+ "tcpWriteTimeouts",
+ "tcpCurrentConnections",
+ "tcpNewConnections",
+ "tcpReusedConnections",
+ "tlsResumptions",
+ "tcpAvgQueriesPerConnection",
+ "tcpAvgConnectionDuration",
+ "tcpLatency",
+ "protocol",
+ "healthCheckFailures",
+ "healthCheckFailuresParsing",
+ "healthCheckFailuresTimeout",
+ "healthCheckFailuresNetwork",
+ "healthCheckFailuresMismatch",
+ "healthCheckFailuresInvalid",
+ ]:
self.assertIn(key, server)
- for key in ['id', 'latency', 'weight', 'outstanding', 'qpsLimit', 'reuseds',
- 'qps', 'queries', 'order', 'tcpLatency', 'responses', 'nonCompliantResponses']:
+ for key in [
+ "id",
+ "latency",
+ "weight",
+ "outstanding",
+ "qpsLimit",
+ "reuseds",
+ "qps",
+ "queries",
+ "order",
+ "tcpLatency",
+ "responses",
+ "nonCompliantResponses",
+ ]:
self.assertGreaterEqual(server[key], 0)
- self.assertIn(server['state'], ['up', 'down', 'UP', 'DOWN'])
+ self.assertIn(server["state"], ["up", "down", "UP", "DOWN"])
- for frontend in content['frontends']:
- for key in ['id', 'address', 'udp', 'tcp', 'type', 'queries', 'nonCompliantQueries']:
+ for frontend in content["frontends"]:
+ for key in ["id", "address", "udp", "tcp", "type", "queries", "nonCompliantQueries"]:
self.assertIn(key, frontend)
- for key in ['id', 'queries', 'nonCompliantQueries']:
+ for key in ["id", "queries", "nonCompliantQueries"]:
self.assertGreaterEqual(frontend[key], 0)
- for pool in content['pools']:
- for key in ['id', 'name', 'cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts', 'cacheCleanupCount']:
+ for pool in content["pools"]:
+ for key in [
+ "id",
+ "name",
+ "cacheSize",
+ "cacheEntries",
+ "cacheHits",
+ "cacheMisses",
+ "cacheDeferredInserts",
+ "cacheDeferredLookups",
+ "cacheLookupCollisions",
+ "cacheInsertCollisions",
+ "cacheTTLTooShorts",
+ "cacheCleanupCount",
+ ]:
self.assertIn(key, pool)
- for key in ['id', 'cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts', 'cacheCleanupCount']:
+ for key in [
+ "id",
+ "cacheSize",
+ "cacheEntries",
+ "cacheHits",
+ "cacheMisses",
+ "cacheDeferredInserts",
+ "cacheDeferredLookups",
+ "cacheLookupCollisions",
+ "cacheInsertCollisions",
+ "cacheTTLTooShorts",
+ "cacheCleanupCount",
+ ]:
self.assertGreaterEqual(pool[key], 0)
- stats = content['statistics']
+ stats = content["statistics"]
for key in self._expectedMetrics:
self.assertIn(key, stats)
self.assertGreaterEqual(stats[key], 0)
"""
API: /api/v1/servers/localhost/pool?name=mypool
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/pool?name=mypool'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost/pool?name=mypool"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertIn('stats', content)
- self.assertIn('servers', content)
-
- for key in ['name', 'cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts']:
- self.assertIn(key, content['stats'])
-
- for key in ['cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts']:
- self.assertGreaterEqual(content['stats'][key], 0)
-
- for server in content['servers']:
- for key in ['id', 'latency', 'name', 'weight', 'outstanding', 'qpsLimit',
- 'reuseds', 'state', 'address', 'pools', 'qps', 'queries', 'order', 'sendErrors',
- 'dropRate', 'responses', 'nonCompliantResponses', 'tcpDiedSendingQuery', 'tcpDiedReadingResponse',
- 'tcpGaveUp', 'tcpReadTimeouts', 'tcpWriteTimeouts', 'tcpCurrentConnections',
- 'tcpNewConnections', 'tcpReusedConnections', 'tcpAvgQueriesPerConnection',
- 'tcpAvgConnectionDuration', 'tcpLatency', 'protocol']:
+ self.assertIn("stats", content)
+ self.assertIn("servers", content)
+
+ for key in [
+ "name",
+ "cacheSize",
+ "cacheEntries",
+ "cacheHits",
+ "cacheMisses",
+ "cacheDeferredInserts",
+ "cacheDeferredLookups",
+ "cacheLookupCollisions",
+ "cacheInsertCollisions",
+ "cacheTTLTooShorts",
+ ]:
+ self.assertIn(key, content["stats"])
+
+ for key in [
+ "cacheSize",
+ "cacheEntries",
+ "cacheHits",
+ "cacheMisses",
+ "cacheDeferredInserts",
+ "cacheDeferredLookups",
+ "cacheLookupCollisions",
+ "cacheInsertCollisions",
+ "cacheTTLTooShorts",
+ ]:
+ self.assertGreaterEqual(content["stats"][key], 0)
+
+ for server in content["servers"]:
+ for key in [
+ "id",
+ "latency",
+ "name",
+ "weight",
+ "outstanding",
+ "qpsLimit",
+ "reuseds",
+ "state",
+ "address",
+ "pools",
+ "qps",
+ "queries",
+ "order",
+ "sendErrors",
+ "dropRate",
+ "responses",
+ "nonCompliantResponses",
+ "tcpDiedSendingQuery",
+ "tcpDiedReadingResponse",
+ "tcpGaveUp",
+ "tcpReadTimeouts",
+ "tcpWriteTimeouts",
+ "tcpCurrentConnections",
+ "tcpNewConnections",
+ "tcpReusedConnections",
+ "tcpAvgQueriesPerConnection",
+ "tcpAvgConnectionDuration",
+ "tcpLatency",
+ "protocol",
+ ]:
self.assertIn(key, server)
- for key in ['id', 'latency', 'weight', 'outstanding', 'qpsLimit', 'reuseds',
- 'qps', 'queries', 'order', 'tcpLatency', 'responses', 'nonCompliantResponses']:
+ for key in [
+ "id",
+ "latency",
+ "weight",
+ "outstanding",
+ "qpsLimit",
+ "reuseds",
+ "qps",
+ "queries",
+ "order",
+ "tcpLatency",
+ "responses",
+ "nonCompliantResponses",
+ ]:
self.assertGreaterEqual(server[key], 0)
- self.assertIn(server['state'], ['up', 'down', 'UP', 'DOWN'])
+ self.assertIn(server["state"], ["up", "down", "UP", "DOWN"])
def testServersIDontExist(self):
"""
API: /api/v1/servers/idonotexist (should be 404)
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/idonotexist'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/idonotexist"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertEqual(r.status_code, 404)
"""
API: /api/v1/servers/localhost/config
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/config'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost/config"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
content = r.json()
values = {}
for entry in content:
- for key in ['type', 'name', 'value']:
+ for key in ["type", "name", "value"]:
self.assertIn(key, entry)
- self.assertEqual(entry['type'], 'ConfigSetting')
- values[entry['name']] = entry['value']
-
- for key in ['acl', 'control-socket', 'ecs-override', 'ecs-source-prefix-v4',
- 'ecs-source-prefix-v6', 'fixup-case', 'max-outstanding', 'server-policy',
- 'stale-cache-entries-ttl', 'tcp-recv-timeout', 'tcp-send-timeout',
- 'truncate-tc', 'verbose', 'verbose-health-checks']:
+ self.assertEqual(entry["type"], "ConfigSetting")
+ values[entry["name"]] = entry["value"]
+
+ for key in [
+ "acl",
+ "control-socket",
+ "ecs-override",
+ "ecs-source-prefix-v4",
+ "ecs-source-prefix-v6",
+ "fixup-case",
+ "max-outstanding",
+ "server-policy",
+ "stale-cache-entries-ttl",
+ "tcp-recv-timeout",
+ "tcp-send-timeout",
+ "truncate-tc",
+ "verbose",
+ "verbose-health-checks",
+ ]:
self.assertIn(key, values)
- for key in ['max-outstanding', 'stale-cache-entries-ttl', 'tcp-recv-timeout',
- 'tcp-send-timeout']:
+ for key in ["max-outstanding", "stale-cache-entries-ttl", "tcp-recv-timeout", "tcp-send-timeout"]:
self.assertGreaterEqual(values[key], 0)
- self.assertTrue(values['ecs-source-prefix-v4'] >= 0 and values['ecs-source-prefix-v4'] <= 32)
- self.assertTrue(values['ecs-source-prefix-v6'] >= 0 and values['ecs-source-prefix-v6'] <= 128)
+ self.assertTrue(values["ecs-source-prefix-v4"] >= 0 and values["ecs-source-prefix-v4"] <= 32)
+ self.assertTrue(values["ecs-source-prefix-v6"] >= 0 and values["ecs-source-prefix-v6"] <= 128)
def testServersLocalhostConfigAllowFrom(self):
"""
API: /api/v1/servers/localhost/config/allow-from
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/config/allow-from'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost/config/allow-from"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- for key in ['type', 'name', 'value']:
+ for key in ["type", "name", "value"]:
self.assertIn(key, content)
- self.assertEqual(content['name'], 'allow-from')
- self.assertEqual(content['type'], 'ConfigSetting')
- acl = content['value']
+ self.assertEqual(content["name"], "allow-from")
+ self.assertEqual(content["type"], "ConfigSetting")
+ acl = content["value"]
expectedACL = ["127.0.0.1/32", "::1/128"]
acl.sort()
expectedACL.sort()
The API is read-only by default, so this should be refused
"""
newACL = ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"]
- payload = json.dumps({"name": "allow-from",
- "type": "ConfigSetting",
- "value": newACL})
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/config/allow-from'
+ payload = json.dumps({"name": "allow-from", "type": "ConfigSetting", "value": newACL})
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost/config/allow-from"
r = requests.put(url, headers=headers, timeout=self._webTimeout, data=payload)
self.assertFalse(r)
self.assertEqual(r.status_code, 405)
"""
API: /api/v1/servers/localhost/statistics
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/statistics'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost/statistics"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
content = r.json()
values = {}
for entry in content:
- self.assertIn('type', entry)
- self.assertIn('name', entry)
- self.assertIn('value', entry)
- self.assertEqual(entry['type'], 'StatisticItem')
- values[entry['name']] = entry['value']
+ self.assertIn("type", entry)
+ self.assertIn("name", entry)
+ self.assertIn("value", entry)
+ self.assertEqual(entry["type"], "StatisticItem")
+ values[entry["name"]] = entry["value"]
for key in self._expectedMetrics:
self.assertIn(key, values)
"""
API: /jsonstat?command=stats
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/jsonstat?command=stats'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/jsonstat?command=stats"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
"""
API: /jsonstat?command=dynblocklist
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/jsonstat?command=dynblocklist'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/jsonstat?command=dynblocklist"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
content = r.json()
if content:
- for key in ['reason', 'seconds', 'blocks', 'action']:
+ for key in ["reason", "seconds", "blocks", "action"]:
self.assertIn(key, content)
- for key in ['blocks']:
+ for key in ["blocks"]:
self.assertGreaterEqual(content[key], 0)
def testServersLocalhostRings(self):
"""
API: /api/v1/servers/localhost/rings
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/rings'
- expectedValues = ['age', 'id', 'name', 'requestor', 'size', 'qtype', 'protocol', 'rd']
- expectedResponseValues = expectedValues + ['latency', 'rcode', 'tc', 'aa', 'answers', 'backend']
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost/rings"
+ expectedValues = ["age", "id", "name", "requestor", "size", "qtype", "protocol", "rd"]
+ expectedResponseValues = expectedValues + ["latency", "rcode", "tc", "aa", "answers", "backend"]
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertIn('queries', content)
- self.assertIn('responses', content)
- self.assertEqual(len(content['queries']), 0)
- self.assertEqual(len(content['responses']), 0)
+ self.assertIn("queries", content)
+ self.assertIn("responses", content)
+ self.assertEqual(len(content["queries"]), 0)
+ self.assertEqual(len(content["responses"]), 0)
- name = 'simple.api.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "simple.api.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertIn('queries', content)
- self.assertIn('responses', content)
- self.assertEqual(len(content['queries']), 2)
- self.assertEqual(len(content['responses']), 2)
- for entry in content['queries']:
+ self.assertIn("queries", content)
+ self.assertIn("responses", content)
+ self.assertEqual(len(content["queries"]), 2)
+ self.assertEqual(len(content["responses"]), 2)
+ for entry in content["queries"]:
for value in expectedValues:
self.assertIn(value, entry)
- for entry in content['responses']:
+ for entry in content["responses"]:
for value in expectedResponseValues:
self.assertIn(value, entry)
+
class TestAPIServerDown(APITestsBase):
__test__ = True
_config_template = """
"""
API: /api/v1/servers/localhost, no latency for a down server
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertEqual(content['servers'][0]['latency'], None)
- self.assertEqual(content['servers'][0]['tcpLatency'], None)
+ self.assertEqual(content["servers"][0]["latency"], None)
+ self.assertEqual(content["servers"][0]["tcpLatency"], None)
+
class TestAPIWritable(APITestsBase):
__test__ = True
- _APIWriteDir = '/tmp'
- _config_params = ['_testServerPort', '_webServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed', '_APIWriteDir']
+ _APIWriteDir = "/tmp"
+ _config_params = [
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ "_APIWriteDir",
+ ]
_config_template = """
setACL({"127.0.0.1/32", "::1/128"})
newServer{address="127.0.0.1:%d"}
"""
API: Set ACL
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/config/allow-from'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost/config/allow-from"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- acl = content['value']
+ acl = content["value"]
expectedACL = ["127.0.0.1/32", "::1/128"]
acl.sort()
expectedACL.sort()
self.assertEqual(acl, expectedACL)
newACL = ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"]
- payload = json.dumps({"name": "allow-from",
- "type": "ConfigSetting",
- "value": newACL})
+ payload = json.dumps({"name": "allow-from", "type": "ConfigSetting", "value": newACL})
r = requests.put(url, headers=headers, timeout=self._webTimeout, data=payload)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- acl = content['value']
+ acl = content["value"]
acl.sort()
self.assertEqual(acl, newACL)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- acl = content['value']
+ acl = content["value"]
acl.sort()
self.assertEqual(acl, newACL)
- configFile = self._APIWriteDir + '/' + 'acl.conf'
+ configFile = self._APIWriteDir + "/" + "acl.conf"
self.assertTrue(os.path.isfile(configFile))
- with open(configFile, 'rt') as f:
+ with open(configFile, "rt") as f:
header = f.readline()
body = f.readline()
self.assertEqual(header, """-- Generated by the REST API, DO NOT EDIT\n""")
- self.assertIn(body, {
- """setACL({"192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"})\n""",
- """setACL({"192.0.2.0/24", "203.0.113.0/24", "198.51.100.0/24"})\n""",
- """setACL({"198.51.100.0/24", "192.0.2.0/24", "203.0.113.0/24"})\n""",
- """setACL({"198.51.100.0/24", "203.0.113.0/24", "192.0.2.0/24"})\n""",
- """setACL({"203.0.113.0/24", "192.0.2.0/24", "198.51.100.0/24"})\n""",
- """setACL({"203.0.113.0/24", "198.51.100.0/24", "192.0.2.0/24"})\n"""
- })
+ self.assertIn(
+ body,
+ {
+ """setACL({"192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"})\n""",
+ """setACL({"192.0.2.0/24", "203.0.113.0/24", "198.51.100.0/24"})\n""",
+ """setACL({"198.51.100.0/24", "192.0.2.0/24", "203.0.113.0/24"})\n""",
+ """setACL({"198.51.100.0/24", "203.0.113.0/24", "192.0.2.0/24"})\n""",
+ """setACL({"203.0.113.0/24", "192.0.2.0/24", "198.51.100.0/24"})\n""",
+ """setACL({"203.0.113.0/24", "198.51.100.0/24", "192.0.2.0/24"})\n""",
+ },
+ )
+
class TestAPICustomHeaders(APITestsBase):
__test__ = True
# paths accessible using the API key only
- _apiOnlyPath = '/api/v1/servers/localhost/config'
+ _apiOnlyPath = "/api/v1/servers/localhost/config"
# paths accessible using basic auth only (list not exhaustive)
- _basicOnlyPath = '/'
+ _basicOnlyPath = "/"
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
API: Basic custom headers
"""
- url = 'http://127.0.0.1:' + str(self._webServerPort) + self._basicOnlyPath
+ url = "http://127.0.0.1:" + str(self._webServerPort) + self._basicOnlyPath
- r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ r = requests.get(url, auth=("whatever", self._webServerBasicAuthPassword), timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
- self.assertEqual(r.headers.get('x-custom'), "custom")
+ self.assertEqual(r.headers.get("x-custom"), "custom")
self.assertNotIn("x-frame-options", r.headers)
def testBasicHeadersUpdate(self):
API: Basic update of custom headers
"""
- url = 'http://127.0.0.1:' + str(self._webServerPort) + self._basicOnlyPath
+ url = "http://127.0.0.1:" + str(self._webServerPort) + self._basicOnlyPath
self.sendConsoleCommand('setWebserverConfig({customHeaders={["x-powered-by"]="dnsdist"}})')
- r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ r = requests.get(url, auth=("whatever", self._webServerBasicAuthPassword), timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
- self.assertEqual(r.headers.get('x-powered-by'), "dnsdist")
+ self.assertEqual(r.headers.get("x-powered-by"), "dnsdist")
self.assertIn("x-frame-options", r.headers)
+
class TestStatsWithoutAuthentication(APITestsBase):
__test__ = True
# paths accessible using the API key only
- _apiOnlyPath = '/api/v1/servers/localhost/config'
+ _apiOnlyPath = "/api/v1/servers/localhost/config"
# paths accessible using basic auth only (list not exhaustive)
- _basicOnlyPath = '/'
- _noAuthenticationPaths = [ '/metrics', '/jsonstat?command=dynblocklist' ]
+ _basicOnlyPath = "/"
+ _noAuthenticationPaths = ["/metrics", "/jsonstat?command=dynblocklist"]
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
"""
for path in self._noAuthenticationPaths:
- url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+ url = "http://127.0.0.1:" + str(self._webServerPort) + path
r = requests.get(url, timeout=self._webTimeout)
self.assertTrue(r)
# these should still require basic authentication
for path in [self._basicOnlyPath]:
- url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+ url = "http://127.0.0.1:" + str(self._webServerPort) + path
r = requests.get(url, timeout=self._webTimeout)
self.assertEqual(r.status_code, 401)
- r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ r = requests.get(url, auth=("whatever", self._webServerBasicAuthPassword), timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
# these should still require API authentication
for path in [self._apiOnlyPath]:
- url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+ url = "http://127.0.0.1:" + str(self._webServerPort) + path
r = requests.get(url, timeout=self._webTimeout)
self.assertEqual(r.status_code, 401)
- headers = {'x-api-key': self._webServerAPIKey}
+ headers = {"x-api-key": self._webServerAPIKey}
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
+
class TestAPIAuth(APITestsBase):
__test__ = True
- _webServerBasicAuthPasswordNew = 'password'
- _webServerBasicAuthPasswordNewHashed = '$scrypt$ln=10,p=1,r=8$yefz8SAuT3lj3moXqUYvmw==$T98/RYMp76ZYNjd7MpAkcVXZEDqpLtrc3tQ52QflVBA='
- _webServerAPIKeyNew = 'apipassword'
- _webServerAPIKeyNewHashed = '$scrypt$ln=9,p=1,r=8$y96I9nfkY0LWDQEdSUzWgA==$jiyn9QD36o9d0ADrlqiIBk4AKyQrkD1KYw3CexwtHp4='
+ _webServerBasicAuthPasswordNew = "password"
+ _webServerBasicAuthPasswordNewHashed = (
+ "$scrypt$ln=10,p=1,r=8$yefz8SAuT3lj3moXqUYvmw==$T98/RYMp76ZYNjd7MpAkcVXZEDqpLtrc3tQ52QflVBA="
+ )
+ _webServerAPIKeyNew = "apipassword"
+ _webServerAPIKeyNewHashed = (
+ "$scrypt$ln=9,p=1,r=8$y96I9nfkY0LWDQEdSUzWgA==$jiyn9QD36o9d0ADrlqiIBk4AKyQrkD1KYw3CexwtHp4="
+ )
# paths accessible using the API key only
- _apiOnlyPath = '/api/v1/servers/localhost/config'
+ _apiOnlyPath = "/api/v1/servers/localhost/config"
# paths accessible using basic auth only (list not exhaustive)
- _basicOnlyPath = '/'
+ _basicOnlyPath = "/"
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
API: Basic Authentication updating credentials
"""
- url = 'http://127.0.0.1:' + str(self._webServerPort) + self._basicOnlyPath
- self.sendConsoleCommand('setWebserverConfig({{password="{}"}})'.format(self._webServerBasicAuthPasswordNewHashed))
+ url = "http://127.0.0.1:" + str(self._webServerPort) + self._basicOnlyPath
+ self.sendConsoleCommand(
+ 'setWebserverConfig({{password="{}"}})'.format(self._webServerBasicAuthPasswordNewHashed)
+ )
- r = requests.get(url, auth=('whatever', self._webServerBasicAuthPasswordNew), timeout=self._webTimeout)
+ r = requests.get(url, auth=("whatever", self._webServerBasicAuthPasswordNew), timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
# Make sure the old password is not usable any more
- r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ r = requests.get(url, auth=("whatever", self._webServerBasicAuthPassword), timeout=self._webTimeout)
self.assertEqual(r.status_code, 401)
def testXAPIKeyChange(self):
API: X-Api-Key updating credentials
"""
- url = 'http://127.0.0.1:' + str(self._webServerPort) + self._apiOnlyPath
+ url = "http://127.0.0.1:" + str(self._webServerPort) + self._apiOnlyPath
self.sendConsoleCommand('setWebserverConfig({{apiKey="{}"}})'.format(self._webServerAPIKeyNewHashed))
- headers = {'x-api-key': self._webServerAPIKeyNew}
+ headers = {"x-api-key": self._webServerAPIKeyNew}
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
# Make sure the old password is not usable any more
- headers = {'x-api-key': self._webServerAPIKey}
+ headers = {"x-api-key": self._webServerAPIKey}
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertEqual(r.status_code, 401)
API: X-Api-Key updated to none (disabled)
"""
- url = 'http://127.0.0.1:' + str(self._webServerPort) + self._apiOnlyPath
+ url = "http://127.0.0.1:" + str(self._webServerPort) + self._apiOnlyPath
self.sendConsoleCommand('setWebserverConfig({{apiKey="{}"}})'.format(self._webServerAPIKeyNewHashed))
- headers = {'x-api-key': self._webServerAPIKeyNew}
+ headers = {"x-api-key": self._webServerAPIKeyNew}
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertEqual(r.status_code, 401)
+
class TestAPIACL(APITestsBase):
__test__ = True
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
API: Should be denied by ACL then allowed
"""
- url = 'http://127.0.0.1:' + str(self._webServerPort) + "/"
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/"
try:
- requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ requests.get(url, auth=("whatever", self._webServerBasicAuthPassword), timeout=self._webTimeout)
self.fail()
except requests.exceptions.ConnectionError as exp:
pass
# reset the ACL
self.sendConsoleCommand('setWebserverConfig({acl="127.0.0.1"})')
- r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ r = requests.get(url, auth=("whatever", self._webServerBasicAuthPassword), timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
+
class TestAPIWithoutAuthentication(APITestsBase):
__test__ = True
- _apiPath = '/api/v1/servers/localhost/config'
+ _apiPath = "/api/v1/servers/localhost/config"
# paths accessible using basic auth only (list not exhaustive)
- _basicOnlyPath = '/'
- _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed']
+ _basicOnlyPath = "/"
+ _config_params = ["_testServerPort", "_webServerPort", "_webServerBasicAuthPasswordHashed"]
_config_template = """
setACL({"127.0.0.1/32", "::1/128"})
newServer({address="127.0.0.1:%d"})
"""
for path in [self._apiPath]:
- url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+ url = "http://127.0.0.1:" + str(self._webServerPort) + path
r = requests.get(url, timeout=self._webTimeout)
self.assertTrue(r)
# these should still require basic authentication
for path in [self._basicOnlyPath]:
- url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+ url = "http://127.0.0.1:" + str(self._webServerPort) + path
r = requests.get(url, timeout=self._webTimeout)
self.assertEqual(r.status_code, 401)
- r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ r = requests.get(url, auth=("whatever", self._webServerBasicAuthPassword), timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
+
class TestDashboardWithoutAuthentication(APITestsBase):
__test__ = True
- _basicPath = '/'
- _config_params = ['_testServerPort', '_webServerPort']
+ _basicPath = "/"
+ _config_params = ["_testServerPort", "_webServerPort"]
_config_template = """
setACL({"127.0.0.1/32", "::1/128"})
newServer({address="127.0.0.1:%d"})
webserver("127.0.0.1:%d")
setWebserverConfig({ dashboardRequiresAuthentication=false })
"""
- _verboseMode=True
+ _verboseMode = True
def testDashboard(self):
"""
"""
for path in [self._basicPath]:
- url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+ url = "http://127.0.0.1:" + str(self._webServerPort) + path
r = requests.get(url, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
+
class TestCustomLuaEndpoint(APITestsBase):
__test__ = True
_config_template = """
end
registerWebHandler('/foo', customHTTPHandler)
"""
- _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed']
+ _config_params = ["_testServerPort", "_webServerPort", "_webServerBasicAuthPasswordHashed"]
def testBasic(self):
"""
Custom Web Handler
"""
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/foo?param=42'
- headers = {'customheader': 'foobar'}
- r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout, headers=headers)
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/foo?param=42"
+ headers = {"customheader": "foobar"}
+ r = requests.get(
+ url, auth=("whatever", self._webServerBasicAuthPassword), timeout=self._webTimeout, headers=headers
+ )
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
- self.assertEqual(r.content, b'It works!')
- self.assertEqual(r.headers.get('foo'), "Bar")
+ self.assertEqual(r.content, b"It works!")
+ self.assertEqual(r.headers.get("foo"), "Bar")
+
class TestWebConcurrentConnections(APITestsBase):
__test__ = True
_maxConns = 2
- _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed', '_maxConns']
+ _config_params = [
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ "_maxConns",
+ ]
_config_template = """
newServer{address="127.0.0.1:%d"}
webserver("127.0.0.1:%d")
conns.append(conn)
# we now hold all the slots, let's try to establish a new connection
- url = 'http://127.0.0.1:' + str(self._webServerPort) + "/"
- self.assertRaises(requests.exceptions.ConnectionError, requests.get, url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/"
+ self.assertRaises(
+ requests.exceptions.ConnectionError,
+ requests.get,
+ url,
+ auth=("whatever", self._webServerBasicAuthPassword),
+ timeout=self._webTimeout,
+ )
# free one slot
conns[0].close()
time.sleep(1)
# this should work
- r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ r = requests.get(url, auth=("whatever", self._webServerBasicAuthPassword), timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
+
class TestAPICustomStatistics(APITestsBase):
__test__ = True
_maxConns = 2
- _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
newServer{address="127.0.0.1:%d"}
webserver("127.0.0.1:%d")
API: /jsonstat?command=stats
Test custom statistics are exposed
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/jsonstat?command=stats'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/jsonstat?command=stats"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- expected = ['my-custom-metric', 'my-other-metric', 'my-gauge']
+ expected = ["my-custom-metric", "my-other-metric", "my-gauge"]
for key in expected:
self.assertIn(key, content)
self.assertGreaterEqual(content[key], 0)
- unexpected = ['my-labeled-gauge', 'my-labeled-counter']
+ unexpected = ["my-labeled-gauge", "my-labeled-counter"]
for key in unexpected:
self.assertNotIn(key, content)
import dns
from dnsdisttests import DNSDistTest, pickAvailablePort
-class TestAXFR(DNSDistTest):
+class TestAXFR(DNSDistTest):
# this test suite uses a different responder port
# because, contrary to the other ones, its
# TCP responder allows multiple responses and we don't want
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, True, None, None, True])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, True, None, None, True],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
"""
AXFR: One message
"""
- name = 'one.axfr.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AXFR', 'IN')
+ name = "one.axfr.tests.powerdns.com."
+ query = dns.message.make_query(name, "AXFR", "IN")
response = dns.message.make_response(query)
- soa = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+ soa = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
response.answer.append(soa)
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1'))
+ response.answer.append(dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"))
response.answer.append(soa)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
"""
AXFR: One message, no SOA
"""
- name = 'onenosoa.axfr.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AXFR', 'IN')
+ name = "onenosoa.axfr.tests.powerdns.com."
+ query = dns.message.make_query(name, "AXFR", "IN")
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1'))
+ response.answer.append(dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"))
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
receivedQuery.id = query.id
"""
AXFR: Four messages
"""
- name = 'four.axfr.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AXFR', 'IN')
+ name = "four.axfr.tests.powerdns.com."
+ query = dns.message.make_query(name, "AXFR", "IN")
responses = []
- soa = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+ soa = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
response = dns.message.make_response(query)
response.answer.append(soa)
responses.append(response)
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1'))
+ response.answer.append(dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"))
responses.append(response)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
response.answer.append(rrset)
responses.append(response)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- 'dummy')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.TXT, "dummy")
response.answer.append(rrset)
responses.append(response)
"""
AXFR: Four messages, no final SOA
"""
- name = 'fournosoa.axfr.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AXFR', 'IN')
+ name = "fournosoa.axfr.tests.powerdns.com."
+ query = dns.message.make_query(name, "AXFR", "IN")
responses = []
- soa = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+ soa = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
response = dns.message.make_response(query)
response.answer.append(soa)
responses.append(response)
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1'))
+ response.answer.append(dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"))
responses.append(response)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
response.answer.append(rrset)
responses.append(response)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- 'dummy')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.TXT, "dummy")
response.answer.append(rrset)
responses.append(response)
"""
AXFR: Three messages including the final SOA, plus a trailing one
"""
- name = 'threeplustrailing.axfr.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AXFR', 'IN')
+ name = "threeplustrailing.axfr.tests.powerdns.com."
+ query = dns.message.make_query(name, "AXFR", "IN")
responses = []
- soa = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+ soa = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
# the SOA starts the AXFR
response = dns.message.make_response(query)
# one A
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1'))
+ response.answer.append(dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"))
responses.append(response)
# one AAAA
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
response.answer.append(rrset)
responses.append(response)
# one TXT then the SOA that ends the AXFR
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- "Some text")
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.TXT, "Some text")
response.answer.append(rrset)
response.answer.append(soa)
responses.append(response)
# be sent by the backend but that dnsdist should not
# pass along to the client
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- 'dummy')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.TXT, "dummy")
response.answer.append(rrset)
responses.append(response)
"""
IXFR: Three messages including the final SOA, plus a trailing one
"""
- name = 'threeplustrailing.ixfr.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AXFR', 'IN')
+ name = "threeplustrailing.ixfr.tests.powerdns.com."
+ query = dns.message.make_query(name, "AXFR", "IN")
responses = []
- finalSoa = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 3 3600 3600 3600 60')
+ finalSoa = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 3 3600 3600 3600 60",
+ )
# the final SOA starts the IXFR, with first an update from 1 to 2 (one removal, two additions)
response = dns.message.make_response(query)
response.answer.append(finalSoa)
# update from 1 to 2
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60'))
+ response.answer.append(
+ dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
+ )
# one removal
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1'))
+ response.answer.append(dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"))
# then additions
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 2 3600 3600 3600 60'))
+ response.answer.append(
+ dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 2 3600 3600 3600 60",
+ )
+ )
# new message in the middle of the additions
responses.append(response)
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text_list(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- ['192.0.2.2', '192.0.2.3']))
+ response.answer.append(
+ dns.rrset.from_text_list(name, 60, dns.rdataclass.IN, dns.rdatatype.A, ["192.0.2.2", "192.0.2.3"])
+ )
# done with 1 -> 2
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 2 3600 3600 3600 60'))
+ response.answer.append(
+ dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 2 3600 3600 3600 60",
+ )
+ )
# new message
responses.append(response)
response = dns.message.make_response(query)
response.answer.append(finalSoa)
# and one addition
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.4'))
+ response.answer.append(dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.4"))
# and the final SOA
response.answer.append(finalSoa)
responses.append(response)
# be sent by the backend but that dnsdist should not
# pass along to the client
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- 'dummy')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.TXT, "dummy")
response.answer.append(rrset)
responses.append(response)
import dns
from dnsdisttests import DNSDistTest
-class TestAdvancedFixupCase(DNSDistTest):
+class TestAdvancedFixupCase(DNSDistTest):
_config_template = """
truncateTC(true)
fixupCase(true)
make the backend return a lowercase version,
check that dnsdist fixes the response.
"""
- name = 'fiXuPCasE.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- lowercasequery = dns.message.make_query(name.lower(), 'A', 'IN')
+ name = "fiXuPCasE.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ lowercasequery = dns.message.make_query(name.lower(), "A", "IN")
response = dns.message.make_response(lowercasequery)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
self.assertEqual(query, receivedQuery)
self.assertEqual(expectedResponse, receivedResponse)
-class TestAdvancedACL(DNSDistTest):
+class TestAdvancedACL(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
"""
- _acl = ['192.0.2.1/32']
+ _acl = ["192.0.2.1/32"]
def testACLBlocked(self):
"""
we expect no response since 127.0.0.1 is not on the
ACL.
"""
- name = 'tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
-class TestAdvancedStringOnlyServer(DNSDistTest):
+class TestAdvancedStringOnlyServer(DNSDistTest):
_config_template = """
newServer("127.0.0.1:%d")
"""
"""
Advanced: "string-only" server is placed in the default pool
"""
- name = 'string-only-server.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "string-only-server.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-@unittest.skipIf('SKIP_INCLUDEDIR_TESTS' in os.environ, 'IncludeDir tests are disabled')
-class TestAdvancedIncludeDir(DNSDistTest):
+@unittest.skipIf("SKIP_INCLUDEDIR_TESTS" in os.environ, "IncludeDir tests are disabled")
+class TestAdvancedIncludeDir(DNSDistTest):
_config_template = """
-- this directory contains a file allowing includedir.advanced.tests.powerdns.com.
includeDirectory('test-include-dir')
"""
Advanced: includeDirectory()
"""
- name = 'includedir.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "includedir.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(response, receivedResponse)
# this one should be refused
- name = 'notincludedir.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "notincludedir.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestStatNodeRespRingSince(DNSDistTest):
+class TestStatNodeRespRingSince(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
Advanced: StatNodeRespRing with optional since parameter
"""
- name = 'statnodesince.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "statnodesince.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 1,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 1, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.sendConsoleCommand("nodesSeen = {}")
self.sendConsoleCommand("statNodeRespRing(visitor)")
- nodes = self.sendConsoleCommand("str = '' for key,value in pairs(nodesSeen) do str = str..value..\"\\n\" end return str")
+ nodes = self.sendConsoleCommand(
+ "str = '' for key,value in pairs(nodesSeen) do str = str..value..\"\\n\" end return str"
+ )
nodes = nodes.strip("\n")
- self.assertEqual(nodes, """statnodesince.advanced.tests.powerdns.com.
+ self.assertEqual(
+ nodes,
+ """statnodesince.advanced.tests.powerdns.com.
advanced.tests.powerdns.com.
tests.powerdns.com.
powerdns.com.
-com.""")
+com.""",
+ )
self.sendConsoleCommand("nodesSeen = {}")
self.sendConsoleCommand("statNodeRespRing(visitor, 0)")
- nodes = self.sendConsoleCommand("str = '' for key,value in pairs(nodesSeen) do str = str..value..\"\\n\" end return str")
+ nodes = self.sendConsoleCommand(
+ "str = '' for key,value in pairs(nodesSeen) do str = str..value..\"\\n\" end return str"
+ )
nodes = nodes.strip("\n")
- self.assertEqual(nodes, """statnodesince.advanced.tests.powerdns.com.
+ self.assertEqual(
+ nodes,
+ """statnodesince.advanced.tests.powerdns.com.
advanced.tests.powerdns.com.
tests.powerdns.com.
powerdns.com.
-com.""")
+com.""",
+ )
time.sleep(5)
self.sendConsoleCommand("nodesSeen = {}")
self.sendConsoleCommand("statNodeRespRing(visitor)")
- nodes = self.sendConsoleCommand("str = '' for key,value in pairs(nodesSeen) do str = str..value..\"\\n\" end return str")
+ nodes = self.sendConsoleCommand(
+ "str = '' for key,value in pairs(nodesSeen) do str = str..value..\"\\n\" end return str"
+ )
nodes = nodes.strip("\n")
- self.assertEqual(nodes, """statnodesince.advanced.tests.powerdns.com.
+ self.assertEqual(
+ nodes,
+ """statnodesince.advanced.tests.powerdns.com.
advanced.tests.powerdns.com.
tests.powerdns.com.
powerdns.com.
-com.""")
+com.""",
+ )
self.sendConsoleCommand("nodesSeen = {}")
self.sendConsoleCommand("statNodeRespRing(visitor, 5)")
- nodes = self.sendConsoleCommand("str = '' for key,value in pairs(nodesSeen) do str = str..value..\"\\n\" end return str")
+ nodes = self.sendConsoleCommand(
+ "str = '' for key,value in pairs(nodesSeen) do str = str..value..\"\\n\" end return str"
+ )
nodes = nodes.strip("\n")
self.assertEqual(nodes, """""")
self.sendConsoleCommand("nodesSeen = {}")
self.sendConsoleCommand("statNodeRespRing(visitor, 10)")
- nodes = self.sendConsoleCommand("str = '' for key,value in pairs(nodesSeen) do str = str..value..\"\\n\" end return str")
+ nodes = self.sendConsoleCommand(
+ "str = '' for key,value in pairs(nodesSeen) do str = str..value..\"\\n\" end return str"
+ )
nodes = nodes.strip("\n")
- self.assertEqual(nodes, """statnodesince.advanced.tests.powerdns.com.
+ self.assertEqual(
+ nodes,
+ """statnodesince.advanced.tests.powerdns.com.
advanced.tests.powerdns.com.
tests.powerdns.com.
powerdns.com.
-com.""")
+com.""",
+ )
-class TestAdvancedGetLocalPort(DNSDistTest):
+class TestAdvancedGetLocalPort(DNSDistTest):
_config_template = """
function answerBasedOnLocalPort(dq)
local port = dq.localaddr:getPort()
"""
Advanced: Return CNAME containing the local port
"""
- name = 'local-port.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "local-port.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'port-was-{}.local-port.advanced.tests.powerdns.com.'.format(self._dnsDistPort))
+ rrset = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.CNAME,
+ "port-was-{}.local-port.advanced.tests.powerdns.com.".format(self._dnsDistPort),
+ )
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
-class TestAdvancedGetLocalPortOnAnyBind(DNSDistTest):
+class TestAdvancedGetLocalPortOnAnyBind(DNSDistTest):
_config_template = """
function answerBasedOnLocalPort(dq)
local port = dq.localaddr:getPort()
addAction("local-port-any.advanced.tests.powerdns.com.", LuaAction(answerBasedOnLocalPort))
newServer{address="127.0.0.1:%d"}
"""
- _dnsDistListeningAddr = '0.0.0.0'
+ _dnsDistListeningAddr = "0.0.0.0"
def testAdvancedGetLocalPortOnAnyBind(self):
"""
Advanced: Return CNAME containing the local port for an ANY bind
"""
- name = 'local-port-any.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "local-port-any.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'port-was-{}.local-port-any.advanced.tests.powerdns.com.'.format(self._dnsDistPort))
+ rrset = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.CNAME,
+ "port-was-{}.local-port-any.advanced.tests.powerdns.com.".format(self._dnsDistPort),
+ )
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
-class TestAdvancedGetLocalAddressOnAnyBind(DNSDistTest):
+class TestAdvancedGetLocalAddressOnAnyBind(DNSDistTest):
_config_template = """
function answerBasedOnLocalAddress(dq)
local dest = tostring(dq.localaddr)
addLocal('0.0.0.0:%d')
addLocal('[::]:%d')
"""
- _config_params = ['_testServerPort', '_dnsDistPort', '_dnsDistPort']
- _acl = ['127.0.0.1/32', '::1/128']
+ _config_params = ["_testServerPort", "_dnsDistPort", "_dnsDistPort"]
+ _acl = ["127.0.0.1/32", "::1/128"]
_skipListeningOnCL = True
_verboseMode = True
"""
Advanced: Return CNAME containing the local address for an ANY bind
"""
- name = 'local-address-any.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "local-address-any.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'address-was-127-0-0-1.local-address-any.advanced.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.CNAME,
+ "address-was-127-0-0-1.local-address-any.advanced.tests.powerdns.com.",
+ )
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
# now a bit more tricky, UDP-only IPv4
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'address-was-127-0-0-2.local-address-any.advanced.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.CNAME,
+ "address-was-127-0-0-2.local-address-any.advanced.tests.powerdns.com.",
+ )
response.answer.append(rrset)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(2.0)
- sock.connect(('127.0.0.2', self._dnsDistPort))
+ sock.connect(("127.0.0.2", self._dnsDistPort))
try:
query = query.to_wire()
sock.send(query)
(data, remote) = sock.recvfrom(4096)
- self.assertEqual(remote[0], '127.0.0.2')
+ self.assertEqual(remote[0], "127.0.0.2")
except socket.timeout:
data = None
"""
Advanced: Check the source address on responses for an ANY bind
"""
- name = 'source-addr-any.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "source-addr-any.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.42')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.42")
response.answer.append(rrset)
# a bit more tricky, UDP-only IPv4
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(2.0)
- sock.connect(('127.0.0.2', self._dnsDistPort))
+ sock.connect(("127.0.0.2", self._dnsDistPort))
self._toResponderQueue.put(response, True, 1.0)
try:
data = query.to_wire()
sock.send(data)
(data, remote) = sock.recvfrom(4096)
- self.assertEqual(remote[0], '127.0.0.2')
+ self.assertEqual(remote[0], "127.0.0.2")
except socket.timeout:
data = None
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- if 'SKIP_IPV6_TESTS' in os.environ:
- return
+ if "SKIP_IPV6_TESTS" in os.environ:
+ return
# a bit more tricky, UDP-only IPv6
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
sock.settimeout(2.0)
- sock.connect(('::1', self._dnsDistPort))
+ sock.connect(("::1", self._dnsDistPort))
self._toResponderQueue.put(response, True, 1.0)
try:
data = query.to_wire()
sock.send(data)
(data, remote) = sock.recvfrom(4096)
- self.assertEqual(remote[0], '::1')
+ self.assertEqual(remote[0], "::1")
except socket.timeout:
data = None
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
+
class TestAdvancedGetLocalAddressOnNonDefaultLoopbackBind(DNSDistTest):
# this one is tricky: on the loopback interface we cannot harvest the destination
# address, so we exercise a different code path when we bind on a different address
newServer{address="127.0.0.1:%d"}
addLocal('127.0.1.19:%d')
"""
- _config_params = ['_testServerPort', '_dnsDistPort']
- _acl = ['127.0.0.1/32']
+ _config_params = ["_testServerPort", "_dnsDistPort"]
+ _acl = ["127.0.0.1/32"]
_skipListeningOnCL = True
- _alternateListeningAddr = '127.0.1.19'
+ _alternateListeningAddr = "127.0.1.19"
_alternateListeningPort = DNSDistTest._dnsDistPort
def testAdvancedCheckSourceAddrOnNonDefaultLoopbackBind(self):
"""
Advanced: Check the source address used to reply on a non-default loopback bind
"""
- name = 'source-addr-non-default-loopback.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "source-addr-non-default-loopback.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.42')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.42")
response.answer.append(rrset)
# a bit more tricky, UDP-only IPv4
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(2.0)
- sock.connect(('127.0.1.19', self._dnsDistPort))
+ sock.connect(("127.0.1.19", self._dnsDistPort))
self._toResponderQueue.put(response, True, 1.0)
try:
data = query.to_wire()
sock.send(data)
(data, remote) = sock.recvfrom(4096)
- self.assertEqual(remote[0], '127.0.1.19')
+ self.assertEqual(remote[0], "127.0.1.19")
except socket.timeout:
data = None
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
-class TestAdvancedAllowHeaderOnly(DNSDistTest):
+class TestAdvancedAllowHeaderOnly(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
setAllowEmptyResponse(true)
"""
Advanced: Header-only refused response
"""
- name = 'header-only-refused-response.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "header-only-refused-response.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.REFUSED)
response.question = []
"""
Advanced: Header-only NoError response should be allowed
"""
- name = 'header-only-noerror-response.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "header-only-noerror-response.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.question = []
"""
Advanced: Header-only NXD response should be allowed
"""
- name = 'header-only-nxd-response.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "header-only-nxd-response.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.NXDOMAIN)
response.question = []
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
-class TestAdvancedDropEmptyQueries(DNSDistTest):
+class TestAdvancedDropEmptyQueries(DNSDistTest):
_config_template = """
setDropEmptyQueries(true)
newServer{address="127.0.0.1:%d"}
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
+
class TestProtocols(DNSDistTest):
_config_template = """
function checkUDP(dq)
"""
Advanced: Test DNSQuestion.Protocol over UDP
"""
- name = 'udp.protocols.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.protocols.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
"""
Advanced: Test DNSQuestion.Protocol over TCP
"""
- name = 'tcp.protocols.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tcp.protocols.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
+
class TestCustomMetrics(DNSDistTest):
_config_template = """
function custommetrics(dq)
"""
Advanced: Test custom metric declaration after config done
"""
- name = 'declare.metric.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "declare.metric.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Advanced: Test basic operations on custom metrics
"""
- name = 'operations.metric.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "operations.metric.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '4.3.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "4.3.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
+
class TestDNSQuestionTime(DNSDistTest):
_config_template = """
local queryTime = nil
"""
Advanced: Test query time
"""
- name = 'query.time.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "query.time.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '4.3.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "4.3.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
+
class TestChangeName(DNSDistTest):
_config_template = """
local tagName = 'initial-name'
"""
Advanced: ChangeName the query name
"""
- name = 'changeName.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "changeName.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
- changedName = 'changeName.advanced.tests.dnsdist.org.'
- changedQuery = dns.message.make_query(changedName, 'A', 'IN')
+ changedName = "changeName.advanced.tests.dnsdist.org."
+ changedQuery = dns.message.make_query(changedName, "A", "IN")
changedQuery.id = query.id
response = dns.message.make_response(changedQuery)
- rrset = dns.rrset.from_text(changedName,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '4.3.2.1')
+ rrset = dns.rrset.from_text(changedName, 60, dns.rdataclass.IN, dns.rdatatype.A, "4.3.2.1")
response.answer.append(rrset)
- rrset = dns.rrset.from_text('sub.sub2.changeName.advanced.tests.dnsdist.org.',
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- 'This text contains sub.sub2.changeName.advanced.tests.dnsdist.org.')
+ rrset = dns.rrset.from_text(
+ "sub.sub2.changeName.advanced.tests.dnsdist.org.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.TXT,
+ "This text contains sub.sub2.changeName.advanced.tests.dnsdist.org.",
+ )
response.additional.append(rrset)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '4.3.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "4.3.2.1")
expectedResponse.answer.append(rrset)
# we only rewrite records if the owner name matches the new target, nothing else
- rrset = dns.rrset.from_text('sub.sub2.changeName.advanced.tests.dnsdist.org.',
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- 'This text contains sub.sub2.changeName.advanced.tests.dnsdist.org.')
+ rrset = dns.rrset.from_text(
+ "sub.sub2.changeName.advanced.tests.dnsdist.org.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.TXT,
+ "This text contains sub.sub2.changeName.advanced.tests.dnsdist.org.",
+ )
expectedResponse.additional.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedQuery, changedQuery)
self.assertEqual(receivedResponse, expectedResponse)
-class TestFlagsOnTimeout(DNSDistTest):
+class TestFlagsOnTimeout(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
"""
Advanced: Test that we record the correct incoming flags on a timeout
"""
- name = 'timeout-flags.advanced.tests.powerdns.com.'
+ name = "timeout-flags.advanced.tests.powerdns.com."
# first with RD=1
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
query.id = 42
query.flags |= dns.flags.RD
self.assertFalse(receivedResponse)
# then with RD=0
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
query.id = 84
query.flags &= ~dns.flags.RD
print(lines)
self.assertEqual(len(lines), 5)
# header line
- self.assertIn('TC RD AA', lines[0])
+ self.assertIn("TC RD AA", lines[0])
queries = {}
timeouts = {}
for line in lines[1:]:
- self.assertIn('DoUDP', line)
- if 'T.O' in line:
+ self.assertIn("DoUDP", line)
+ if "T.O" in line:
queryID = int(line.split()[4])
timeouts[queryID] = line
else:
queryID = int(line.split()[3])
queries[queryID] = line
if queryID == 42:
- self.assertIn('RD', line)
+ self.assertIn("RD", line)
else:
- self.assertNotIn('RD', line)
+ self.assertNotIn("RD", line)
self.assertEqual(len(timeouts), 2)
self.assertEqual(len(queries), 2)
+
class TestTruncatedUDPLargeAnswers(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
"""
+
def testVeryLargeAnswer(self):
"""
Advanced: Check that UDP responses that are too large for our buffer are dismissed
"""
- name = 'very-large-answer-dismissed.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'TXT', 'IN')
+ name = "very-large-answer-dismissed.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "TXT", "IN")
response = dns.message.make_response(query)
# we prepare a large answer
- content = ''
+ content = ""
for i in range(31):
if len(content) > 0:
- content = content + ' '
- content = content + 'A' * 255
+ content = content + " "
+ content = content + "A" * 255
# pad up to 8192
- content = content + ' ' + 'B' * 170
+ content = content + " " + "B" * 170
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- content)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, content)
response.answer.append(rrset)
self.assertEqual(len(response.to_wire()), 8192)
query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(
- name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"
- )
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in (
query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(
- name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"
- )
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in (
query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(
- name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"
- )
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in (
query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(
- name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"
- )
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in (
query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(
- name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"
- )
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(
- name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"
- )
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
expectedQuery = dns.message.make_query(name, "A", "IN")
query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(
- name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"
- )
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in (
):
sender = getattr(self, method)
try:
- if method in ['sendDOQQueryWrapper']:
+ if method in ["sendDOQQueryWrapper"]:
(receivedQuery, receivedResponse) = sender(query, response, passExceptions=True)
else:
(receivedQuery, receivedResponse) = sender(query, response)
name = "timeout-then-accept." + method + ".tc.async.tests.powerdns.com."
query = dns.message.make_query(name, "A", "IN")
query.id = 42
- expectedQuery = dns.message.make_query(
- name, "A", "IN", use_edns=True, payload=4096
- )
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 42
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(
- name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1"
- )
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# first response is a TC=1
from dnsdisttests import DNSDistTest
+
class TestBackendDiscovery(DNSDistTest):
# these ports are hardcoded for now, sorry about that!
_noSVCBackendPort = 10600
_badQNameBackendPort = 10615
_svcUpgradeDoTNoPortBackendPort = 10616
_svcUpgradeDoHNoPortBackendPort = 10617
- _upgradedBackendsPool = 'upgraded'
+ _upgradedBackendsPool = "upgraded"
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_noSVCBackendPort', '_svcNoUpgradeBackendPort', '_svcUpgradeDoTBackendPort', '_upgradedBackendsPool', '_svcUpgradeDoHBackendPort', '_svcUpgradeDoTBackendDifferentAddrPort1', '_svcUpgradeDoTBackendDifferentAddrPort2', '_svcUpgradeDoTUnreachableBackendPort', '_svcBrokenDNSResponseBackendPort', '_svcUpgradeDoHBackendWithoutPathPort', '_connectionRefusedBackendPort', '_eofBackendPort', '_servfailBackendPort', '_wrongNameBackendPort', '_wrongIDBackendPort', '_tooManyQuestionsBackendPort', '_badQNameBackendPort', '_svcUpgradeDoTNoPortBackendPort', '_svcUpgradeDoHNoPortBackendPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_noSVCBackendPort",
+ "_svcNoUpgradeBackendPort",
+ "_svcUpgradeDoTBackendPort",
+ "_upgradedBackendsPool",
+ "_svcUpgradeDoHBackendPort",
+ "_svcUpgradeDoTBackendDifferentAddrPort1",
+ "_svcUpgradeDoTBackendDifferentAddrPort2",
+ "_svcUpgradeDoTUnreachableBackendPort",
+ "_svcBrokenDNSResponseBackendPort",
+ "_svcUpgradeDoHBackendWithoutPathPort",
+ "_connectionRefusedBackendPort",
+ "_eofBackendPort",
+ "_servfailBackendPort",
+ "_wrongNameBackendPort",
+ "_wrongIDBackendPort",
+ "_tooManyQuestionsBackendPort",
+ "_badQNameBackendPort",
+ "_svcUpgradeDoTNoPortBackendPort",
+ "_svcUpgradeDoHNoPortBackendPort",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
def NoUpgradePathCallback(request):
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SVCB,
- '1 no-upgrade. alpn="h3"')
+ rrset = dns.rrset.from_text(
+ request.question[0].name, 60, dns.rdataclass.IN, dns.rdatatype.SVCB, '1 no-upgrade. alpn="h3"'
+ )
response.answer.append(rrset)
return response.to_wire()
def UpgradeDoTCallback(request):
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SVCB,
- '1 tls.tests.dnsdist.org. alpn="dot" port=10652 ipv4hint=127.0.0.1')
+ rrset = dns.rrset.from_text(
+ request.question[0].name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SVCB,
+ '1 tls.tests.dnsdist.org. alpn="dot" port=10652 ipv4hint=127.0.0.1',
+ )
response.answer.append(rrset)
# add a useless A record for good measure
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(request.question[0].name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
# plus more useless records in authority
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(request.question[0].name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.authority.append(rrset)
# and finally valid, albeit useless, hints
- rrset = dns.rrset.from_text('tls.tests.dnsdist.org.',
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text("tls.tests.dnsdist.org.", 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.additional.append(rrset)
- rrset = dns.rrset.from_text('tls.tests.dnsdist.org.',
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text("tls.tests.dnsdist.org.", 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.additional.append(rrset)
return response.to_wire()
def UpgradeDoHCallback(request):
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SVCB,
- '1 tls.tests.dnsdist.org. alpn="h2" port=10653 ipv4hint=127.0.0.1 key7="/dns-query{?dns}"')
+ rrset = dns.rrset.from_text(
+ request.question[0].name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SVCB,
+ '1 tls.tests.dnsdist.org. alpn="h2" port=10653 ipv4hint=127.0.0.1 key7="/dns-query{?dns}"',
+ )
response.answer.append(rrset)
return response.to_wire()
def UpgradeDoTDifferentAddr1Callback(request):
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SVCB,
- '1 tls.tests.dnsdist.org. alpn="dot" port=10654 ipv4hint=127.0.0.2')
+ rrset = dns.rrset.from_text(
+ request.question[0].name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SVCB,
+ '1 tls.tests.dnsdist.org. alpn="dot" port=10654 ipv4hint=127.0.0.2',
+ )
response.answer.append(rrset)
return response.to_wire()
def UpgradeDoTDifferentAddr2Callback(request):
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SVCB,
- '1 tls.tests.dnsdist.org. alpn="dot" port=10655 ipv4hint=127.0.0.1')
+ rrset = dns.rrset.from_text(
+ request.question[0].name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SVCB,
+ '1 tls.tests.dnsdist.org. alpn="dot" port=10655 ipv4hint=127.0.0.1',
+ )
response.answer.append(rrset)
return response.to_wire()
def UpgradeDoTUnreachableCallback(request):
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SVCB,
- '1 tls.tests.dnsdist.org. alpn="dot" port=10656 ipv4hint=127.0.0.1')
+ rrset = dns.rrset.from_text(
+ request.question[0].name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SVCB,
+ '1 tls.tests.dnsdist.org. alpn="dot" port=10656 ipv4hint=127.0.0.1',
+ )
response.answer.append(rrset)
return response.to_wire()
def UpgradeDoHMissingPathCallback(request):
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SVCB,
- '1 tls.tests.dnsdist.org. alpn="h2" port=10653 ipv4hint=127.0.0.1')
+ rrset = dns.rrset.from_text(
+ request.question[0].name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SVCB,
+ '1 tls.tests.dnsdist.org. alpn="h2" port=10653 ipv4hint=127.0.0.1',
+ )
response.answer.append(rrset)
return response.to_wire()
return response.to_wire()
def WrongNameCallback(request):
- query = dns.message.make_query('not-the-right-one.', dns.rdatatype.SVCB)
+ query = dns.message.make_query("not-the-right-one.", dns.rdatatype.SVCB)
response = dns.message.make_response(query)
response.id = request.id
return response.to_wire()
def UpgradeDoTNoPortCallback(request):
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SVCB,
- '1 tls.tests.dnsdist.org. alpn="dot" ipv4hint=127.0.0.1')
+ rrset = dns.rrset.from_text(
+ request.question[0].name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SVCB,
+ '1 tls.tests.dnsdist.org. alpn="dot" ipv4hint=127.0.0.1',
+ )
response.answer.append(rrset)
return response.to_wire()
def UpgradeDoHNoPortCallback(request):
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SVCB,
- '1 tls.tests.dnsdist.org. alpn="h2" ipv4hint=127.0.0.1 key7="/dns-query{?dns}"')
+ rrset = dns.rrset.from_text(
+ request.question[0].name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SVCB,
+ '1 tls.tests.dnsdist.org. alpn="h2" ipv4hint=127.0.0.1 key7="/dns-query{?dns}"',
+ )
response.answer.append(rrset)
return response.to_wire()
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
-
- TCPNoSVCResponder = threading.Thread(name='TCP no SVC Responder', target=cls.TCPResponder, args=[cls._noSVCBackendPort, cls._toResponderQueue, cls._fromResponderQueue, True, False, cls.NoSVCCallback])
+ tlsContext.load_cert_chain("server.chain", "server.key")
+
+ TCPNoSVCResponder = threading.Thread(
+ name="TCP no SVC Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._noSVCBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ True,
+ False,
+ cls.NoSVCCallback,
+ ],
+ )
TCPNoSVCResponder.daemon = True
TCPNoSVCResponder.start()
- TCPNoUpgradeResponder = threading.Thread(name='TCP no upgrade Responder', target=cls.TCPResponder, args=[cls._svcNoUpgradeBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.NoUpgradePathCallback])
+ TCPNoUpgradeResponder = threading.Thread(
+ name="TCP no upgrade Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._svcNoUpgradeBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.NoUpgradePathCallback,
+ ],
+ )
TCPNoUpgradeResponder.daemon = True
TCPNoUpgradeResponder.start()
# this one is special, does partial writes!
- TCPUpgradeToDoTResponder = threading.Thread(name='TCP upgrade to DoT Responder', target=cls.TCPResponder, args=[cls._svcUpgradeDoTBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.UpgradeDoTCallback, None, False, '127.0.0.1', True])
+ TCPUpgradeToDoTResponder = threading.Thread(
+ name="TCP upgrade to DoT Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._svcUpgradeDoTBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.UpgradeDoTCallback,
+ None,
+ False,
+ "127.0.0.1",
+ True,
+ ],
+ )
TCPUpgradeToDoTResponder.daemon = True
TCPUpgradeToDoTResponder.start()
# and the corresponding DoT responder
- UpgradedDoTResponder = threading.Thread(name='DoT upgraded Responder', target=cls.TCPResponder, args=[10652, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ UpgradedDoTResponder = threading.Thread(
+ name="DoT upgraded Responder",
+ target=cls.TCPResponder,
+ args=[10652, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
UpgradedDoTResponder.daemon = True
UpgradedDoTResponder.start()
- TCPUpgradeToDoHResponder = threading.Thread(name='TCP upgrade to DoH Responder', target=cls.TCPResponder, args=[cls._svcUpgradeDoHBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.UpgradeDoHCallback])
+ TCPUpgradeToDoHResponder = threading.Thread(
+ name="TCP upgrade to DoH Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._svcUpgradeDoHBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.UpgradeDoHCallback,
+ ],
+ )
TCPUpgradeToDoHResponder.daemon = True
TCPUpgradeToDoHResponder.start()
# and the corresponding DoH responder
- UpgradedDOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[10653, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ UpgradedDOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[10653, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
UpgradedDOHResponder.daemon = True
UpgradedDOHResponder.start()
- TCPUpgradeToDoTDifferentAddrResponder = threading.Thread(name='TCP upgrade to DoT different addr 1 Responder', target=cls.TCPResponder, args=[cls._svcUpgradeDoTBackendDifferentAddrPort1, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.UpgradeDoTDifferentAddr1Callback])
+ TCPUpgradeToDoTDifferentAddrResponder = threading.Thread(
+ name="TCP upgrade to DoT different addr 1 Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._svcUpgradeDoTBackendDifferentAddrPort1,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.UpgradeDoTDifferentAddr1Callback,
+ ],
+ )
TCPUpgradeToDoTDifferentAddrResponder.daemon = True
TCPUpgradeToDoTDifferentAddrResponder.start()
# and the corresponding DoT responder
- UpgradedDoTResponder = threading.Thread(name='DoT upgraded different addr 1 Responder', target=cls.TCPResponder, args=[10654, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext, False, '127.0.0.2'])
+ UpgradedDoTResponder = threading.Thread(
+ name="DoT upgraded different addr 1 Responder",
+ target=cls.TCPResponder,
+ args=[
+ 10654,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ None,
+ tlsContext,
+ False,
+ "127.0.0.2",
+ ],
+ )
UpgradedDoTResponder.daemon = True
UpgradedDoTResponder.start()
- TCPUpgradeToDoTDifferentAddrResponder = threading.Thread(name='TCP upgrade to DoT different addr 2 Responder', target=cls.TCPResponder, args=[cls._svcUpgradeDoTBackendDifferentAddrPort2, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.UpgradeDoTDifferentAddr2Callback, None, False, '127.0.0.2'])
+ TCPUpgradeToDoTDifferentAddrResponder = threading.Thread(
+ name="TCP upgrade to DoT different addr 2 Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._svcUpgradeDoTBackendDifferentAddrPort2,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.UpgradeDoTDifferentAddr2Callback,
+ None,
+ False,
+ "127.0.0.2",
+ ],
+ )
TCPUpgradeToDoTDifferentAddrResponder.daemon = True
TCPUpgradeToDoTDifferentAddrResponder.start()
# and the corresponding DoT responder
- UpgradedDoTResponder = threading.Thread(name='DoT upgraded different addr 2 Responder', target=cls.TCPResponder, args=[10655, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext, False])
+ UpgradedDoTResponder = threading.Thread(
+ name="DoT upgraded different addr 2 Responder",
+ target=cls.TCPResponder,
+ args=[10655, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext, False],
+ )
UpgradedDoTResponder.daemon = True
UpgradedDoTResponder.start()
- TCPUpgradeToUnreachableDoTResponder = threading.Thread(name='TCP upgrade to unreachable DoT Responder', target=cls.TCPResponder, args=[cls._svcUpgradeDoTUnreachableBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.UpgradeDoTUnreachableCallback])
+ TCPUpgradeToUnreachableDoTResponder = threading.Thread(
+ name="TCP upgrade to unreachable DoT Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._svcUpgradeDoTUnreachableBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.UpgradeDoTUnreachableCallback,
+ ],
+ )
TCPUpgradeToUnreachableDoTResponder.daemon = True
TCPUpgradeToUnreachableDoTResponder.start()
# and NO corresponding DoT responder
# this is not a mistake!
- BrokenResponseResponder = threading.Thread(name='Broken response Responder', target=cls.TCPResponder, args=[cls._svcBrokenDNSResponseBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.BrokenResponseCallback])
+ BrokenResponseResponder = threading.Thread(
+ name="Broken response Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._svcBrokenDNSResponseBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.BrokenResponseCallback,
+ ],
+ )
BrokenResponseResponder.daemon = True
BrokenResponseResponder.start()
- DOHMissingPathResponder = threading.Thread(name='DoH missing path Responder', target=cls.TCPResponder, args=[cls._svcUpgradeDoHBackendWithoutPathPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.UpgradeDoHMissingPathCallback])
+ DOHMissingPathResponder = threading.Thread(
+ name="DoH missing path Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._svcUpgradeDoHBackendWithoutPathPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.UpgradeDoHMissingPathCallback,
+ ],
+ )
DOHMissingPathResponder.daemon = True
DOHMissingPathResponder.start()
- EOFResponder = threading.Thread(name='EOF Responder', target=cls.TCPResponder, args=[cls._eofBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.EOFCallback])
+ EOFResponder = threading.Thread(
+ name="EOF Responder",
+ target=cls.TCPResponder,
+ args=[cls._eofBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.EOFCallback],
+ )
EOFResponder.daemon = True
EOFResponder.start()
- ServFailResponder = threading.Thread(name='ServFail Responder', target=cls.TCPResponder, args=[cls._servfailBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.ServFailCallback])
+ ServFailResponder = threading.Thread(
+ name="ServFail Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._servfailBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.ServFailCallback,
+ ],
+ )
ServFailResponder.daemon = True
ServFailResponder.start()
- WrongNameResponder = threading.Thread(name='Wrong Name Responder', target=cls.TCPResponder, args=[cls._wrongNameBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.WrongNameCallback])
+ WrongNameResponder = threading.Thread(
+ name="Wrong Name Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._wrongNameBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.WrongNameCallback,
+ ],
+ )
WrongNameResponder.daemon = True
WrongNameResponder.start()
- WrongIDResponder = threading.Thread(name='Wrong ID Responder', target=cls.TCPResponder, args=[cls._wrongIDBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.WrongIDCallback])
+ WrongIDResponder = threading.Thread(
+ name="Wrong ID Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._wrongIDBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.WrongIDCallback,
+ ],
+ )
WrongIDResponder.daemon = True
WrongIDResponder.start()
- TooManyQuestionsResponder = threading.Thread(name='Too many questions Responder', target=cls.TCPResponder, args=[cls._tooManyQuestionsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.TooManyQuestionsCallback])
+ TooManyQuestionsResponder = threading.Thread(
+ name="Too many questions Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._tooManyQuestionsBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.TooManyQuestionsCallback,
+ ],
+ )
TooManyQuestionsResponder.daemon = True
TooManyQuestionsResponder.start()
- badQNameResponder = threading.Thread(name='Bad QName Responder', target=cls.TCPResponder, args=[cls._badQNameBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.BadQNameCallback])
+ badQNameResponder = threading.Thread(
+ name="Bad QName Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._badQNameBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.BadQNameCallback,
+ ],
+ )
badQNameResponder.daemon = True
badQNameResponder.start()
- TCPUpgradeToDoTNoPortResponder = threading.Thread(name='TCP upgrade to DoT (no port) Responder', target=cls.TCPResponder, args=[cls._svcUpgradeDoTNoPortBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.UpgradeDoTNoPortCallback])
+ TCPUpgradeToDoTNoPortResponder = threading.Thread(
+ name="TCP upgrade to DoT (no port) Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._svcUpgradeDoTNoPortBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.UpgradeDoTNoPortCallback,
+ ],
+ )
TCPUpgradeToDoTNoPortResponder.daemon = True
TCPUpgradeToDoTNoPortResponder.start()
- TCPUpgradeToDoHNoPortResponder = threading.Thread(name='TCP upgrade to DoH (no port) Responder', target=cls.TCPResponder, args=[cls._svcUpgradeDoHNoPortBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.UpgradeDoHNoPortCallback])
+ TCPUpgradeToDoHNoPortResponder = threading.Thread(
+ name="TCP upgrade to DoH (no port) Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._svcUpgradeDoHNoPortBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.UpgradeDoHNoPortCallback,
+ ],
+ )
TCPUpgradeToDoHNoPortResponder.daemon = True
TCPUpgradeToDoHNoPortResponder.start()
-
def checkBackendsUpgraded(self):
- output = self.sendConsoleCommand('showServers()')
+ output = self.sendConsoleCommand("showServers()")
print(output)
backends = {}
for line in output.splitlines(False):
- if line.startswith('#') or line.startswith('All'):
+ if line.startswith("#") or line.startswith("All"):
continue
tokens = line.split()
self.assertTrue(len(tokens) == 13 or len(tokens) == 14)
- if tokens[1] == '127.0.0.1:10652':
+ if tokens[1] == "127.0.0.1:10652":
# in this particular case, the upgraded backend
# does not replace the existing one and thus
# the health-check is forced to auto (or lazy auto)
- self.assertEqual(tokens[2], 'up')
+ self.assertEqual(tokens[2], "up")
else:
- self.assertEqual(tokens[2], 'UP')
- pool = ''
+ self.assertEqual(tokens[2], "UP")
+ pool = ""
if len(tokens) == 14:
pool = tokens[13]
backends[tokens[1]] = pool
expected = {
- '127.0.0.1:10600': '',
- '127.0.0.1:10601': '',
- '127.0.0.1:10602': 'another-pool',
+ "127.0.0.1:10600": "",
+ "127.0.0.1:10601": "",
+ "127.0.0.1:10602": "another-pool",
# 10603 has been upgraded to 10653 and removed
# 10604 has been upgraded to 10654 and removed
- '127.0.0.2:10605': '',
- '127.0.0.1:10606': '',
- '127.0.0.1:10607': '',
- '127.0.0.1:10608': '',
- '127.0.0.1:10609': 'other-pool',
- '127.0.0.1:10610': '',
- '127.0.0.1:10611': '',
- '127.0.0.1:10612': '',
- '127.0.0.1:10613': '',
- '127.0.0.1:10614': '',
- '127.0.0.1:10615': '',
+ "127.0.0.2:10605": "",
+ "127.0.0.1:10606": "",
+ "127.0.0.1:10607": "",
+ "127.0.0.1:10608": "",
+ "127.0.0.1:10609": "other-pool",
+ "127.0.0.1:10610": "",
+ "127.0.0.1:10611": "",
+ "127.0.0.1:10612": "",
+ "127.0.0.1:10613": "",
+ "127.0.0.1:10614": "",
+ "127.0.0.1:10615": "",
# these two are not upgraded because there is no backend listening on the default ports (443 and 853)
- '127.0.0.1:10616': '',
- '127.0.0.1:10617': '',
- '127.0.0.1:10652': 'upgraded',
- '127.0.0.1:10653': 'another-pool',
- '127.0.0.2:10654': ''
+ "127.0.0.1:10616": "",
+ "127.0.0.1:10617": "",
+ "127.0.0.1:10652": "upgraded",
+ "127.0.0.1:10653": "another-pool",
+ "127.0.0.2:10654": "",
}
print(backends)
return backends == expected
time.sleep(5)
self.assertTrue(self.checkBackendsUpgraded())
+
class TestBackendDiscoveryByHostname(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
_verboseMode = True
def checkBackends(self):
- output = self.sendConsoleCommand('showServers()')
+ output = self.sendConsoleCommand("showServers()")
print(output)
backends = {}
for line in output.splitlines(False):
- if line.startswith('#') or line.startswith('All'):
+ if line.startswith("#") or line.startswith("All"):
continue
tokens = line.split()
self.assertTrue(len(tokens) == 13 or len(tokens) == 14)
backends[tokens[1]] = tokens[2]
if len(backends) == 4:
- for expected in ['9.9.9.9:53', '149.112.112.112:53', '[2620:fe::9]:53', '[2620:fe::fe]:53']:
+ for expected in ["9.9.9.9:53", "149.112.112.112:53", "[2620:fe::9]:53", "[2620:fe::fe]:53"]:
self.assertIn(expected, backends)
elif len(backends) == 2:
# looks like we are not getting the IPv6 addresses, thanks GitHub!
- for expected in ['9.9.9.9:53', '149.112.112.112:53']:
+ for expected in ["9.9.9.9:53", "149.112.112.112:53"]:
self.assertIn(expected, backends)
else:
return False
for backend in backends:
- if str(backend) in ['2620:fe::9]:53', '[2620:fe::fe]:53']:
+ if str(backend) in ["2620:fe::9]:53", "[2620:fe::fe]:53"]:
# IPv6 is very flaky on GH actions these days (202505),
# let's not require these to be up
continue
- if backends[backend] != 'up':
+ if backends[backend] != "up":
return False
return True
import clientsubnetoption
from dnsdisttests import DNSDistTest
-class TestBasics(DNSDistTest):
+class TestBasics(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
truncateTC(true)
which is dropped by configuration. We expect
no response.
"""
- for name in ['drop.test.powerdns.com.', 'drop2.test.powerdns.com.']:
- query = dns.message.make_query(name, 'A', 'IN')
+ for name in ["drop.test.powerdns.com.", "drop2.test.powerdns.com."]:
+ query = dns.message.make_query(name, "A", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
"""
Basics: A query with an ECS value
"""
- name = 'awithecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.4')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso])
+ name = "awithecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("1.2.3.4")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
"""
Basics: A query without EDNS
"""
- name = 'simplea.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simplea.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
send an ANY query and check the result.
It should be truncated over UDP, not over TCP.
"""
- name = 'any.tests.powerdns.com.'
- query = dns.message.make_query(name, 'ANY', 'IN')
+ name = "any.tests.powerdns.com."
+ query = dns.message.make_query(name, "ANY", "IN")
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
self.assertEqual(receivedResponse, expectedResponse)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
with TC set and additional content,
and check that the received response has been fixed.
"""
- name = 'atruncatetc.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "atruncatetc.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
response.flags |= dns.flags.TC
Note that the query and initial response had EDNS,
so the final response should have it too.
"""
- name = 'atruncatetc.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=True)
+ name = "atruncatetc.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=True)
# force a different responder payload than the one in the query,
# so we check that we don't just mirror it
response = dns.message.make_response(query, our_payload=4242)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
response.flags |= dns.flags.TC
We send a query for evil4242.powerdns.com
and check that the response is "refused".
"""
- name = 'evil4242.regex.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "evil4242.regex.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
dnsdist is configured to reply 1.2.3.4 for A query for exactly ds9a.nl
"""
- name = 'ds9a.nl.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "ds9a.nl."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NOERROR)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.4')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
We send a TXT query for "nameAndQtype.powerdns.com."
and check that the response is 'not implemented'.
"""
- name = 'nameAndQtype.tests.powerdns.com.'
- query = dns.message.make_query(name, 'TXT', 'IN')
+ name = "nameAndQtype.tests.powerdns.com."
+ query = dns.message.make_query(name, "TXT", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NOTIMP)
We send a A query for "nameAndQtype.tests.powerdns.com."
and check that the response is OK.
"""
- name = 'nameAndQtype.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nameAndQtype.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
We send a TXT query for "OtherNameAndQtype.tests.powerdns.com."
and check that the response is OK.
"""
- name = 'OtherNameAndQtype.tests.powerdns.com.'
- query = dns.message.make_query(name, 'TXT', 'IN')
+ name = "OtherNameAndQtype.tests.powerdns.com."
+ query = dns.message.make_query(name, "TXT", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- 'nothing to see here')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, "nothing to see here")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
queries to a specific backend in the air over UDP,
but does not really make sense over TCP.
"""
- name = 'query.unrelated.tests.powerdns.com.'
- unrelatedName = 'answer.unrelated.tests.powerdns.com.'
- query = dns.message.make_query(name, 'TXT', 'IN')
- unrelatedQuery = dns.message.make_query(unrelatedName, 'TXT', 'IN')
+ name = "query.unrelated.tests.powerdns.com."
+ unrelatedName = "answer.unrelated.tests.powerdns.com."
+ query = dns.message.make_query(name, "TXT", "IN")
+ unrelatedQuery = dns.message.make_query(unrelatedName, "TXT", "IN")
unrelatedResponse = dns.message.make_response(unrelatedQuery)
- rrset = dns.rrset.from_text(unrelatedName,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- 'nothing to see here')
+ rrset = dns.rrset.from_text(unrelatedName, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, "nothing to see here")
unrelatedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Basics: Header-only refused response
"""
- name = 'header-only-refused-response.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "header-only-refused-response.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.REFUSED)
response.question = []
"""
Basics: Header-only NoError response should be dropped
"""
- name = 'header-only-noerror-response.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "header-only-noerror-response.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.question = []
"""
Basics: Header-only NXD response should be dropped
"""
- name = 'header-only-nxd-response.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "header-only-nxd-response.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.NXDOMAIN)
response.question = []
"""
Basics: test if addAction accepts a DNSName
"""
- name = 'dnsname.addaction.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "dnsname.addaction.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
"""
Basics: test if addAction accepts a table of DNSNames
"""
- for name in ['dnsname-table{}.addaction.powerdns.com.'.format(i) for i in range(1,2)]:
- query = dns.message.make_query(name, 'A', 'IN')
+ for name in ["dnsname-table{}.addaction.powerdns.com.".format(i) for i in range(1, 2)]:
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
import dns
from dnsdisttests import DNSDistTest, pickAvailablePort
+
def responseCallback(request):
if len(request.question) != 1:
print("Skipping query with question count %d" % (len(request.question)))
return None
- healthCheck = str(request.question[0].name).endswith('a.root-servers.net.')
+ healthCheck = str(request.question[0].name).endswith("a.root-servers.net.")
if healthCheck:
response = dns.message.make_response(request)
return response.to_wire()
# now we create a broken response
response = dns.message.make_response(request)
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 32)
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 32)
response.use_edns(edns=True, payload=4096, options=[ecso])
- rrset = dns.rrset.from_text(request.question[0].name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(request.question[0].name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
raw = response.to_wire()
# first label length of this rrset is at 12 (dnsheader) + length(qname) + 2 (leading label length + trailing 0) + 2 (qtype) + 2 (qclass)
offset = 12 + len(str(request.question[0].name)) + 2 + 2 + 2
- altered = raw[:offset] + b'\xff' + raw[offset+1:]
+ altered = raw[:offset] + b"\xff" + raw[offset + 1 :]
return altered
-class TestBrokenAnswerECS(DNSDistTest):
+class TestBrokenAnswerECS(DNSDistTest):
# this test suite uses a different responder port
# because, contrary to the other ones, its
# responders send raw, broken data
setECSSourcePrefixV4(32)
newServer{address="127.0.0.1:%d", useClientSubnet=true}
"""
+
@classmethod
def startResponders(cls):
print("Launching responders..")
# Returns broken data for non-healthcheck queries
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, responseCallback])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, responseCallback],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
# Returns broken data for non-healthcheck queries
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, responseCallback])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, responseCallback],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
"""
Broken Answer: Invalid UDP answer with ECS
"""
- name = 'invalid-ecs-udp.broken-answer.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ name = "invalid-ecs-udp.broken-answer.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
"""
Broken Answer: Invalid TCP answer with ECS
"""
- name = 'invalid-ecs-tcp.broken-answer.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ name = "invalid-ecs-tcp.broken-answer.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
import time
from dnsdisttests import DNSDistTest
+
def writeCDB(fname, variant=1):
- cdb = cdbx.CDB.make(fname+'.tmp')
- cdb.add(socket.inet_aton(f'127.0.0.{variant}'), b'this is the value of the source address tag')
- cdb.add(b'\x05qname\x03cdb\x05tests\x08powerdns\x03com\x00', b'this is the value of the qname tag')
- cdb.add(b'\x06suffix\x03cdb\x05tests\x08powerdns\x03com\x00', b'this is the value of the suffix tag')
- cdb.add(b'this is the value of the qname tag', b'this is the value of the second tag')
+ cdb = cdbx.CDB.make(fname + ".tmp")
+ cdb.add(socket.inet_aton(f"127.0.0.{variant}"), b"this is the value of the source address tag")
+ cdb.add(b"\x05qname\x03cdb\x05tests\x08powerdns\x03com\x00", b"this is the value of the qname tag")
+ cdb.add(b"\x06suffix\x03cdb\x05tests\x08powerdns\x03com\x00", b"this is the value of the suffix tag")
+ cdb.add(b"this is the value of the qname tag", b"this is the value of the second tag")
cdb.commit().close()
- os.rename(fname+'.tmp', fname)
+ os.rename(fname + ".tmp", fname)
cdb.close()
-@unittest.skipIf('SKIP_CDB_TESTS' in os.environ, 'CDB tests are disabled')
-class CDBTest(DNSDistTest):
- _cdbFileName = '/tmp/test-cdb-db'
+@unittest.skipIf("SKIP_CDB_TESTS" in os.environ, "CDB tests are disabled")
+class CDBTest(DNSDistTest):
+ _cdbFileName = "/tmp/test-cdb-db"
_cdbRefreshDelay = 1
_config_template = """
newServer{address="127.0.0.1:%d"}
-- otherwise, spoof a different response
addAction(AllRule(), SpoofAction('9.9.9.9'))
"""
- _config_params = ['_testServerPort', '_cdbFileName', '_cdbRefreshDelay']
+ _config_params = ["_testServerPort", "_cdbFileName", "_cdbRefreshDelay"]
-class TestCDBSimple(CDBTest):
+class TestCDBSimple(CDBTest):
@classmethod
def setUpCDB(cls):
writeCDB(cls._cdbFileName, 1)
"""
CDB: Match on source address
"""
- name = 'source-ip.cdb.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "source-ip.cdb.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '5.6.7.8')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "5.6.7.8")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
CDB: Match on qname then does a second lookup using the value of the first lookup
"""
- name = 'qname.cdb.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "qname.cdb.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.4')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
CDB: Match on the qname via a suffix lookup
"""
- name = 'sub.sub.suffix.cdb.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "sub.sub.suffix.cdb.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '42.42.42.42')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "42.42.42.42")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(receivedResponse)
self.assertEqual(expectedResponse, receivedResponse)
-class TestCDBReload(CDBTest):
+class TestCDBReload(CDBTest):
@classmethod
def setUpCDB(cls):
writeCDB(cls._cdbFileName, 1)
"""
CDB: Test that the CDB is correctly reloaded
"""
- name = 'reload.cdb.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "reload.cdb.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '5.6.7.8')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "5.6.7.8")
expectedResponse.answer.append(rrset)
# only the source address should match
time.sleep(self._cdbRefreshDelay + 1)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '9.9.9.9')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "9.9.9.9")
expectedResponse.answer.append(rrset)
# nothing (qname, suffix or source IP) should match
import dns
from dnsdisttests import DNSDistTest
-class TestCacheHitResponses(DNSDistTest):
+class TestCacheHitResponses(DNSDistTest):
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
CacheHitResponse: Drop when served from the cache
"""
ttl = 5
- name = 'dropwhencached.cachehitresponses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "dropwhencached.cachehitresponses.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
self.assertEqual(total, 2)
-class TestStaleCacheHitResponses(DNSDistTest):
+class TestStaleCacheHitResponses(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
CacheHitResponse: Drop when served from the stale cache entry
"""
ttl = 5
- name = 'dropstaleentry.cachehitresponses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "dropstaleentry.cachehitresponses.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
import dns
from dnsdisttests import DNSDistTest
-class TestCacheInsertedResponses(DNSDistTest):
+class TestCacheInsertedResponses(DNSDistTest):
capTTLMax = 3600
capTTLMin = 60
_config_template = """
addCacheInsertedResponseAction(SuffixMatchNodeRule("cacheinsertedresponses.tests.powerdns.com."), LimitTTLResponseAction(%d, %d))
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['capTTLMax', 'capTTLMin', '_testServerPort']
+ _config_params = ["capTTLMax", "capTTLMin", "_testServerPort"]
def testTTLSetAfterInsertion(self):
"""
CacheInsertedResponse: Check that the TTL is capped after inserting into the cache
"""
initialTTL = 86400
- name = 'reduce-ttl-after-insertion.cacheinsertedresponses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "reduce-ttl-after-insertion.cacheinsertedresponses.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- initialTTL,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, initialTTL, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
responseOnMiss = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- self.capTTLMax,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, self.capTTLMax, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
responseOnMiss.answer.append(rrset)
# first query to fill the cache
CacheInsertedResponse: Check that the TTL can be raised after inserting into the cache
"""
initialTTL = 0
- name = 'raise-ttl-after-insertion.cacheinsertedresponses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "raise-ttl-after-insertion.cacheinsertedresponses.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- initialTTL,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, initialTTL, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
responseOnMiss = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- self.capTTLMax,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, self.capTTLMax, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
responseOnMiss.answer.append(rrset)
# first query to fill the cache
import dns
from dnsdisttests import DNSDistTest, pickAvailablePort
+
class TestCacheMissSelfAnswered(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
setKey("%s")
CacheMiss: Refused when not in cache
"""
# check that the rule is in place
- lines = self.sendConsoleCommand('showCacheMissRules()').splitlines()
+ lines = self.sendConsoleCommand("showCacheMissRules()").splitlines()
self.assertEqual(len(lines), 2)
- self.assertIn('myFirstRule', lines[1])
+ self.assertIn("myFirstRule", lines[1])
- name = 'refused.cache-miss.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "refused.cache-miss.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
self.assertEqual(receivedResponse, expectedResponse)
# now we remove the rule
- self.sendConsoleCommand('clearCacheMissRules()')
- lines = self.sendConsoleCommand('showCacheMissRules()').splitlines()
+ self.sendConsoleCommand("clearCacheMissRules()")
+ lines = self.sendConsoleCommand("showCacheMissRules()").splitlines()
self.assertEqual(len(lines), 1)
# get a response inserted into the cache
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:db8::1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:db8::1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response=response)
self.assertTrue(receivedQuery)
self.assertEqual(receivedResponse, response)
# add the rule back
- self.sendConsoleCommand('addCacheMissAction(SuffixMatchNodeRule("refused.cache-miss.tests.powerdns.com."), RCodeAction(DNSRCode.REFUSED), {name="myFirstRule"})')
- lines = self.sendConsoleCommand('showCacheMissRules()').splitlines()
+ self.sendConsoleCommand(
+ 'addCacheMissAction(SuffixMatchNodeRule("refused.cache-miss.tests.powerdns.com."), RCodeAction(DNSRCode.REFUSED), {name="myFirstRule"})'
+ )
+ lines = self.sendConsoleCommand("showCacheMissRules()").splitlines()
self.assertEqual(len(lines), 2)
- self.assertIn('myFirstRule', lines[1])
+ self.assertIn("myFirstRule", lines[1])
# and check that we do get the cached response
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertTrue(receivedResponse)
self.assertEqual(receivedResponse, response)
+
class TestCacheMissGoToADifferentPool(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_testServer2Port = pickAvailablePort()
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_testServer2Port']
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort", "_testServer2Port"]
_config_template = """
setKey("%s")
"""
CacheMiss: Routed to a different pool when not in cache
"""
- name = 'routed-to-slow.cache-miss.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "routed-to-slow.cache-miss.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:db8::1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:db8::1")
response.answer.append(rrset)
# first query goes to the 'slow' server
self.assertTrue(receivedResponse)
self.assertEqual(receivedResponse, response)
- backendLines = self.sendConsoleCommand('showServers()').splitlines(False)
+ backendLines = self.sendConsoleCommand("showServers()").splitlines(False)
self.assertEqual(len(backendLines), 4)
for line in backendLines:
- if line.startswith('#') or line.startswith('All'):
+ if line.startswith("#") or line.startswith("All"):
continue
tokens = line.split()
self.assertEqual(len(tokens), 15)
pool = tokens[13]
queries = int(tokens[9])
- if pool == 'slow':
+ if pool == "slow":
self.assertEqual(queries, 1)
else:
self.assertEqual(queries, 0)
import randompaddingoption
from dnsdisttests import DNSDistTest
-class TestCachePadding(DNSDistTest):
+class TestCachePadding(DNSDistTest):
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
"""
Cache padding
"""
- name = 'padding.cache-padding.tests.powerdns.com.'
+ name = "padding.cache-padding.tests.powerdns.com."
po = paddingoption.PaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[po])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[po])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
# generate a new padding payload, with random bytes
rpo = randompaddingoption.RandomPaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[rpo])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[rpo])
response = dns.message.make_response(query)
response.answer.append(rrset)
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
-class TestCacheNotSkippingPadding(DNSDistTest):
+class TestCacheNotSkippingPadding(DNSDistTest):
_config_template = """
-- only skip EDNS cookies, not padding
pc = newPacketCache(100, {maxTTL=86400, minTTL=1, skipOptions={10}})
"""
Cache padding: not skipping the padding
"""
- name = 'not-skipping-padding.cache-padding.tests.powerdns.com.'
+ name = "not-skipping-padding.cache-padding.tests.powerdns.com."
po = paddingoption.PaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[po])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[po])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
# generate a new padding payload, with random bytes
rpo = randompaddingoption.RandomPaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[rpo])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[rpo])
response = dns.message.make_response(query)
# identical query except for the padding content which should NOT be skipped, should NOT be cached
import requests
from dnsdisttests import DNSDistTest, pickAvailablePort
-class TestCaching(DNSDistTest):
+class TestCaching(DNSDistTest):
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
the first one.
"""
numberOfQueries = 10
- name = 'cached.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "cached.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
"""
Cache: Empty TC=1 is not cached by default
"""
- name = 'empty-tc.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "empty-tc.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
response.flags |= dns.flags.TC
the first one.
"""
numberOfQueries = 10
- name = 'cached-do.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, want_dnssec=True)
+ name = "cached-do.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, payload=4096, want_dnssec=True)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
dnsdist is configured to not cache entries for nocache.cache.tests.powerdns.com.
we are sending several requests and checking that the backend get them all.
"""
- name = 'nocache.cache.tests.powerdns.com.'
+ name = "nocache.cache.tests.powerdns.com."
numberOfQueries = 10
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
for _ in range(numberOfQueries):
dnsdist is configured to not cache entries for nocachevialua.cache.tests.powerdns.com.
we are sending several requests and checking that the backend get them all.
"""
- name = 'nocachevialua.cache.tests.powerdns.com.'
+ name = "nocachevialua.cache.tests.powerdns.com."
numberOfQueries = 10
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
for _ in range(numberOfQueries):
dnsdist is configured to not cache entries for answer matching nocache-response.cache.tests.powerdns.com.
we are sending several requests and checking that the backend get them all.
"""
- name = 'nocache-response.cache.tests.powerdns.com.'
+ name = "nocache-response.cache.tests.powerdns.com."
numberOfQueries = 10
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
for _ in range(numberOfQueries):
dnsdist should not cache responses to AXFR queries.
"""
- name = 'axfr.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AXFR', 'IN')
+ name = "axfr.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AXFR", "IN")
response = dns.message.make_response(query)
- soa = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+ soa = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
response.answer.append(soa)
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1'))
+ response.answer.append(dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"))
response.answer.append(soa)
numberOfQueries = 5
dnsdist should not cache responses to IXFR queries.
"""
- name = 'ixfr.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'IXFR', 'IN')
+ name = "ixfr.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "IXFR", "IN")
response = dns.message.make_response(query)
- soa = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+ soa = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
response.answer.append(soa)
- response.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1'))
+ response.answer.append(dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"))
response.answer.append(soa)
numberOfQueries = 5
"""
ttl = 2
misses = 0
- name = 'cacheexpiration.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "cacheexpiration.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
"""
ttl = 2
misses = 0
- name = 'cacheexpirationdifferentsets.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "cacheexpirationdifferentsets.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'cname.cacheexpirationdifferentsets.cache.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.CNAME,
+ "cname.cacheexpirationdifferentsets.cache.tests.powerdns.com.",
+ )
response.answer.append(rrset)
- rrset = dns.rrset.from_text('cname.cacheexpirationdifferentsets.cache.tests.powerdns.com.',
- ttl + 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.2.0.1')
+ rrset = dns.rrset.from_text(
+ "cname.cacheexpirationdifferentsets.cache.tests.powerdns.com.",
+ ttl + 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ "192.2.0.1",
+ )
response.additional.append(rrset)
# first query to fill the cache
"""
ttl = 600
misses = 0
- name = 'cachedecreasettl.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "cachedecreasettl.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
matches.
"""
ttl = 600
- name = 'cachedifferentcase.cache.tests.powerdns.com.'
- differentCaseName = 'CacheDifferentCASE.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
- differentCaseQuery = dns.message.make_query(differentCaseName, 'AAAA', 'IN')
+ name = "cachedifferentcase.cache.tests.powerdns.com."
+ differentCaseName = "CacheDifferentCASE.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
+ differentCaseQuery = dns.message.make_query(differentCaseName, "AAAA", "IN")
response = dns.message.make_response(query)
differentCaseResponse = dns.message.make_response(differentCaseQuery)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
differentCaseResponse.answer.append(rrset)
We should be able to get answers as large as 4096 bytes
"""
numberOfQueries = 10
- name = 'large-answer.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'TXT', 'IN', payload=4096)
+ name = "large-answer.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "TXT", "IN", payload=4096)
response = dns.message.make_response(query)
# we prepare a large answer
content = ""
for i in range(44):
if len(content) > 0:
- content = content + ', '
- content = content + (str(i)*50)
+ content = content + ", "
+ content = content + (str(i) * 50)
# pad up to 4096 (less 11 for EDNS)
- content = content + 'A'*31
+ content = content + "A" * 31
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- content)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, content)
response.answer.append(rrset)
self.assertEqual(len(response.to_wire()), 4096)
Cache: The content of cookies should be ignored by the cache
"""
ttl = 600
- name = 'cache-different-cookies.cache.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
+ name = "cache-different-cookies.cache.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, payload=4096, options=[eco])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
- eco = cookiesoption.CookiesOption(b'badc0fee', b'badc0fee')
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
+ eco = cookiesoption.CookiesOption(b"badc0fee", b"badc0fee")
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, payload=4096, options=[eco])
# second query should be served from the cache
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
receivedResponse.id = response.id
Cache: A query with a cookie should not match one without any cookie
"""
ttl = 600
- name = 'cache-cookie.cache.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco])
+ name = "cache-cookie.cache.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[eco])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
# first query to fill the cache
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[])
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# second query should NOT be served from the cache
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
Cache: The content of cookies should be ignored by the cache but not the ECS one
"""
ttl = 600
- name = 'cache-different-cookies-different-ecs.cache.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
+ name = "cache-different-cookies-different-ecs.cache.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, payload=4096, options=[eco, ecso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.2", 32)
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, payload=4096, options=[eco, ecso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
-class TestCachingHashingOptions(DNSDistTest):
+class TestCachingHashingOptions(DNSDistTest):
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1, cookieHashing=true, skipOptions={8}})
getPool(""):setCache(pc)
Cache: ECS should be ignored by the cache even if cookie is present
"""
ttl = 600
- name = 'cache-different-ecs.cache.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
+ name = "cache-different-ecs.cache.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.2", 32)
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, payload=4096, options=[eco, ecso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, payload=4096, options=[eco, ecso])
# second query should be served from the cache
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
receivedResponse.id = response.id
self.assertEqual(receivedResponse, response)
-class TestCachingHashingCookies(DNSDistTest):
+class TestCachingHashingCookies(DNSDistTest):
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1, cookieHashing=true})
getPool(""):setCache(pc)
the first one.
"""
numberOfQueries = 10
- name = 'cached.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "cached.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
self.assertEqual(total, 1)
-
def testCacheDifferentCookies(self):
"""
Cache: The content of cookies should NOT be ignored by the cache (cookieHashing is set)
"""
ttl = 600
- name = 'cache-different-cookies.cache-cookie-hashing.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
+ name = "cache-different-cookies.cache-cookie-hashing.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, payload=4096, options=[eco])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
- eco = cookiesoption.CookiesOption(b'badc0fee', b'badc0fee')
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
+ eco = cookiesoption.CookiesOption(b"badc0fee", b"badc0fee")
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, payload=4096, options=[eco])
differentResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
differentResponse.answer.append(rrset)
# second query should NOT be served from the cache
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, differentResponse)
Cache: A query with a cookie should not match one without any cookie (cookieHashing=true)
"""
ttl = 600
- name = 'cache-cookie.cache-cookie-hashing.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco])
+ name = "cache-cookie.cache-cookie-hashing.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[eco])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
# first query to fill the cache
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[])
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# second query should NOT be served from the cache
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
Cache: The content of cookies should NOT be ignored by the cache (cookieHashing=true), even with ECS there
"""
ttl = 600
- name = 'cache-different-cookies-different-ecs.cache-cookie-hashing.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
+ name = "cache-different-cookies-different-ecs.cache-cookie-hashing.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, payload=4096, options=[eco, ecso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.2", 32)
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, payload=4096, options=[eco, ecso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
-class TestTempFailureCacheTTLAction(DNSDistTest):
+class TestTempFailureCacheTTLAction(DNSDistTest):
_extraStartupSleep = 1
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
(cache miss) and verify that the cache is hit for the following query,
but the TTL then expires before the larger "good" packetcache TTL.
"""
- name = 'servfail.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "servfail.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.SERVFAIL)
self.assertTrue(receivedResponse)
self.assertEqual(receivedResponse, response)
-class TestCachingWithExistingEDNS(DNSDistTest):
+class TestCachingWithExistingEDNS(DNSDistTest):
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
newServer{address="127.0.0.1:%d"}
"""
+
def testCacheWithEDNS(self):
"""
Cache: Cache should not match different EDNS value
Payload size is not served from the cache.
"""
misses = 0
- name = 'cachedifferentedns.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=512)
+ name = "cachedifferentedns.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=512)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertEqual(response, receivedResponse)
misses += 1
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertEqual(total, misses)
-class TestCachingCacheFull(DNSDistTest):
+class TestCachingCacheFull(DNSDistTest):
_config_template = """
pc = newPacketCache(1, {maxTTL=86400, minTTL=1, numberOfShards=1})
getPool(""):setCache(pc)
newServer{address="127.0.0.1:%d"}
"""
+
def testCacheFull(self):
"""
Cache: No new entries are cached when the cache is full
"""
misses = 0
- name = 'cachenotfullyet.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "cachenotfullyet.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# Miss
self.assertEqual(receivedResponse, response)
# ok, now the cache is full, send another query
- name = 'cachefull.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "cachefull.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# Miss
self.assertEqual(total, misses)
-class TestCachingNoStale(DNSDistTest):
+class TestCachingNoStale(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
controlSocket("127.0.0.1:%d")
newServer{address="127.0.0.1:%d"}
"""
+
def testCacheNoStale(self):
"""
Cache: Cache entry, set backend down, we should not get a stale entry
"""
ttl = 2
- name = 'nostale.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nostale.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# Miss
class TestCachingStale(DNSDistTest):
-
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_staleCacheTTL = 60
- _config_params = ['_staleCacheTTL', '_consoleKeyB64', '_consolePort', '_testServerPort', '_testServerPort']
+ _config_params = ["_staleCacheTTL", "_consoleKeyB64", "_consolePort", "_testServerPort", "_testServerPort"]
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=0, staleTTL=%d})
getPool(""):setCache(pc)
"""
misses = 0
ttl = 2
- name = 'stale.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "stale.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# Miss
"""
misses = 0
ttl = 2
- name = 'stale-tcp-only.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "stale-tcp-only.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# Miss
self.assertEqual(total, misses)
-class TestCachingStaleExpunged(DNSDistTest):
+class TestCachingStaleExpunged(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_staleCacheTTL = 60
- _config_params = ['_staleCacheTTL', '_consoleKeyB64', '_consolePort', '_testServerPort']
+ _config_params = ["_staleCacheTTL", "_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=0, staleTTL=%d})
getPool(""):setCache(pc)
controlSocket("127.0.0.1:%d")
newServer{address="127.0.0.1:%d"}
"""
+
def testCacheStale(self):
"""
Cache: Cache entry, set backend down, wait for the cache cleaning to run and remove the entry, get no entry
misses = 0
drops = 0
ttl = 2
- name = 'stale-but-expunged.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "stale-but-expunged.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# Miss
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
misses += 1
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), misses + drops)
+ self.assertEqual(
+ int(self.sendConsoleCommand('getPool(""):getCache():getStats()["misses"]').strip("\n")), misses + drops
+ )
# next queries should hit the cache
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
# the cache should have one entry
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 1)
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 1)
+ self.assertEqual(int(self.sendConsoleCommand('getPool(""):getCache():getStats()["entries"]').strip("\n")), 1)
+ self.assertEqual(int(self.sendConsoleCommand('getPool(""):getCache():getStats()["hits"]').strip("\n")), 1)
# ok, we mark the backend as down
self.sendConsoleCommand("getServer(0):setDown()")
# wait a bit more to be sure that the cache cleaning algo has been run
time.sleep(1)
# the cache should be empty now
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 0)
+ self.assertEqual(int(self.sendConsoleCommand('getPool(""):getCache():getStats()["entries"]').strip("\n")), 0)
# we should get a DROP (backend is down, nothing in the cache anymore)
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
drops += 1
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), misses + drops)
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 1)
+ self.assertEqual(
+ int(self.sendConsoleCommand('getPool(""):getCache():getStats()["misses"]').strip("\n")), misses + drops
+ )
+ self.assertEqual(int(self.sendConsoleCommand('getPool(""):getCache():getStats()["hits"]').strip("\n")), 1)
total = 0
for key in self._responsesCounter:
self.assertEqual(total, misses)
-class TestCachingStaleExpungePrevented(DNSDistTest):
+class TestCachingStaleExpungePrevented(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=0, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=3600, parseECS=false, keepStaleData=true})
getPool(""):setCache(pc)
controlSocket("127.0.0.1:%d")
newServer{address="127.0.0.1:%d"}
"""
+
def testCacheStale(self):
"""
Cache: Cache entry, set backend down, wait for the cache cleaning to run and remove the entry, still get a cache HIT because the stale entry was not removed
"""
misses = 0
ttl = 2
- name = 'stale-not-expunged.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "stale-not-expunged.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# Miss
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
misses += 1
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), 1)
+ self.assertEqual(int(self.sendConsoleCommand('getPool(""):getCache():getStats()["misses"]').strip("\n")), 1)
# next queries should hit the cache
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
# the cache should have one entry
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 1)
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 1)
+ self.assertEqual(int(self.sendConsoleCommand('getPool(""):getCache():getStats()["entries"]').strip("\n")), 1)
+ self.assertEqual(int(self.sendConsoleCommand('getPool(""):getCache():getStats()["hits"]').strip("\n")), 1)
# ok, we mark the backend as down
self.sendConsoleCommand("getServer(0):setDown()")
time.sleep(1)
# the cache should NOT be empty because the removal of the expired entry should have been prevented
# since all backends for this pool are down
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 1)
+ self.assertEqual(int(self.sendConsoleCommand('getPool(""):getCache():getStats()["entries"]').strip("\n")), 1)
# we should get a HIT
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), 1)
- self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 2)
+ self.assertEqual(int(self.sendConsoleCommand('getPool(""):getCache():getStats()["misses"]').strip("\n")), 1)
+ self.assertEqual(int(self.sendConsoleCommand('getPool(""):getCache():getStats()["hits"]').strip("\n")), 2)
total = 0
for key in self._responsesCounter:
self.assertEqual(total, misses)
-class TestCacheManagement(DNSDistTest):
+class TestCacheManagement(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
controlSocket("127.0.0.1:%d")
newServer{address="127.0.0.1:%d"}
"""
+
def testCacheExpunge(self):
"""
Cache: Expunge
"""
misses = 0
ttl = 600
- name = 'expunge.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "expunge.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# Miss
self.assertEqual(receivedResponse, response)
# remove cached entries
- self.sendConsoleCommand("getPool(\"\"):getCache():expunge(0)")
+ self.sendConsoleCommand('getPool(""):getCache():expunge(0)')
# Miss
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
"""
misses = 0
ttl = 600
- name = 'expungebyname.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "expungebyname.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- name2 = 'expungebynameother.cache.tests.powerdns.com.'
- query2 = dns.message.make_query(name2, 'A', 'IN')
+ name2 = "expungebynameother.cache.tests.powerdns.com."
+ query2 = dns.message.make_query(name2, "A", "IN")
response2 = dns.message.make_response(query2)
- rrset2 = dns.rrset.from_text(name2,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset2 = dns.rrset.from_text(name2, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response2.answer.append(rrset2)
# Miss
self.assertEqual(receivedResponse, response2)
# remove cached entries from name
- self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"" + name + "\"))")
+ self.sendConsoleCommand('getPool(""):getCache():expungeByName(newDNSName("' + name + '"))')
# Miss for name
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
"""
misses = 0
ttl = 600
- name = 'expungebynameandtype.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "expungebynameandtype.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- query2 = dns.message.make_query(name, 'AAAA', 'IN')
+ query2 = dns.message.make_query(name, "AAAA", "IN")
response2 = dns.message.make_response(query2)
- rrset2 = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset2 = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response2.answer.append(rrset2)
# Miss
self.assertEqual(receivedResponse, response2)
# remove cached entries from name A
- self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"" + name + "\"), DNSQType.A)")
+ self.sendConsoleCommand('getPool(""):getCache():expungeByName(newDNSName("' + name + '"), DNSQType.A)')
# Miss for name A
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
"""
misses = 0
ttl = 600
- name = 'expungebyname.suffix.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "expungebyname.suffix.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- name2 = 'expungebyname.suffixother.cache.tests.powerdns.com.'
- query2 = dns.message.make_query(name2, 'A', 'IN')
+ name2 = "expungebyname.suffixother.cache.tests.powerdns.com."
+ query2 = dns.message.make_query(name2, "A", "IN")
response2 = dns.message.make_response(query2)
- rrset2 = dns.rrset.from_text(name2,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset2 = dns.rrset.from_text(name2, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response2.answer.append(rrset2)
# Miss
self.assertEqual(receivedResponse, response2)
# remove cached entries from name
- self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffix.cache.tests.powerdns.com.\"), DNSQType.ANY, true)")
+ self.sendConsoleCommand(
+ 'getPool(""):getCache():expungeByName(newDNSName("suffix.cache.tests.powerdns.com."), DNSQType.ANY, true)'
+ )
# Miss for name
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
"""
misses = 0
ttl = 600
- name = 'expungebynameandtype.suffixtype.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "expungebynameandtype.suffixtype.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- query2 = dns.message.make_query(name, 'AAAA', 'IN')
+ query2 = dns.message.make_query(name, "AAAA", "IN")
response2 = dns.message.make_response(query2)
- rrset2 = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset2 = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response2.answer.append(rrset2)
# Miss
self.assertEqual(receivedResponse, response2)
# remove cached entries from name A
- self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffixtype.cache.tests.powerdns.com.\"), DNSQType.A, true)")
+ self.sendConsoleCommand(
+ 'getPool(""):getCache():expungeByName(newDNSName("suffixtype.cache.tests.powerdns.com."), DNSQType.A, true)'
+ )
# Miss for name A
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
total += self._responsesCounter[key]
self.assertEqual(total, misses)
-class TestCachingTTL(DNSDistTest):
+class TestCachingTTL(DNSDistTest):
_maxCacheTTL = 86400
_minCacheTTL = 600
- _config_params = ['_maxCacheTTL', '_minCacheTTL', '_testServerPort']
+ _config_params = ["_maxCacheTTL", "_minCacheTTL", "_testServerPort"]
_config_template = """
pc = newPacketCache(1000, {maxTTL=%d, minTTL=%d})
getPool(""):setCache(pc)
newServer{address="127.0.0.1:%d"}
"""
+
def testCacheShortTTL(self):
"""
Cache: Entries with a TTL shorter than minTTL
"""
misses = 0
ttl = 60
- name = 'ttltooshort.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "ttltooshort.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# Miss
"""
misses = 0
- name = 'nxwithnorr.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nxwithnorr.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.NXDOMAIN)
self.assertEqual(total, misses)
-class TestCachingLongTTL(DNSDistTest):
+class TestCachingLongTTL(DNSDistTest):
_maxCacheTTL = 2
- _config_params = ['_maxCacheTTL', '_testServerPort']
+ _config_params = ["_maxCacheTTL", "_testServerPort"]
_config_template = """
pc = newPacketCache(1000, {maxTTL=%d})
getPool(""):setCache(pc)
newServer{address="127.0.0.1:%d"}
"""
+
def testCacheLongTTL(self):
"""
Cache: Entries with a longer TTL than the maximum
"""
misses = 0
ttl = 172800
- name = 'longttl.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "longttl.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# Miss
self.assertEqual(total, misses)
-class TestCachingFailureTTL(DNSDistTest):
+class TestCachingFailureTTL(DNSDistTest):
_failureCacheTTL = 2
- _config_params = ['_failureCacheTTL', '_testServerPort']
+ _config_params = ["_failureCacheTTL", "_testServerPort"]
_config_template = """
pc = newPacketCache(1000, {maxTTL=86400, minTTL=0, temporaryFailureTTL=%d, staleTTL=60})
getPool(""):setCache(pc)
newServer{address="127.0.0.1:%d"}
"""
+
def testCacheServFailTTL(self):
"""
Cache: ServFail TTL
"""
misses = 0
- name = 'servfail.failure.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "servfail.failure.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.SERVFAIL)
"""
misses = 0
- name = 'refused.failure.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "refused.failure.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.REFUSED)
"""
misses = 0
- name = 'header-only-refused.failure.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "header-only-refused.failure.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.REFUSED)
response.question = []
self.assertEqual(total, misses)
-class TestCachingNegativeTTL(DNSDistTest):
+class TestCachingNegativeTTL(DNSDistTest):
_negCacheTTL = 2
- _config_params = ['_negCacheTTL', '_testServerPort']
+ _config_params = ["_negCacheTTL", "_testServerPort"]
_config_template = """
pc = newPacketCache(1000, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=%d})
getPool(""):setCache(pc)
"""
misses = 0
- name = 'nxdomain.negativettl.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nxdomain.negativettl.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.NXDOMAIN)
- soa = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+ soa = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
response.authority.append(soa)
# Miss
"""
misses = 0
- name = 'nodata.negativettl.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nodata.negativettl.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.NOERROR)
- soa = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+ soa = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
response.authority.append(soa)
# Miss
self.assertEqual(total, misses)
-class TestCachingDontAge(DNSDistTest):
+class TestCachingDontAge(DNSDistTest):
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=true})
getPool(""):setCache(pc)
newServer{address="127.0.0.1:%d"}
"""
+
def testCacheDoesntDecreaseTTL(self):
"""
Cache: Cache doesn't decrease TTL with 'don't age' set
"""
ttl = 600
misses = 0
- name = 'cachedoesntdecreasettl.cache-dont-age.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "cachedoesntdecreasettl.cache-dont-age.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
self.assertEqual(total, misses)
-class TestCachingECSWithoutPoolECS(DNSDistTest):
+class TestCachingECSWithoutPoolECS(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort", "_testServerPort"]
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
Cache: Cached entry with ECS is a miss when no backend are available
"""
ttl = 600
- name = 'cached.cache-ecs-without-pool-ecs.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "cached.cache-ecs-without-pool-ecs.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
-class TestCachingECSWithPoolECS(DNSDistTest):
+class TestCachingECSWithPoolECS(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
Cache: Cached entry with ECS is a hit when no backend are available
"""
ttl = 600
- name = 'cached.cache-ecs-with-pool-ecs.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "cached.cache-ecs-with-pool-ecs.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
-class TestCachingCollisionNoECSParsing(DNSDistTest):
+class TestCachingCollisionNoECSParsing(DNSDistTest):
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
"""
Cache: Collision with no ECS parsing
"""
- name = 'collision-no-ecs-parsing.cache.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('10.0.226.63', 32)
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "collision-no-ecs-parsing.cache.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("10.0.226.63", 32)
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso], payload=512)
query.flags = dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query should to fill the cache
# second query will hash to the same key, triggering a collision which
# will not be detected because the qname, qtype, qclass and flags will
# match and EDNS Client Subnet parsing has not been enabled
- ecso2 = clientsubnetoption.ClientSubnetOption('10.1.60.19', 32)
- query2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
+ ecso2 = clientsubnetoption.ClientSubnetOption("10.1.60.19", 32)
+ query2 = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso2], payload=512)
query2.flags = dns.flags.RD
(_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
receivedResponse.id = response.id
self.assertEqual(receivedResponse, response)
-class TestCachingCollisionWithECSParsing(DNSDistTest):
+class TestCachingCollisionWithECSParsing(DNSDistTest):
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=3600, parseECS=true})
getPool(""):setCache(pc)
"""
Cache: Collision with ECS parsing
"""
- name = 'collision-with-ecs-parsing.cache.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('10.0.150.206', 32)
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "collision-with-ecs-parsing.cache.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("10.0.150.206", 32)
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso], payload=512)
query.flags = dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query should to fill the cache
# second query will hash to the same key, triggering a collision which
# _will_ be detected this time because the qname, qtype, qclass and flags will
# match but EDNS Client Subnet parsing is now enabled and will detect the issue
- ecso2 = clientsubnetoption.ClientSubnetOption('10.0.212.51', 32)
- query2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
+ ecso2 = clientsubnetoption.ClientSubnetOption("10.0.212.51", 32)
+ query2 = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso2], payload=512)
query2.flags = dns.flags.RD
response2 = dns.message.make_response(query2)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
response2.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
self.assertEqual(receivedResponse, response2)
-class TestCachingScopeZero(DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+class TestCachingScopeZero(DNSDistTest):
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_config_template = """
local backendPort = %d
-- Be careful to enable ECS parsing in the packet cache, otherwise scope zero is disabled
-- test the DoH special case (query received over TCP, forwarded over UDP)
addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/" }, {library='nghttp2'})
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_dohServerPort", "_serverCert", "_serverKey"]
def testScopeZero(self):
"""
Cache: Test the scope-zero feature, backend returns a scope of zero
"""
ttl = 600
- name = 'scope-zero.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "scope-zero.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
query.flags &= ~dns.flags.RD
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
- expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.0", 24)
+ expectedQuery = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso], payload=512)
expectedQuery.flags &= ~dns.flags.RD
- ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 0)
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24, 0)
expectedResponse = dns.message.make_response(query)
scopedResponse = dns.message.make_response(query)
scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
scopedResponse.answer.append(rrset)
expectedResponse.answer.append(rrset)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.checkMessageNoEDNS(receivedResponse, expectedResponse)
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ query = dns.message.make_query(name, "AAAA", "IN")
query.flags &= dns.flags.RD
# next query FROM A DIFFERENT CLIENT since RD is now set should STILL hit the cache
for method in ("sendUDPQuery", "sendTCPQuery"):
receivedResponse.id = expectedResponse.id
self.checkMessageNoEDNS(receivedResponse, expectedResponse)
- name = 'scope-zero-with-ecs.cache.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "scope-zero-with-ecs.cache.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso], payload=512)
query.flags &= ~dns.flags.RD
- expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
+ expectedQuery = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso], payload=512)
expectedQuery.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
Cache: Test the scope-zero feature, backend returns a scope of non-zero
"""
ttl = 600
- name = 'scope-not-zero.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "scope-not-zero.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
query.flags &= ~dns.flags.RD
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
- expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.0", 24)
+ expectedQuery = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso], payload=512)
expectedQuery.flags &= ~dns.flags.RD
- ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
+ ecso2 = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ expectedQuery2 = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso2], payload=512)
expectedQuery2.flags &= ~dns.flags.RD
- ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 24)
- ecsoResponse2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32, 24)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24, 24)
+ ecsoResponse2 = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32, 24)
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
scopedResponse = dns.message.make_response(query)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.checkMessageNoEDNS(receivedResponse, expectedResponse)
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ query = dns.message.make_query(name, "AAAA", "IN")
query.flags &= dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
Cache: Test the scope-zero feature, backend returns no ECS at all
"""
ttl = 600
- name = 'scope-zero-no-ecs.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "scope-zero-no-ecs.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
query.flags &= ~dns.flags.RD
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
- expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.0", 24)
+ expectedQuery = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso], payload=512)
expectedQuery.flags &= ~dns.flags.RD
- ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
+ ecso2 = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ expectedQuery2 = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso2], payload=512)
expectedQuery2.flags &= ~dns.flags.RD
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response = dns.message.make_response(query)
response.answer.append(rrset)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.checkMessageNoEDNS(receivedResponse, response)
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ query = dns.message.make_query(name, "AAAA", "IN")
query.flags &= dns.flags.RD
response = dns.message.make_response(query)
response.answer.append(rrset)
Cache: Test the scope-zero feature with a query received over DoH, backend returns a scope of zero
"""
ttl = 600
- name = 'scope-zero-incoming-doh.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "scope-zero-incoming-doh.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
query.flags &= ~dns.flags.RD
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
- expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=4096)
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.0", 24)
+ expectedQuery = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso], payload=4096)
expectedQuery.flags &= ~dns.flags.RD
- ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 0)
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24, 0)
expectedResponse = dns.message.make_response(query)
scopedResponse = dns.message.make_response(query)
scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
scopedResponse.answer.append(rrset)
expectedResponse.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendDOHQueryWrapper(query, response=None, useQueue=False)
self.checkMessageNoEDNS(receivedResponse, expectedResponse)
-class TestCachingScopeZeroButNoSubnetcheck(DNSDistTest):
+class TestCachingScopeZeroButNoSubnetcheck(DNSDistTest):
_config_template = """
-- We disable ECS parsing in the packet cache, meaning scope zero is disabled
pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=3600, parseECS=false})
Cache: Test that the scope-zero feature is disabled when ECS parsing is not enabled in the cache
"""
ttl = 600
- name = 'scope-zero-no-subnet.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "scope-zero-no-subnet.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
query.flags &= ~dns.flags.RD
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
- expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.0", 24)
+ expectedQuery = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso], payload=512)
expectedQuery.flags &= ~dns.flags.RD
- ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
+ ecso2 = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ expectedQuery2 = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso2], payload=512)
expectedQuery2.flags &= ~dns.flags.RD
- ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 0)
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24, 0)
expectedResponse = dns.message.make_response(query)
scopedResponse = dns.message.make_response(query)
scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
scopedResponse.answer.append(rrset)
expectedResponse.answer.append(rrset)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.checkMessageNoEDNS(receivedResponse, expectedResponse)
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ query = dns.message.make_query(name, "AAAA", "IN")
query.flags &= dns.flags.RD
response = dns.message.make_response(query)
response.answer.append(rrset)
self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery)
self.checkMessageNoEDNS(receivedResponse, response)
-class TestCachingAlteredHeader(DNSDistTest):
+class TestCachingAlteredHeader(DNSDistTest):
_config_template = """
pc = newPacketCache(100)
getPool(""):setCache(pc)
"""
Cache: The header has been altered via a rule
"""
- name = 'cache-set-rd.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "cache-set-rd.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# the query reaching the backend will never have the RD flag set
- expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ expectedQuery = dns.message.make_query(name, "A", "IN")
expectedQuery.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
response.flags &= ~dns.flags.RD
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
# first query has RD=1
query.flags |= dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
# same query with RD=0, should hit the cache as well
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertTrue(receivedResponse)
self.assertEqual(receivedResponse, expectedResponse)
-class TestCachingBackendSettingRD(DNSDistTest):
+class TestCachingBackendSettingRD(DNSDistTest):
_config_template = """
pc = newPacketCache(100)
getPool(""):setCache(pc)
"""
Cache: The backend sets RD=1 in the response even if the query had RD=0
"""
- name = 'backend-sets-rd.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "backend-sets-rd.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
- expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ expectedQuery = dns.message.make_query(name, "A", "IN")
expectedQuery.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
response.flags |= dns.flags.RD
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.RD
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
# same query with RD=1, should NOT hit the cache
query.flags |= dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(receivedResponse)
self.assertEqual(receivedResponse, expectedResponse)
+
class TestAPICache(DNSDistTest):
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKey = 'apisecret'
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
- _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _webServerBasicAuthPassword = "secret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKey = "apisecret"
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
+ _config_params = [
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
newServer{address="127.0.0.1:%d"}
webserver("127.0.0.1:%d")
"""
Cache: Clear cache via API
"""
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/cache'
- name = 'cache-api.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/cache"
+ name = "cache-api.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
# first query to fill the cache
self.assertEqual(receivedResponse, response)
# GET should on the cache API should yield a 400
- r = requests.get(url + '?pool=pool-without-cache&name=cache-api.cache.tests.powerdns.com.&type=AAAA', headers=headers, timeout=self._webTimeout)
+ r = requests.get(
+ url + "?pool=pool-without-cache&name=cache-api.cache.tests.powerdns.com.&type=AAAA",
+ headers=headers,
+ timeout=self._webTimeout,
+ )
self.assertEqual(r.status_code, 400)
# different pool
- r = requests.delete(url + '?pool=pool-without-cache&name=cache-api.cache.tests.powerdns.com.&type=AAAA', headers=headers, timeout=self._webTimeout)
+ r = requests.delete(
+ url + "?pool=pool-without-cache&name=cache-api.cache.tests.powerdns.com.&type=AAAA",
+ headers=headers,
+ timeout=self._webTimeout,
+ )
self.assertEqual(r.status_code, 404)
# no 'pool'
- r = requests.delete(url + '?name=cache-api.cache.tests.powerdns.com.&type=AAAA', headers=headers, timeout=self._webTimeout)
+ r = requests.delete(
+ url + "?name=cache-api.cache.tests.powerdns.com.&type=AAAA", headers=headers, timeout=self._webTimeout
+ )
self.assertEqual(r.status_code, 400)
# no 'name'
- r = requests.delete(url + '?pool=pool-without-cache&type=AAAA', headers=headers, timeout=self._webTimeout)
+ r = requests.delete(url + "?pool=pool-without-cache&type=AAAA", headers=headers, timeout=self._webTimeout)
self.assertEqual(r.status_code, 400)
# invalid name (label is too long)
- r = requests.delete(url + '?pool=&name=' + 'a'*65, headers=headers, timeout=self._webTimeout)
+ r = requests.delete(url + "?pool=&name=" + "a" * 65, headers=headers, timeout=self._webTimeout)
self.assertEqual(r.status_code, 400)
# different name
- r = requests.delete(url + '?pool=&name=not-cache-api.cache.tests.powerdns.com.', headers=headers, timeout=self._webTimeout)
+ r = requests.delete(
+ url + "?pool=&name=not-cache-api.cache.tests.powerdns.com.", headers=headers, timeout=self._webTimeout
+ )
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
content = r.json()
- self.assertIn('count', content)
- self.assertEqual(int(content['count']), 0)
+ self.assertIn("count", content)
+ self.assertEqual(int(content["count"]), 0)
# different type
- r = requests.delete(url + '?pool=&name=cache-api.cache.tests.powerdns.com.&type=A', headers=headers, timeout=self._webTimeout)
+ r = requests.delete(
+ url + "?pool=&name=cache-api.cache.tests.powerdns.com.&type=A", headers=headers, timeout=self._webTimeout
+ )
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
content = r.json()
- self.assertIn('count', content)
- self.assertEqual(int(content['count']), 0)
+ self.assertIn("count", content)
+ self.assertEqual(int(content["count"]), 0)
# should still be a hit
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
# remove
- r = requests.delete(url + '?pool=&name=cache-api.cache.tests.powerdns.com.&type=AAAA', headers=headers, timeout=self._webTimeout)
+ r = requests.delete(
+ url + "?pool=&name=cache-api.cache.tests.powerdns.com.&type=AAAA", headers=headers, timeout=self._webTimeout
+ )
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
content = r.json()
- self.assertIn('count', content)
- self.assertEqual(int(content['count']), 1)
+ self.assertIn("count", content)
+ self.assertEqual(int(content["count"]), 1)
# should be a miss
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertEqual(receivedResponse, response)
# remove all types
- r = requests.delete(url + '?pool=&name=cache-api.cache.tests.powerdns.com.', headers=headers, timeout=self._webTimeout)
+ r = requests.delete(
+ url + "?pool=&name=cache-api.cache.tests.powerdns.com.", headers=headers, timeout=self._webTimeout
+ )
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
content = r.json()
- self.assertIn('count', content)
- self.assertEqual(int(content['count']), 1)
+ self.assertIn("count", content)
+ self.assertEqual(int(content["count"]), 1)
# should be a miss
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertEqual(receivedResponse, response)
# suffix removal
- r = requests.delete(url + '?pool=&name=cache.tests.powerdns.com.&suffix=true', headers=headers, timeout=self._webTimeout)
+ r = requests.delete(
+ url + "?pool=&name=cache.tests.powerdns.com.&suffix=true", headers=headers, timeout=self._webTimeout
+ )
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
content = r.json()
- self.assertIn('count', content)
- self.assertEqual(int(content['count']), 1)
+ self.assertIn("count", content)
+ self.assertEqual(int(content["count"]), 1)
# should be a miss
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
-class TestCachingOfVeryLargeAnswers(DNSDistTest):
+class TestCachingOfVeryLargeAnswers(DNSDistTest):
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1, maximumEntrySize=8192})
getPool(""):setCache(pc)
We should be able to get answers as large as 8192 bytes this time
"""
numberOfQueries = 10
- name = 'very-large-answer.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'TXT', 'IN')
+ name = "very-large-answer.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "TXT", "IN")
response = dns.message.make_response(query)
# we prepare a large answer
- content = ''
+ content = ""
for i in range(31):
if len(content) > 0:
- content = content + ' '
- content = content + 'A' * 255
+ content = content + " "
+ content = content + "A" * 255
# pad up to 8192
- content = content + ' ' + 'B' * 183
+ content = content + " " + "B" * 183
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- content)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, content)
response.answer.append(rrset)
self.assertEqual(len(response.to_wire()), 8192)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
-class TestCacheEmptyTC(DNSDistTest):
+class TestCacheEmptyTC(DNSDistTest):
_truncated_ttl = 42
_config_template = """
pc = newPacketCache(100, {maxTTL=86400, minTTL=1, truncatedTTL=%d})
getPool(""):setCache(pc)
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_truncated_ttl', '_testServerPort']
+ _config_params = ["_truncated_ttl", "_testServerPort"]
def testEmptyTruncated(self):
"""
Cache: Empty TC=1 should be cached
"""
- name = 'cache-empty-tc.cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "cache-empty-tc.cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
response.flags |= dns.flags.TC
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
-class TestCachingPayloadRanks(DNSDistTest):
+class TestCachingPayloadRanks(DNSDistTest):
_verboseMode = True
_testServerPort = pickAvailablePort()
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
- _webServerAPIKey = 'apisecret'
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
- _config_params = ['_webServerPort', '_webServerAPIKeyHashed', '_testServerPort']
+ _webServerAPIKey = "apisecret"
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
+ _config_params = ["_webServerPort", "_webServerAPIKeyHashed", "_testServerPort"]
_config_template = """
webserver("127.0.0.1:%d")
setWebserverConfig({apiKey="%s"})
"""
def getPoolMetric(self, poolID, metricName):
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertIn('pools', content)
- pools = content['pools']
+ self.assertIn("pools", content)
+ pools = content["pools"]
self.assertGreater(len(pools), poolID)
pool = pools[poolID]
return int(pool[metricName])
"""
# testing with and without EDNS0 payload size
- name1 = 'cached.cache.tests.powerdns.com.'
- query1 = dns.message.make_query(name1, 'AAAA', 'IN', payload=512)
- query1_1 = dns.message.make_query(name1, 'AAAA', 'IN', payload=600)
+ name1 = "cached.cache.tests.powerdns.com."
+ query1 = dns.message.make_query(name1, "AAAA", "IN", payload=512)
+ query1_1 = dns.message.make_query(name1, "AAAA", "IN", payload=600)
response1 = dns.message.make_response(query1)
- rrset1 = dns.rrset.from_text(name1,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset1 = dns.rrset.from_text(name1, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response1.answer.append(rrset1)
# first query to fill the cache
(receivedQuery, receivedResponse) = self.sendUDPQuery(query1, response1)
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
- self.assertEqual(self.getPoolMetric(0, 'cacheHits'), 0)
+ self.assertEqual(self.getPoolMetric(0, "cacheHits"), 0)
receivedQuery.id = query1.id
self.assertEqual(query1, receivedQuery)
self.assertEqual(receivedResponse, response1)
# same query shall hit cache
(_, receivedResponse) = self.sendUDPQuery(query1, response=None, useQueue=False)
self.assertTrue(receivedResponse)
- self.assertEqual(self.getPoolMetric(0, 'cacheHits'), 1)
+ self.assertEqual(self.getPoolMetric(0, "cacheHits"), 1)
self.assertEqual(receivedResponse, response1)
# query1_1 shall also hit cache since 600 round down to 512
(_, receivedResponse) = self.sendUDPQuery(query1_1, response=None, useQueue=False)
self.assertTrue(receivedResponse)
- self.assertEqual(self.getPoolMetric(0, 'cacheHits'), 2)
+ self.assertEqual(self.getPoolMetric(0, "cacheHits"), 2)
self.assertEqual(len(receivedResponse.answer), 1)
self.assertEqual(receivedResponse.answer[0], rrset1)
# testing for large sized cache entry
- name2 = 'bigcached.cache.tests.powerdns.com.'
- query2 = dns.message.make_query(name2, 'AAAA', 'IN', payload=1279)
- query2_1 = dns.message.make_query(name2, 'AAAA', 'IN', payload=1200)
- query2_2 = dns.message.make_query(name2, 'AAAA', 'IN', payload=1024)
+ name2 = "bigcached.cache.tests.powerdns.com."
+ query2 = dns.message.make_query(name2, "AAAA", "IN", payload=1279)
+ query2_1 = dns.message.make_query(name2, "AAAA", "IN", payload=1200)
+ query2_2 = dns.message.make_query(name2, "AAAA", "IN", payload=1024)
response2 = dns.message.make_response(query2)
v6addr_list = []
- for i in range(1,41):
- v6addr_list.append(f'fe80:fe80:fe80:fe80::{i}')
- rrset2 = dns.rrset.from_text_list(name2,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- v6addr_list)
- response2.answer.append(rrset2) # response > 40x(16+10)=1040 bytes
+ for i in range(1, 41):
+ v6addr_list.append(f"fe80:fe80:fe80:fe80::{i}")
+ rrset2 = dns.rrset.from_text_list(name2, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, v6addr_list)
+ response2.answer.append(rrset2) # response > 40x(16+10)=1040 bytes
# first query to fill the cache
(receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
self.assertTrue(receivedQuery)
- self.assertEqual(self.getPoolMetric(0, 'cacheHits'), 2)
+ self.assertEqual(self.getPoolMetric(0, "cacheHits"), 2)
self.assertTrue(receivedResponse)
receivedQuery.id = query2.id
self.assertEqual(query2, receivedQuery)
# same query shall hit cache
(_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
self.assertTrue(receivedResponse)
- self.assertEqual(self.getPoolMetric(0, 'cacheHits'), 3)
+ self.assertEqual(self.getPoolMetric(0, "cacheHits"), 3)
self.assertEqual(receivedResponse, response2)
# query2_1 shall hit cache
(_, receivedResponse) = self.sendUDPQuery(query2_1, response=None, useQueue=False)
self.assertTrue(receivedResponse)
- self.assertEqual(self.getPoolMetric(0, 'cacheHits'), 4)
+ self.assertEqual(self.getPoolMetric(0, "cacheHits"), 4)
self.assertEqual(len(receivedResponse.answer), 1)
self.assertEqual(receivedResponse.answer[0], rrset2)
# query2_2 shall hit cache but truncated since payload size is not enough
(_, receivedResponse) = self.sendUDPQuery(query2_2, response=None, useQueue=False)
self.assertTrue(receivedResponse)
- self.assertEqual(self.getPoolMetric(0, 'cacheHits'), 5)
+ self.assertEqual(self.getPoolMetric(0, "cacheHits"), 5)
self.assertEqual(len(receivedResponse.answer), 0)
self.assertEqual(receivedResponse.flags & dns.flags.TC, dns.flags.TC)
import time
from dnsdisttests import DNSDistTest, Queue, pickAvailablePort
-class TestCarbon(DNSDistTest):
+class TestCarbon(DNSDistTest):
_carbonServer1Port = pickAvailablePort()
_carbonServer1Name = "carbonname1"
_carbonServer2Port = pickAvailablePort()
_carbonQueue2 = Queue()
_carbonInterval = 2
_carbonCounters = {}
- _config_params = ['_carbonServer1Port', '_carbonServer1Name', '_carbonInterval',
- '_carbonServer2Port', '_carbonServer2Name', '_carbonInterval']
+ _config_params = [
+ "_carbonServer1Port",
+ "_carbonServer1Name",
+ "_carbonInterval",
+ "_carbonServer2Port",
+ "_carbonServer2Name",
+ "_carbonInterval",
+ ]
_config_template = """
s = newServer{address="127.0.0.1:5353"}
s:setDown()
while True:
(conn, _) = sock.accept()
conn.settimeout(2.0)
- lines = b''
+ lines = b""
while True:
data = conn.recv(4096)
if not data:
@classmethod
def startResponders(cls):
- cls._CarbonResponder1 = threading.Thread(name='Carbon Responder 1', target=cls.CarbonResponder, args=[cls._carbonServer1Port])
+ cls._CarbonResponder1 = threading.Thread(
+ name="Carbon Responder 1", target=cls.CarbonResponder, args=[cls._carbonServer1Port]
+ )
cls._CarbonResponder1.daemon = True
cls._CarbonResponder1.start()
- cls._CarbonResponder2 = threading.Thread(name='Carbon Responder 2', target=cls.CarbonResponder, args=[cls._carbonServer2Port])
+ cls._CarbonResponder2 = threading.Thread(
+ name="Carbon Responder 2", target=cls.CarbonResponder, args=[cls._carbonServer2Port]
+ )
cls._CarbonResponder2.daemon = True
cls._CarbonResponder2.start()
self.assertTrue(data1)
self.assertGreater(len(data1.splitlines()), 1)
- expectedStart = b"dnsdist.%s.main." % self._carbonServer1Name.encode('UTF-8')
+ expectedStart = b"dnsdist.%s.main." % self._carbonServer1Name.encode("UTF-8")
for line in data1.splitlines():
self.assertTrue(line.startswith(expectedStart))
- parts = line.split(b' ')
+ parts = line.split(b" ")
self.assertEqual(len(parts), 3)
self.assertTrue(self.isfloat(parts[1]))
self.assertTrue(parts[2].isdigit())
self.assertTrue(data2)
self.assertGreater(len(data2.splitlines()), 1)
- expectedStart = b"dnsdist.%s.main." % self._carbonServer2Name.encode('UTF-8')
+ expectedStart = b"dnsdist.%s.main." % self._carbonServer2Name.encode("UTF-8")
for line in data2.splitlines():
self.assertTrue(line.startswith(expectedStart))
- parts = line.split(b' ')
+ parts = line.split(b" ")
self.assertEqual(len(parts), 3)
self.assertTrue(self.isfloat(parts[1]))
self.assertTrue(parts[2].isdigit())
# configured in the class definition
self.assertTrue(data1)
self.assertGreater(len(data1.splitlines()), 1)
- expectedStart = b"dnsdist.%s.main.pools._default_.servers" % self._carbonServer1Name.encode('UTF-8')
+ expectedStart = b"dnsdist.%s.main.pools._default_.servers" % self._carbonServer1Name.encode("UTF-8")
for line in data1.splitlines():
if expectedStart in line:
- parts = line.split(b' ')
- if b'servers-up' in line:
+ parts = line.split(b" ")
+ if b"servers-up" in line:
self.assertEqual(len(parts), 3)
self.assertTrue(parts[1].isdigit())
self.assertEqual(int(parts[1]), 2)
# the first carbon server
self.assertTrue(data2)
self.assertGreater(len(data2.splitlines()), 1)
- expectedStart = b"dnsdist.%s.main.pools._default_.servers" % self._carbonServer2Name.encode('UTF-8')
+ expectedStart = b"dnsdist.%s.main.pools._default_.servers" % self._carbonServer2Name.encode("UTF-8")
for line in data2.splitlines():
if expectedStart in line:
- parts = line.split(b' ')
- if b'servers-up' in line:
+ parts = line.split(b" ")
+ if b"servers-up" in line:
self.assertEqual(len(parts), 3)
self.assertTrue(parts[1].isdigit())
self.assertEqual(int(parts[1]), 2)
import subprocess
import time
-class TestCheckConfig(unittest.TestCase):
+class TestCheckConfig(unittest.TestCase):
def tryDNSDist(self, configTemplate, shouldBeSuccessful=True, delay=1):
- conffile = 'dnsdist_test.conf'
- with open(conffile, 'w') as conf:
+ conffile = "dnsdist_test.conf"
+ with open(conffile, "w") as conf:
conf.write("-- Autogenerated by dnsdisttests.py\n")
conf.write(configTemplate)
- dnsdistcmd = [os.environ['DNSDISTBIN'], '-C', conffile, '--check-config']
+ dnsdistcmd = [os.environ["DNSDISTBIN"], "-C", conffile, "--check-config"]
- with open(os.devnull, 'w') as fdDevNull:
+ with open(os.devnull, "w") as fdDevNull:
dnsdist = subprocess.Popen(dnsdistcmd, close_fds=True, stdout=fdDevNull)
if dnsdist.poll() is None:
from dnsdisttests import DNSDistTest, pickAvailablePort
import extendederrors
+
class TestConfigurationUpdates(DNSDistTest):
_yaml_config_template = """---
logging:
_dohWithNGHTTP2ServerPort = pickAvailablePort()
_doqServerPort = pickAvailablePort()
_doh3ServerPort = pickAvailablePort()
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _dohWithNGHTTP2BaseURL = ("https://%s:%d/" % (_serverName, _dohWithNGHTTP2ServerPort))
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doh3ServerPort))
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _dohWithNGHTTP2BaseURL = "https://%s:%d/" % (_serverName, _dohWithNGHTTP2ServerPort)
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doh3ServerPort)
+ _caCert = "ca.pem"
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_consolePort = pickAvailablePort()
_testServerPort = pickAvailablePort()
- _yaml_config_params = ['_consolePort', '_consoleKeyB64', '_dnsDistPort', '_tlsServerPort', '_serverCert', '_serverKey', '_dohWithNGHTTP2ServerPort', '_serverCert', '_serverKey', '_doqServerPort','_serverCert', '_serverKey', '_doh3ServerPort', '_serverCert', '_serverKey', '_testServerPort', '_testServerPort']
+ _yaml_config_params = [
+ "_consolePort",
+ "_consoleKeyB64",
+ "_dnsDistPort",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_dohWithNGHTTP2ServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doh3ServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_testServerPort",
+ "_testServerPort",
+ ]
_config_params = []
- _checkConfigExpectedOutput = b"DNS over HTTPS configured\nConfiguration 'configs/dnsdist_TestConfigurationUpdates.yml' OK!\n"
+ _checkConfigExpectedOutput = (
+ b"DNS over HTTPS configured\nConfiguration 'configs/dnsdist_TestConfigurationUpdates.yml' OK!\n"
+ )
def testRegular(self):
"""
Configuration updates: regular
"""
- for protocol in ['UDP', 'TCP', 'DOT', 'DOH', 'DOQ', 'DOH3']:
- name = f'regular-{protocol}.config-updates.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ for protocol in ["UDP", "TCP", "DOT", "DOH", "DOQ", "DOH3"]:
+ name = f"regular-{protocol}.config-updates.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- method = f'send{protocol}Query'
- if not protocol in ['UDP', 'TCP']:
- if protocol == 'DOH':
- method = 'sendDOHWithNGHTTP2QueryWrapper'
+ method = f"send{protocol}Query"
+ if not protocol in ["UDP", "TCP"]:
+ if protocol == "DOH":
+ method = "sendDOHWithNGHTTP2QueryWrapper"
else:
- method += 'Wrapper'
+ method += "Wrapper"
sender = getattr(self, method)
(receivedQuery, receivedResponse) = sender(query, response=response)
"""
Configuration updates: response
"""
- for protocol in ['UDP', 'TCP']:
- name = f'{protocol}-response.config-updates.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ for protocol in ["UDP", "TCP"]:
+ name = f"{protocol}-response.config-updates.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- method = f'send{protocol}Query'
+ method = f"send{protocol}Query"
sender = getattr(self, method)
(receivedQuery, receivedResponse) = sender(query, response=response)
self.assertEqual(receivedResponse, response)
self.sendConsoleCommand(f'addResponseAction(QNameRule("{name}"), SetExtendedDNSErrorResponseAction(15))')
- if protocol == 'TCP':
+ if protocol == "TCP":
time.sleep(1)
# the configuration should have been updated
expectedResponse = dns.message.make_response(query)
- ede = extendederrors.ExtendedErrorOption(15, b'')
+ ede = extendederrors.ExtendedErrorOption(15, b"")
expectedResponse.use_edns(edns=True, payload=4096, options=[ede])
expectedResponse.answer.append(rrset)
(_, receivedResponse) = sender(query, response=response)
self.assertEqual(receivedResponse, expectedResponse)
+
class TestConfigurationUpdatesRecvMMSG(DNSDistTest):
_yaml_config_template = """---
console:
"""
_dnsDistPort = pickAvailablePort()
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_consolePort = pickAvailablePort()
_testServerPort = pickAvailablePort()
- _yaml_config_params = ['_consolePort', '_consoleKeyB64', '_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_consolePort", "_consoleKeyB64", "_dnsDistPort", "_testServerPort"]
_config_params = []
def testRecvMMSGUDP(self):
"""
Configuration updates: recvmmsg UDP
"""
- name = 'recvmmsg-udp.config-updates.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "recvmmsg-udp.config-updates.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
"""
Configuration updates: UDP response
"""
- name = 'recvmmsg-udp-response.config-updates.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "recvmmsg-udp-response.config-updates.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# the configuration should have been updated
expectedResponse = dns.message.make_response(query)
- ede = extendederrors.ExtendedErrorOption(15, b'')
+ ede = extendederrors.ExtendedErrorOption(15, b"")
expectedResponse.use_edns(edns=True, payload=4096, options=[ede])
expectedResponse.answer.append(rrset)
(_, receivedResponse) = self.sendUDPQuery(query, response=response)
import time
from dnsdisttests import DNSDistTest
-class TestConsoleAllowed(DNSDistTest):
+class TestConsoleAllowed(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
"""
Console: Allowed
"""
- version = self.sendConsoleCommand('showVersion()')
- self.assertTrue(version.startswith('dnsdist '))
+ version = self.sendConsoleCommand("showVersion()")
+ self.assertTrue(version.startswith("dnsdist "))
-class TestConsoleAllowedV6(DNSDistTest):
+class TestConsoleAllowedV6(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
setKey("%s")
controlSocket("[::1]:%d")
"""
Console: Allowed IPv6
"""
- if 'SKIP_IPV6_TESTS' in os.environ:
- raise unittest.SkipTest('IPv6 tests are disabled')
- version = self.sendConsoleCommand('showVersion()', IPv6=True)
- self.assertTrue(version.startswith('dnsdist '))
+ if "SKIP_IPV6_TESTS" in os.environ:
+ raise unittest.SkipTest("IPv6 tests are disabled")
+ version = self.sendConsoleCommand("showVersion()", IPv6=True)
+ self.assertTrue(version.startswith("dnsdist "))
-class TestConsoleNotAllowed(DNSDistTest):
+class TestConsoleNotAllowed(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
"""
Console: Not allowed by the ACL
"""
- self.assertRaises(socket.error, self.sendConsoleCommand, 'showVersion()')
+ self.assertRaises(socket.error, self.sendConsoleCommand, "showVersion()")
-class TestConsoleNoKey(DNSDistTest):
+class TestConsoleNoKey(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _config_params = ['_consolePort', '_testServerPort']
+ _config_params = ["_consolePort", "_testServerPort"]
_config_template = """
controlSocket("127.0.0.1:%d")
newServer{address="127.0.0.1:%d"}
"""
Console: No key, the connection should not be allowed
"""
- self.assertRaises(socket.error, self.sendConsoleCommand, 'showVersion()')
+ self.assertRaises(socket.error, self.sendConsoleCommand, "showVersion()")
-class TestConsoleConcurrentConnections(DNSDistTest):
+class TestConsoleConcurrentConnections(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_maxConns = 2
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_maxConns']
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort", "_maxConns"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
conns.append(conn)
# we now hold all the slots, let's try to establish a new connection
- self.assertRaises(socket.error, self.sendConsoleCommand, 'showVersion()')
+ self.assertRaises(socket.error, self.sendConsoleCommand, "showVersion()")
# free one slot
conns[0].close()
time.sleep(1)
# this should work
- version = self.sendConsoleCommand('showVersion()')
- self.assertTrue(version.startswith('dnsdist '))
+ version = self.sendConsoleCommand("showVersion()")
+ self.assertTrue(version.startswith("dnsdist "))
+
def writeCDB(fname, variant=1):
- cdb = cdbx.CDB.make(fname+'.tmp')
- cdb.add(socket.inet_aton(f'127.0.0.{variant}'), b'this is the value of the source address tag')
- cdb.add(b'\x05qname\x03cdb\x05tests\x08powerdns\x03com\x00', b'this is the value of the qname tag')
- cdb.add(b'\x06suffix\x03cdb\x05tests\x08powerdns\x03com\x00', b'this is the value of the suffix tag')
- cdb.add(b'this is the value of the qname tag', b'this is the value of the second tag')
+ cdb = cdbx.CDB.make(fname + ".tmp")
+ cdb.add(socket.inet_aton(f"127.0.0.{variant}"), b"this is the value of the source address tag")
+ cdb.add(b"\x05qname\x03cdb\x05tests\x08powerdns\x03com\x00", b"this is the value of the qname tag")
+ cdb.add(b"\x06suffix\x03cdb\x05tests\x08powerdns\x03com\x00", b"this is the value of the suffix tag")
+ cdb.add(b"this is the value of the qname tag", b"this is the value of the second tag")
cdb.commit().close()
- os.rename(fname+'.tmp', fname)
+ os.rename(fname + ".tmp", fname)
cdb.close()
-class TestConsoleAccessObjectsFromYAML(DNSDistTest):
+class TestConsoleAccessObjectsFromYAML(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _cdbFileName = '/tmp/test-cdb-db'
+ _cdbFileName = "/tmp/test-cdb-db"
_yaml_config_template = """
console:
file_name: "%s"
refresh_delay: 1
"""
- _yaml_config_params = ['_consoleKeyB64', '_consolePort', '_cdbFileName']
+ _yaml_config_params = ["_consoleKeyB64", "_consolePort", "_cdbFileName"]
_config_params = []
@classmethod
Console: Check that we can access Yaml-defined objects
"""
cdb = self.sendConsoleCommand("getObjectFromYAMLConfiguration('cdb-kvs')")
- self.assertTrue(cdb.startswith('Command returned an object we can\'t print: Trying to cast a lua variable from "userdata" to'))
- got = self.sendConsoleCommand("if getObjectFromYAMLConfiguration('cdb-kvs'):reload() then return 'reloading worked' else return 'reloading failed' end")
- self.assertEqual(got, 'reloading worked\n')
+ self.assertTrue(
+ cdb.startswith(
+ 'Command returned an object we can\'t print: Trying to cast a lua variable from "userdata" to'
+ )
+ )
+ got = self.sendConsoleCommand(
+ "if getObjectFromYAMLConfiguration('cdb-kvs'):reload() then return 'reloading worked' else return 'reloading failed' end"
+ )
+ self.assertEqual(got, "reloading worked\n")
-class TestConsoleRings(DNSDistTest):
+class TestConsoleRings(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
Console: Ring entries
"""
# check that the ring is empty first
- numberOfEntries = self.sendConsoleCommand('#getRingEntries()')
+ numberOfEntries = self.sendConsoleCommand("#getRingEntries()")
self.assertEqual(int(numberOfEntries), 0)
- name = 'a.console-ring.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "a.console-ring.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(response, receivedResponse)
# we should now have 4 entries in the ring
- numberOfEntries = self.sendConsoleCommand('#getRingEntries()')
+ numberOfEntries = self.sendConsoleCommand("#getRingEntries()")
self.assertEqual(int(numberOfEntries), 4)
# check the first query
index = 1
- qname = self.sendConsoleCommand(f'getRingEntries()[{index}].qname:toString()').rstrip()
+ qname = self.sendConsoleCommand(f"getRingEntries()[{index}].qname:toString()").rstrip()
self.assertEqual(qname, name)
- qtype = self.sendConsoleCommand(f'getRingEntries()[{index}].qtype').rstrip()
+ qtype = self.sendConsoleCommand(f"getRingEntries()[{index}].qtype").rstrip()
self.assertEqual(int(qtype), 1)
- protocol = self.sendConsoleCommand(f'getRingEntries()[{index}].protocol').rstrip()
+ protocol = self.sendConsoleCommand(f"getRingEntries()[{index}].protocol").rstrip()
self.assertEqual(protocol, "DoUDP")
- requestor = self.sendConsoleCommand(f'getRingEntries()[{index}].requestor:toString()').rstrip()
+ requestor = self.sendConsoleCommand(f"getRingEntries()[{index}].requestor:toString()").rstrip()
self.assertEqual(requestor, "127.0.0.1")
- isResponse = self.sendConsoleCommand(f'tostring(getRingEntries()[{index}].isResponse)').rstrip()
+ isResponse = self.sendConsoleCommand(f"tostring(getRingEntries()[{index}].isResponse)").rstrip()
self.assertEqual(isResponse, "false")
# check the first response
index = 2
- qname = self.sendConsoleCommand(f'getRingEntries()[{index}].qname:toString()').rstrip()
+ qname = self.sendConsoleCommand(f"getRingEntries()[{index}].qname:toString()").rstrip()
self.assertEqual(qname, name)
- qtype = self.sendConsoleCommand(f'getRingEntries()[{index}].qtype').rstrip()
+ qtype = self.sendConsoleCommand(f"getRingEntries()[{index}].qtype").rstrip()
self.assertEqual(int(qtype), 1)
- protocol = self.sendConsoleCommand(f'getRingEntries()[{index}].protocol').rstrip()
+ protocol = self.sendConsoleCommand(f"getRingEntries()[{index}].protocol").rstrip()
self.assertEqual(protocol, "DoUDP")
- requestor = self.sendConsoleCommand(f'getRingEntries()[{index}].requestor:toString()').rstrip()
+ requestor = self.sendConsoleCommand(f"getRingEntries()[{index}].requestor:toString()").rstrip()
self.assertEqual(requestor, "127.0.0.1")
- backend = self.sendConsoleCommand(f'getRingEntries()[{index}].backend:toStringWithPort()').rstrip()
+ backend = self.sendConsoleCommand(f"getRingEntries()[{index}].backend:toStringWithPort()").rstrip()
self.assertEqual(backend, f"127.0.0.1:{self._testServerPort}")
- isResponse = self.sendConsoleCommand(f'tostring(getRingEntries()[{index}].isResponse)').rstrip()
+ isResponse = self.sendConsoleCommand(f"tostring(getRingEntries()[{index}].isResponse)").rstrip()
self.assertEqual(isResponse, "true")
-class TestConsoleViaBuiltInClient(DNSDistTest):
+class TestConsoleViaBuiltInClient(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
"""
output = None
try:
- confFile = os.path.join('configs', 'dnsdist_%s.conf' % (self.__class__.__name__))
- testcmd = [os.environ['DNSDISTBIN'], '--client', '-C', confFile ]
- process = subprocess.Popen(testcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
- output = process.communicate(input=b'showVersion()\n')
+ confFile = os.path.join("configs", "dnsdist_%s.conf" % (self.__class__.__name__))
+ testcmd = [os.environ["DNSDISTBIN"], "--client", "-C", confFile]
+ process = subprocess.Popen(
+ testcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True
+ )
+ output = process.communicate(input=b"showVersion()\n")
except subprocess.CalledProcessError as exc:
- raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, process.output))
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, process.output))
if process.returncode != 0:
- raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, output))
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, output))
- self.assertTrue(output[0].startswith(b'dnsdist '))
+ self.assertTrue(output[0].startswith(b"dnsdist "))
from dnsdisttests import DNSDistTest, pickAvailablePort
import dnscrypt
+
class DNSCryptTest(DNSDistTest):
"""
dnsdist is configured to accept DNSCrypt queries on 127.0.0.1:_dnsDistPortDNSCrypt.
_dnsDistPortDNSCrypt = pickAvailablePort()
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _providerFingerprint = 'E1D7:2108:9A59:BF8D:F101:16FA:ED5E:EA6A:9F6C:C78F:7F91:AF6B:027E:62F4:69C3:B1AA'
+ _providerFingerprint = "E1D7:2108:9A59:BF8D:F101:16FA:ED5E:EA6A:9F6C:C78F:7F91:AF6B:027E:62F4:69C3:B1AA"
_providerName = "2.provider.name"
_resolverCertificateSerial = 42
addAction("tcp.protocols.dnscrypt.tests.powerdns.com.", LuaAction(checkDNSCryptTCP))
"""
- _config_params = ['_consoleKeyB64', '_consolePort', '_resolverCertificateSerial', '_resolverCertificateValidFrom', '_resolverCertificateValidUntil', '_dnsDistPortDNSCrypt', '_providerName', '_testServerPort']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_resolverCertificateSerial",
+ "_resolverCertificateValidFrom",
+ "_resolverCertificateValidUntil",
+ "_dnsDistPortDNSCrypt",
+ "_providerName",
+ "_testServerPort",
+ ]
def testSimpleA(self):
"""
DNSCrypt: encrypted A query
"""
- client = dnscrypt.DNSCryptClient(self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt)
- name = 'a.dnscrypt.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ client = dnscrypt.DNSCryptClient(
+ self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt
+ )
+ name = "a.dnscrypt.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.2.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.2.0.1")
response.answer.append(rrset)
self.doDNSCryptQuery(client, query, response, False)
the padding into account) and check that the response
is truncated.
"""
- client = dnscrypt.DNSCryptClient(self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt)
- name = 'smallquerylargeresponse.dnscrypt.tests.powerdns.com.'
- query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096)
+ client = dnscrypt.DNSCryptClient(
+ self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt
+ )
+ name = "smallquerylargeresponse.dnscrypt.tests.powerdns.com."
+ query = dns.message.make_query(name, "TXT", "IN", use_edns=True, payload=4096)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- 'A'*255)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, "A" * 255)
response.answer.append(rrset)
self._toResponderQueue.put(response)
"""
DNSCrypt: certificate rotation
"""
- client = dnscrypt.DNSCryptClient(self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt)
+ client = dnscrypt.DNSCryptClient(
+ self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt
+ )
client.refreshResolverCertificates()
cert = client.getResolverCertificate()
self.assertTrue(cert)
self.assertEqual(cert.serial, self._resolverCertificateSerial)
- name = 'rotation.dnscrypt.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "rotation.dnscrypt.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.2.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.2.0.1")
response.answer.append(rrset)
self.doDNSCryptQuery(client, query, response, False)
self.doDNSCryptQuery(client, query, response, True)
# generate a new certificate
- self.sendConsoleCommand("generateDNSCryptCertificate('DNSCryptProviderPrivate.key', 'DNSCryptResolver.cert.2', 'DNSCryptResolver.key.2', {!s}, {:.0f}, {:.0f})".format(self._resolverCertificateSerial + 1, self._resolverCertificateValidFrom, self._resolverCertificateValidUntil))
+ self.sendConsoleCommand(
+ "generateDNSCryptCertificate('DNSCryptProviderPrivate.key', 'DNSCryptResolver.cert.2', 'DNSCryptResolver.key.2', {!s}, {:.0f}, {:.0f})".format(
+ self._resolverCertificateSerial + 1,
+ self._resolverCertificateValidFrom,
+ self._resolverCertificateValidUntil,
+ )
+ )
# add that new certificate
- self.sendConsoleCommand("getDNSCryptBind(0):loadNewCertificate('DNSCryptResolver.cert.2', 'DNSCryptResolver.key.2')")
+ self.sendConsoleCommand(
+ "getDNSCryptBind(0):loadNewCertificate('DNSCryptResolver.cert.2', 'DNSCryptResolver.key.2')"
+ )
oldSerial = self.sendConsoleCommand("getDNSCryptBind(0):getCertificate(0):getSerial()")
self.assertEqual(int(oldSerial), self._resolverCertificateSerial)
self.assertEqual(certs[1].serial, self._resolverCertificateSerial + 1)
# generate a third certificate, this time in memory
- self.sendConsoleCommand("getDNSCryptBind(0):generateAndLoadInMemoryCertificate('DNSCryptProviderPrivate.key', {!s}, {:.0f}, {:.0f})".format(self._resolverCertificateSerial + 2, self._resolverCertificateValidFrom, self._resolverCertificateValidUntil))
+ self.sendConsoleCommand(
+ "getDNSCryptBind(0):generateAndLoadInMemoryCertificate('DNSCryptProviderPrivate.key', {!s}, {:.0f}, {:.0f})".format(
+ self._resolverCertificateSerial + 2,
+ self._resolverCertificateValidFrom,
+ self._resolverCertificateValidUntil,
+ )
+ )
# we should still be able to send queries with the previous certificate
self.doDNSCryptQuery(client, query, response, False)
self.assertEqual(certs[2].serial, self._resolverCertificateSerial + 2)
# generate a fourth certificate, still in memory
- self.sendConsoleCommand("getDNSCryptBind(0):generateAndLoadInMemoryCertificate('DNSCryptProviderPrivate.key', {!s}, {:.0f}, {:.0f})".format(self._resolverCertificateSerial + 3, self._resolverCertificateValidFrom, self._resolverCertificateValidUntil))
+ self.sendConsoleCommand(
+ "getDNSCryptBind(0):generateAndLoadInMemoryCertificate('DNSCryptProviderPrivate.key', {!s}, {:.0f}, {:.0f})".format(
+ self._resolverCertificateSerial + 3,
+ self._resolverCertificateValidFrom,
+ self._resolverCertificateValidUntil,
+ )
+ )
# mark the old ones as inactive
self.sendConsoleCommand("getDNSCryptBind(0):markInactive({!s})".format(self._resolverCertificateSerial))
self.assertTrue(cert)
self.assertEqual(cert.serial, self._resolverCertificateSerial + 2)
# now remove them
- self.sendConsoleCommand("getDNSCryptBind(0):removeInactiveCertificate({!s})".format(self._resolverCertificateSerial))
- self.sendConsoleCommand("getDNSCryptBind(0):removeInactiveCertificate({!s})".format(self._resolverCertificateSerial + 1))
- self.sendConsoleCommand("getDNSCryptBind(0):removeInactiveCertificate({!s})".format(self._resolverCertificateSerial + 2))
+ self.sendConsoleCommand(
+ "getDNSCryptBind(0):removeInactiveCertificate({!s})".format(self._resolverCertificateSerial)
+ )
+ self.sendConsoleCommand(
+ "getDNSCryptBind(0):removeInactiveCertificate({!s})".format(self._resolverCertificateSerial + 1)
+ )
+ self.sendConsoleCommand(
+ "getDNSCryptBind(0):removeInactiveCertificate({!s})".format(self._resolverCertificateSerial + 2)
+ )
# we should not be able to send with the old ones anymore
try:
"""
DNSCrypt: Test DNSQuestion.Protocol over UDP
"""
- client = dnscrypt.DNSCryptClient(self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt)
- name = 'udp.protocols.dnscrypt.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ client = dnscrypt.DNSCryptClient(
+ self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt
+ )
+ name = "udp.protocols.dnscrypt.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
self.doDNSCryptQuery(client, query, response, False)
"""
DNSCrypt: Test DNSQuestion.Protocol over TCP
"""
- client = dnscrypt.DNSCryptClient(self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt)
- name = 'tcp.protocols.dnscrypt.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ client = dnscrypt.DNSCryptClient(
+ self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt
+ )
+ name = "tcp.protocols.dnscrypt.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
self.doDNSCryptQuery(client, query, response, True)
+
class TestDNSCryptYaml(TestDNSCrypt):
_config_template = """
function checkDNSCryptUDP(dq)
function_name: "checkDNSCryptTCP"
"""
_config_params = []
- _yaml_config_params = ['_consoleKeyB64', '_consolePort', '_dnsDistPortDNSCrypt', '_providerName', '_testServerPort']
+ _yaml_config_params = ["_consoleKeyB64", "_consolePort", "_dnsDistPortDNSCrypt", "_providerName", "_testServerPort"]
-class TestDNSCryptWithCache(DNSCryptTest):
- _config_params = ['_resolverCertificateSerial', '_resolverCertificateValidFrom', '_resolverCertificateValidUntil', '_dnsDistPortDNSCrypt', '_providerName', '_testServerPort']
+class TestDNSCryptWithCache(DNSCryptTest):
+ _config_params = [
+ "_resolverCertificateSerial",
+ "_resolverCertificateValidFrom",
+ "_resolverCertificateValidUntil",
+ "_dnsDistPortDNSCrypt",
+ "_providerName",
+ "_testServerPort",
+ ]
_config_template = """
generateDNSCryptCertificate("DNSCryptProviderPrivate.key", "DNSCryptResolver.cert", "DNSCryptResolver.key", %d, %d, %d)
addDNSCryptBind("127.0.0.1:%d", "%s", "DNSCryptResolver.cert", "DNSCryptResolver.key")
DNSCrypt: encrypted A query served from cache
"""
misses = 0
- client = dnscrypt.DNSCryptClient(self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt)
- name = 'cacheda.dnscrypt.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ client = dnscrypt.DNSCryptClient(
+ self._providerName, self._providerFingerprint, "127.0.0.1", self._dnsDistPortDNSCrypt
+ )
+ name = "cacheda.dnscrypt.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.2.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.2.0.1")
response.answer.append(rrset)
# first query to fill the cache
total += self._responsesCounter[key]
self.assertEqual(total, misses)
+
class TestDNSCryptAutomaticRotation(DNSCryptTest):
_config_template = """
setKey("%s")
"""
_dnsDistPortDNSCrypt2 = pickAvailablePort()
- _config_params = ['_consoleKeyB64', '_consolePort', '_resolverCertificateSerial', '_resolverCertificateValidFrom', '_resolverCertificateValidUntil', '_dnsDistPortDNSCrypt', '_providerName', '_dnsDistPortDNSCrypt2', '_providerName', '_testServerPort', '_resolverCertificateSerial']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_resolverCertificateSerial",
+ "_resolverCertificateValidFrom",
+ "_resolverCertificateValidUntil",
+ "_dnsDistPortDNSCrypt",
+ "_providerName",
+ "_dnsDistPortDNSCrypt2",
+ "_providerName",
+ "_testServerPort",
+ "_resolverCertificateSerial",
+ ]
def testCertRotation(self):
"""
self.assertTrue(cert)
self.assertGreater(cert.serial, serials[client])
- name = 'automatic-rotation.dnscrypt.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "automatic-rotation.dnscrypt.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.2.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.2.0.1")
response.answer.append(rrset)
for client in clients:
import dns
from dnsdisttests import DNSDistTest
-class TestDNSParser(DNSDistTest):
+class TestDNSParser(DNSDistTest):
_verboseMode = True
_config_template = """
function checkQueryPacket(dq)
"""
DNS Parser: basic checks
"""
- name = 'powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
class TestDNSRecordParser(DNSDistTest):
-
_verboseMode = True
_config_template = """
function checkResponsePacket(dq)
"""
DNS Parser: parsers checks
"""
- name = 'powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- 'ff:db8::ffff')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "ff:db8::ffff")
response.answer.append(rrset)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'not-powerdns.com.')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, "not-powerdns.com.")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
import pycurl
from io import BytesIO
+
class DOHTests(object):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _customResponseHeader1 = 'access-control-allow-origin: *'
- _customResponseHeader2 = 'user-agent: derp'
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _customResponseHeader1 = "access-control-allow-origin: *"
+ _customResponseHeader2 = "user-agent: derp"
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
dohFE = getDOHFrontend(0)
dohFE:setResponsesMap({newDOHResponseMapEntry('^/coffee$', 418, 'C0FFEE', {['FoO']='bar'})})
"""
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_serverName', '_dohServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_serverName",
+ "_dohServerPort",
+ "_dohServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_dohLibrary",
+ ]
_verboseMode = True
def testDOHSimple(self):
"""
DOH: Simple query
"""
- name = 'simple.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
self.assertEqual(expectedQuery, receivedQuery)
self.assertIn(self._customResponseHeader1, self._response_headers.decode())
self.assertIn(self._customResponseHeader2, self._response_headers.decode())
- self.assertNotIn('UPPERCASE: VaLuE', self._response_headers.decode())
- self.assertIn('uppercase: VaLuE', self._response_headers.decode())
- self.assertIn('cache-control: max-age=3600', self._response_headers.decode())
+ self.assertNotIn("UPPERCASE: VaLuE", self._response_headers.decode())
+ self.assertIn("uppercase: VaLuE", self._response_headers.decode())
+ self.assertIn("cache-control: max-age=3600", self._response_headers.decode())
self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
self.assertEqual(response, receivedResponse)
- self.checkHasHeader('cache-control', 'max-age=3600')
+ self.checkHasHeader("cache-control", "max-age=3600")
def testDOHTransactionID(self):
"""
DOH: Simple query with ID != 0
"""
- name = 'simple-with-non-zero-id.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple-with-non-zero-id.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 42
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
DOH: Simple POST query
"""
- name = 'simple-post.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple-post.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHPostQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHPostQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
DOH: Existing EDNS
"""
- name = 'existing-edns.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=8192)
+ name = "existing-edns.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=8192)
query.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = query.id
"""
DOH: Existing EDNS Client Subnet
"""
- name = 'existing-ecs.doh.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.4')
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=512, options=[ecso], want_dnssec=True)
+ name = "existing-ecs.doh.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("1.2.3.4")
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=512, options=[ecso], want_dnssec=True)
query.id = 0
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[rewrittenEcso])
response.want_dnssec(True)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = query.id
"""
DOH: Dropped query
"""
- name = 'drop.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, caFile=self._caCert, query=query, response=None, useQueue=False)
+ name = "drop.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ caFile=self._caCert,
+ query=query,
+ response=None,
+ useQueue=False,
+ )
self.assertEqual(receivedResponse, None)
def testRefused(self):
"""
DOH: Refused
"""
- name = 'refused.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "refused.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, caFile=self._caCert, query=query, response=None, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ caFile=self._caCert,
+ query=query,
+ response=None,
+ useQueue=False,
+ )
self.assertEqual(receivedResponse, expectedResponse)
def testSpoof(self):
"""
DOH: Spoofed
"""
- name = 'spoof.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "spoof.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.4')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
expectedResponse.answer.append(rrset)
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, caFile=self._caCert, query=query, response=None, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ caFile=self._caCert,
+ query=query,
+ response=None,
+ useQueue=False,
+ )
self.assertEqual(receivedResponse, expectedResponse)
def testDOHWithoutQuery(self):
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NOTIMP)
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, caFile=self._caCert, query=query, response=None, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ caFile=self._caCert,
+ query=query,
+ response=None,
+ useQueue=False,
+ )
self.assertEqual(receivedResponse, expectedResponse)
def testDOHShortPath(self):
"""
DOH: Short path in GET query
"""
- url = self._dohBaseURL + '/AA'
+ url = self._dohBaseURL + "/AA"
conn = self.openDOHConnection(self._dohServerPort, self._caCert, timeout=2.0)
conn.setopt(pycurl.URL, url)
conn.setopt(pycurl.RESOLVE, ["%s:%d:127.0.0.1" % (self._serverName, self._dohServerPort)])
"""
DOH: No parameter GET query
"""
- name = 'no-parameter-get.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "no-parameter-get.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
wire = query.to_wire()
- b64 = base64.urlsafe_b64encode(wire).decode('UTF8').rstrip('=')
- url = self._dohBaseURL + '?not-dns=' + b64
+ b64 = base64.urlsafe_b64encode(wire).decode("UTF8").rstrip("=")
+ url = self._dohBaseURL + "?not-dns=" + b64
conn = self.openDOHConnection(self._dohServerPort, self._caCert, timeout=2.0)
conn.setopt(pycurl.URL, url)
conn.setopt(pycurl.RESOLVE, ["%s:%d:127.0.0.1" % (self._serverName, self._dohServerPort)])
"""
DOH: Invalid Base64 GET query
"""
- name = 'invalid-b64-get.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
- url = self._dohBaseURL + '?dns=' + '_-~~~~-_'
+ name = "invalid-b64-get.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
+ url = self._dohBaseURL + "?dns=" + "_-~~~~-_"
conn = self.openDOHConnection(self._dohServerPort, self._caCert, timeout=2.0)
conn.setopt(pycurl.URL, url)
conn.setopt(pycurl.RESOLVE, ["%s:%d:127.0.0.1" % (self._serverName, self._dohServerPort)])
"""
DOH: Invalid DNS headers
"""
- name = 'invalid-dns-headers.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "invalid-dns-headers.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.flags |= dns.flags.QR
wire = query.to_wire()
- b64 = base64.urlsafe_b64encode(wire).decode('UTF8').rstrip('=')
- url = self._dohBaseURL + '?dns=' + b64
+ b64 = base64.urlsafe_b64encode(wire).decode("UTF8").rstrip("=")
+ url = self._dohBaseURL + "?dns=" + b64
conn = self.openDOHConnection(self._dohServerPort, self._caCert, timeout=2.0)
conn.setopt(pycurl.URL, url)
conn.setopt(pycurl.RESOLVE, ["%s:%d:127.0.0.1" % (self._serverName, self._dohServerPort)])
"""
DOH: Invalid method
"""
- name = 'invalid-method.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "invalid-method.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
wire = query.to_wire()
- b64 = base64.urlsafe_b64encode(wire).decode('UTF8').rstrip('=')
- url = self._dohBaseURL + '?dns=' + b64
+ b64 = base64.urlsafe_b64encode(wire).decode("UTF8").rstrip("=")
+ url = self._dohBaseURL + "?dns=" + b64
conn = self.openDOHConnection(self._dohServerPort, self._caCert, timeout=2)
conn.setopt(pycurl.URL, url)
conn.setopt(pycurl.RESOLVE, ["%s:%d:127.0.0.1" % (self._serverName, self._dohServerPort)])
conn.setopt(pycurl.SSL_VERIFYPEER, 1)
conn.setopt(pycurl.SSL_VERIFYHOST, 2)
conn.setopt(pycurl.CAINFO, self._caCert)
- conn.setopt(pycurl.CUSTOMREQUEST, 'PATCH')
+ conn.setopt(pycurl.CUSTOMREQUEST, "PATCH")
conn.perform_rb()
rcode = conn.getinfo(pycurl.RESPONSE_CODE)
self.assertEqual(rcode, 400)
"""
DOH: Invalid ALPN
"""
- alpn = ['bogus-alpn']
+ alpn = ["bogus-alpn"]
conn = self.openTLSConnection(self._dohServerPort, self._serverName, self._caCert, alpn=alpn)
try:
- conn.send('AAAA')
+ conn.send("AAAA")
response = conn.recv(65535)
self.assertFalse(response)
except Exception:
pass
metricMap = {
- 'connects': 2,
- 'http/1.1': 3,
- 'http/2': 4,
+ "connects": 2,
+ "http/1.1": 3,
+ "http/2": 4,
}
def getHTTPCounter(self, name):
"""
DOH: HTTP/1.1
"""
- httpConnections = self.getHTTPCounter('connects')
- http1 = self.getHTTPCounter('http/1.1')
- http2 = self.getHTTPCounter('http/2')
- name = 'http11.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ httpConnections = self.getHTTPCounter("connects")
+ http1 = self.getHTTPCounter("http/1.1")
+ http2 = self.getHTTPCounter("http/2")
+ name = "http11.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
wire = query.to_wire()
- b64 = base64.urlsafe_b64encode(wire).decode('UTF8').rstrip('=')
- url = self._dohBaseURL + '?dns=' + b64
+ b64 = base64.urlsafe_b64encode(wire).decode("UTF8").rstrip("=")
+ url = self._dohBaseURL + "?dns=" + b64
responseHeaders = BytesIO()
conn = pycurl.Curl()
conn.setopt(pycurl.HTTP_VERSION, pycurl.CURL_HTTP_VERSION_1_1)
- conn.setopt(pycurl.HTTPHEADER, ["Content-type: application/dns-message",
- "Accept: application/dns-message"])
+ conn.setopt(pycurl.HTTPHEADER, ["Content-type: application/dns-message", "Accept: application/dns-message"])
conn.setopt(pycurl.URL, url)
conn.setopt(pycurl.RESOLVE, ["%s:%d:127.0.0.1" % (self._serverName, self._dohServerPort)])
conn.setopt(pycurl.SSL_VERIFYPEER, 1)
rcode = conn.getinfo(pycurl.RESPONSE_CODE)
responseHeaders = responseHeaders.getvalue()
self.assertEqual(rcode, 400)
- self.assertEqual(data, b'<html><body>This server implements RFC 8484 - DNS Queries over HTTP, and requires HTTP/2 in accordance with section 5.2 of the RFC.</body></html>\r\n')
- self.assertEqual(self.getHTTPCounter('connects'), httpConnections + 1)
- self.assertEqual(self.getHTTPCounter('http/1.1'), http1 + 1)
- self.assertEqual(self.getHTTPCounter('http/2'), http2)
+ self.assertEqual(
+ data,
+ b"<html><body>This server implements RFC 8484 - DNS Queries over HTTP, and requires HTTP/2 in accordance with section 5.2 of the RFC.</body></html>\r\n",
+ )
+ self.assertEqual(self.getHTTPCounter("connects"), httpConnections + 1)
+ self.assertEqual(self.getHTTPCounter("http/1.1"), http1 + 1)
+ self.assertEqual(self.getHTTPCounter("http/2"), http2)
dateFound = False
for header in responseHeaders.decode().splitlines(False):
- values = header.split(':')
+ values = header.split(":")
key = values[0]
- if key.lower() == 'date':
+ if key.lower() == "date":
dateFound = True
break
self.assertTrue(dateFound)
"""
DOH: Check that HTTP/1.1 is not selected over H2 when offered in the wrong order by the client
"""
- alpn = ['http/1.1', 'h2']
+ alpn = ["http/1.1", "h2"]
conn = self.openTLSConnection(self._dohServerPort, self._serverName, self._caCert, alpn=alpn)
- if not hasattr(conn, 'selected_alpn_protocol'):
- raise unittest.SkipTest('Unable to check the selected ALPN, Python version is too old to support selected_alpn_protocol')
- self.assertEqual(conn.selected_alpn_protocol(), 'h2')
+ if not hasattr(conn, "selected_alpn_protocol"):
+ raise unittest.SkipTest(
+ "Unable to check the selected ALPN, Python version is too old to support selected_alpn_protocol"
+ )
+ self.assertEqual(conn.selected_alpn_protocol(), "h2")
def testDOHInvalid(self):
"""
DOH: Invalid DNS query
"""
- name = 'invalid.doh.tests.powerdns.com.'
- invalidQuery = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "invalid.doh.tests.powerdns.com."
+ invalidQuery = dns.message.make_query(name, "A", "IN", use_edns=False)
invalidQuery.id = 0
# first an invalid query
invalidQuery = invalidQuery.to_wire()
invalidQuery = invalidQuery[:-5]
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, caFile=self._caCert, query=invalidQuery, response=None, useQueue=False, rawQuery=True)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ caFile=self._caCert,
+ query=invalidQuery,
+ response=None,
+ useQueue=False,
+ rawQuery=True,
+ )
self.assertEqual(receivedResponse, None)
# and now a valid one
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
DOH: Invalid HTTP header name query
"""
- name = 'invalid-header-name.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "invalid-header-name.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# this header is invalid, see rfc9113 section 8.2.1. Field Validity
- customHeaders = ['{}: test']
+ customHeaders = ["{}: test"]
try:
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert, customHeaders=customHeaders)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ customHeaders=customHeaders,
+ )
self.assertFalse(receivedQuery)
self.assertFalse(receivedResponse)
except pycurl.error:
"""
DOH: No backend
"""
- name = 'no-backend.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "no-backend.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
wire = query.to_wire()
- b64 = base64.urlsafe_b64encode(wire).decode('UTF8').rstrip('=')
- url = self._dohBaseURL + '?dns=' + b64
+ b64 = base64.urlsafe_b64encode(wire).decode("UTF8").rstrip("=")
+ url = self._dohBaseURL + "?dns=" + b64
conn = self.openDOHConnection(self._dohServerPort, self._caCert, timeout=2)
conn.setopt(pycurl.URL, url)
conn.setopt(pycurl.RESOLVE, ["%s:%d:127.0.0.1" % (self._serverName, self._dohServerPort)])
"""
DOH: Empty POST query
"""
- name = 'empty-post.doh.tests.powerdns.com.'
-
- (_, receivedResponse) = self.sendDOHPostQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query="", rawQuery=True, response=None, caFile=self._caCert)
+ name = "empty-post.doh.tests.powerdns.com."
+
+ (_, receivedResponse) = self.sendDOHPostQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ query="",
+ rawQuery=True,
+ response=None,
+ caFile=self._caCert,
+ )
self.assertEqual(receivedResponse, None)
# and now a valid one
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHPostQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHPostQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
DOH: HeaderRule
"""
- name = 'header-rule.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "header-rule.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '2.3.4.5')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "2.3.4.5")
expectedResponse.answer.append(rrset)
# this header should match
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, caFile=self._caCert, query=query, response=None, useQueue=False, customHeaders=['x-powerdnS: aaaaa'])
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ caFile=self._caCert,
+ query=query,
+ response=None,
+ useQueue=False,
+ customHeaders=["x-powerdnS: aaaaa"],
+ )
self.assertEqual(receivedResponse, expectedResponse)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.flags &= ~dns.flags.RD
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# this content of the header should NOT match
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert, customHeaders=['x-powerdnS: bbbbb'])
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ customHeaders=["x-powerdnS: bbbbb"],
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
DOH: HTTPPath
"""
- name = 'http-path.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "http-path.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '3.4.5.6')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "3.4.5.6")
expectedResponse.answer.append(rrset)
# this path should match
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + 'PowerDNS', caFile=self._caCert, query=query, response=None, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL + "PowerDNS",
+ caFile=self._caCert,
+ query=query,
+ response=None,
+ useQueue=False,
+ )
self.assertEqual(receivedResponse, expectedResponse)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
expectedQuery.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# this path should NOT match
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + "PowerDNS2", query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL + "PowerDNS2",
+ query,
+ response=response,
+ caFile=self._caCert,
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
self.assertEqual(response, receivedResponse)
# this path is not in the URLs map and should lead to a 404
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + "PowerDNS/something", query, caFile=self._caCert, useQueue=False, rawResponse=True)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL + "PowerDNS/something",
+ query,
+ caFile=self._caCert,
+ useQueue=False,
+ rawResponse=True,
+ )
self.assertTrue(receivedResponse)
- self.assertEqual(receivedResponse, b'there is no endpoint configured for this path')
+ self.assertEqual(receivedResponse, b"there is no endpoint configured for this path")
self.assertEqual(self._rcode, 404)
def testHTTPPathRegex(self):
"""
DOH: HTTPPathRegex
"""
- name = 'http-path-regex.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "http-path-regex.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '6.7.8.9')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "6.7.8.9")
expectedResponse.answer.append(rrset)
# this path should match
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + 'PowerDNS-999', caFile=self._caCert, query=query, response=None, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL + "PowerDNS-999",
+ caFile=self._caCert,
+ query=query,
+ response=None,
+ useQueue=False,
+ )
self.assertEqual(receivedResponse, expectedResponse)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
expectedQuery.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# this path should NOT match
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + "PowerDNS2", query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL + "PowerDNS2",
+ query,
+ response=response,
+ caFile=self._caCert,
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
DOH: HTTPStatusAction 200 OK
"""
- name = 'http-status-action.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "http-status-action.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False, rawResponse=True)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ query,
+ caFile=self._caCert,
+ useQueue=False,
+ rawResponse=True,
+ )
self.assertTrue(receivedResponse)
- self.assertEqual(receivedResponse, b'Plaintext answer')
+ self.assertEqual(receivedResponse, b"Plaintext answer")
self.assertEqual(self._rcode, 200)
- self.assertIn('content-type: text/plain', self._response_headers.decode())
+ self.assertIn("content-type: text/plain", self._response_headers.decode())
def testHTTPStatusAction307(self):
"""
DOH: HTTPStatusAction 307
"""
- name = 'http-status-action-redirect.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "http-status-action-redirect.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False, rawResponse=True)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ query,
+ caFile=self._caCert,
+ useQueue=False,
+ rawResponse=True,
+ )
self.assertTrue(receivedResponse)
self.assertEqual(self._rcode, 307)
- self.assertIn('location: https://doh.powerdns.org', self._response_headers.decode())
+ self.assertIn("location: https://doh.powerdns.org", self._response_headers.decode())
def testHTTPLuaResponse(self):
"""
DOH: Lua HTTP Response
"""
- name = 'http-lua.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "http-lua.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- (_, receivedResponse) = self.sendDOHPostQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False, rawResponse=True)
+ (_, receivedResponse) = self.sendDOHPostQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ query,
+ caFile=self._caCert,
+ useQueue=False,
+ rawResponse=True,
+ )
self.assertTrue(receivedResponse)
- self.assertEqual(receivedResponse, b'It works!')
+ self.assertEqual(receivedResponse, b"It works!")
self.assertEqual(self._rcode, 200)
- self.assertIn('content-type: text/plain', self._response_headers.decode())
+ self.assertIn("content-type: text/plain", self._response_headers.decode())
def testHTTPEarlyResponse(self):
"""
DOH: HTTP Early Response
"""
response_headers = BytesIO()
- url = self._dohBaseURL + 'coffee'
+ url = self._dohBaseURL + "coffee"
conn = self.openDOHConnection(self._dohServerPort, caFile=self._caCert, timeout=2.0)
conn.setopt(pycurl.URL, url)
conn.setopt(pycurl.RESOLVE, ["%s:%d:127.0.0.1" % (self._serverName, self._dohServerPort)])
headers = response_headers.getvalue().decode()
self.assertEqual(rcode, 418)
- self.assertEqual(data, b'C0FFEE')
- self.assertIn('foo: bar', headers)
+ self.assertEqual(data, b"C0FFEE")
+ self.assertIn("foo: bar", headers)
self.assertNotIn(self._customResponseHeader2, headers)
response_headers = BytesIO()
conn.setopt(pycurl.CAINFO, self._caCert)
conn.setopt(pycurl.HEADERFUNCTION, response_headers.write)
conn.setopt(pycurl.POST, True)
- data = ''
+ data = ""
conn.setopt(pycurl.POSTFIELDS, data)
data = conn.perform_rb()
rcode = conn.getinfo(pycurl.RESPONSE_CODE)
headers = response_headers.getvalue().decode()
self.assertEqual(rcode, 418)
- self.assertEqual(data, b'C0FFEE')
- self.assertIn('foo: bar', headers)
+ self.assertEqual(data, b"C0FFEE")
+ self.assertIn("foo: bar", headers)
self.assertNotIn(self._customResponseHeader2, headers)
def testFrontendAccessViaBuiltInClient(self):
output = None
try:
- confFile = os.path.join('configs', 'dnsdist_%s.conf' % (self.__class__.__name__))
- testcmd = [os.environ['DNSDISTBIN'], '--client', '-C', confFile ]
- process = subprocess.Popen(testcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
- output = process.communicate(input=b'showVersion()\n')
+ confFile = os.path.join("configs", "dnsdist_%s.conf" % (self.__class__.__name__))
+ testcmd = [os.environ["DNSDISTBIN"], "--client", "-C", confFile]
+ process = subprocess.Popen(
+ testcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True
+ )
+ output = process.communicate(input=b"showVersion()\n")
except subprocess.CalledProcessError as exc:
- raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, process.output))
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, process.output))
if process.returncode != 0:
- raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, output))
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, output))
+
+ self.assertTrue(output[0].startswith(b"dnsdist "))
- self.assertTrue(output[0].startswith(b'dnsdist '))
class TestDoHNGHTTP2(DOHTests, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
+
class TestDoHNGHTTP2Yaml(DOHTests, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
_yaml_config_template = """---
console:
key: "%s"
type: "Lua"
function_name: "dohHandler"
"""
- _yaml_config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary']
+ _yaml_config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_dohServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_dohLibrary",
+ ]
_config_template = """
function dohHandler(dq)
if dq:getHTTPScheme() == 'https' and dq:getHTTPHost() == '%s:%d' and dq:getHTTPPath() == '/' and dq:getHTTPQueryString() == '' then
return DNSAction.None
end
"""
- _config_params = ['_serverName', '_dohServerPort']
+ _config_params = ["_serverName", "_dohServerPort"]
+
class DOHSubPathsTests(object):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/PowerDNS" }, {exactPathMatching=false, library='%s'})
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary']
+ _config_params = ["_testServerPort", "_dohServerPort", "_serverCert", "_serverKey", "_dohLibrary"]
def testSubPath(self):
"""
DOH: sub-path
"""
- name = 'sub-path.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "sub-path.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '3.4.5.6')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "3.4.5.6")
expectedResponse.answer.append(rrset)
# this path should match
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + 'PowerDNS', caFile=self._caCert, query=query, response=None, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL + "PowerDNS",
+ caFile=self._caCert,
+ query=query,
+ response=None,
+ useQueue=False,
+ )
self.assertEqual(receivedResponse, expectedResponse)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
expectedQuery.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# this path is not in the URLs map and should lead to a 404
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + "NotPowerDNS", query, caFile=self._caCert, useQueue=False, rawResponse=True)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL + "NotPowerDNS",
+ query,
+ caFile=self._caCert,
+ useQueue=False,
+ rawResponse=True,
+ )
self.assertTrue(receivedResponse)
- self.assertIn(receivedResponse, [b'there is no endpoint configured for this path', b'not found'])
+ self.assertIn(receivedResponse, [b"there is no endpoint configured for this path", b"not found"])
self.assertEqual(self._rcode, 404)
# this path is below one in the URLs map and exactPathMatching is false, so we should be good
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + 'PowerDNS/something', caFile=self._caCert, query=query, response=None, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL + "PowerDNS/something",
+ caFile=self._caCert,
+ query=query,
+ response=None,
+ useQueue=False,
+ )
self.assertEqual(receivedResponse, expectedResponse)
+
class TestDoHSubPathsNGHTTP2(DOHSubPathsTests, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
-class DOHAddingECSTests(object):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+class DOHAddingECSTests(object):
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_config_template = """
newServer{address="127.0.0.1:%d", useClientSubnet=true}
addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/" }, {library='%s'})
setECSOverride(true)
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary']
+ _config_params = ["_testServerPort", "_dohServerPort", "_serverCert", "_serverKey", "_dohLibrary"]
def testDOHSimple(self):
"""
DOH with ECS: Simple query
"""
- name = 'simple.doh-ecs.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple.doh-ecs.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[rewrittenEcso])
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.0", 24)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[rewrittenEcso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
expectedQuery.id = receivedQuery.id
"""
DOH with ECS: Existing EDNS
"""
- name = 'existing-edns.doh-ecs.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=8192)
+ name = "existing-edns.doh-ecs.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=8192)
query.id = 0
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=8192, options=[rewrittenEcso])
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.0", 24)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=8192, options=[rewrittenEcso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
DOH with ECS: Existing EDNS Client Subnet
"""
- name = 'existing-ecs.doh-ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.4')
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=512, options=[ecso], want_dnssec=True)
+ name = "existing-ecs.doh-ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("1.2.3.4")
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.0", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=512, options=[ecso], want_dnssec=True)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=512, options=[rewrittenEcso], want_dnssec=True)
+ expectedQuery = dns.message.make_query(
+ name, "A", "IN", use_edns=True, payload=512, options=[rewrittenEcso], want_dnssec=True
+ )
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[rewrittenEcso])
response.want_dnssec(True)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
self.checkQueryEDNSWithECS(expectedQuery, receivedQuery)
self.checkResponseEDNSWithECS(response, receivedResponse)
+
class TestDoHAddingECSNGHTTP2(DOHAddingECSTests, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
+
class DOHOverHTTP(object):
_dohServerPort = pickAvailablePort()
- _serverName = 'tls.tests.dnsdist.org'
- _dohBaseURL = ("http://%s:%d/dns-query" % (_serverName, _dohServerPort))
+ _serverName = "tls.tests.dnsdist.org"
+ _dohBaseURL = "http://%s:%d/dns-query" % (_serverName, _dohServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
addDOHLocal("127.0.0.1:%d", nil, nil, '/dns-query', {library='%s'})
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_dohLibrary']
+ _config_params = ["_testServerPort", "_dohServerPort", "_dohLibrary"]
def testDOHSimple(self):
"""
DOH over HTTP: Simple query
"""
- name = 'simple.doh-over-http.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple.doh-over-http.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, useHTTPS=False)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, useHTTPS=False
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
expectedQuery.id = receivedQuery.id
"""
DOH over HTTP: Simple POST query
"""
- name = 'simple-post.doh-over-http.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple-post.doh-over-http.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHPostQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, useHTTPS=False)
+ (receivedQuery, receivedResponse) = self.sendDOHPostQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, useHTTPS=False
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
self.assertEqual(response, receivedResponse)
self.checkResponseNoEDNS(response, receivedResponse)
+
class TestDOHOverHTTPNGHTTP2(DOHOverHTTP, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
- _checkConfigExpectedOutputPrefix = b'msg="No certificate provided for DoH frontend, running in DNS over HTTP mode instead of DNS over HTTPS"'
+ _dohLibrary = "nghttp2"
+ _checkConfigExpectedOutputPrefix = (
+ b'msg="No certificate provided for DoH frontend, running in DNS over HTTP mode instead of DNS over HTTPS"'
+ )
-class DOHWithCache(object):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+class DOHWithCache(object):
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/dns-query" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/dns-query" % (_serverName, _dohServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary']
+ _config_params = ["_testServerPort", "_dohServerPort", "_serverCert", "_serverKey", "_dohLibrary"]
def testDOHCacheLargeAnswer(self):
"""
DOH with cache: Check that we can cache (and retrieve) large answers
"""
numberOfQueries = 10
- name = 'large.doh-with-cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "large.doh-with-cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
# we prepare a large answer
content = ""
for i in range(44):
if len(content) > 0:
- content = content + ', '
- content = content + (str(i)*50)
+ content = content + ", "
+ content = content + (str(i) * 50)
# pad up to 4096
- content = content + 'A'*40
+ content = content + "A" * 40
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- content)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, content)
response.answer.append(rrset)
self.assertEqual(len(response.to_wire()), 4096)
# first query to fill the cache
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
self.assertEqual(expectedQuery, receivedQuery)
self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
self.assertEqual(response, receivedResponse)
- self.checkHasHeader('cache-control', 'max-age=3600')
+ self.checkHasHeader("cache-control", "max-age=3600")
for _ in range(numberOfQueries):
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False
+ )
self.assertEqual(receivedResponse, response)
- self.checkHasHeader('cache-control', 'max-age=' + str(receivedResponse.answer[0].ttl))
+ self.checkHasHeader("cache-control", "max-age=" + str(receivedResponse.answer[0].ttl))
time.sleep(1)
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False
+ )
self.assertEqual(receivedResponse, response)
- self.checkHasHeader('cache-control', 'max-age=' + str(receivedResponse.answer[0].ttl))
+ self.checkHasHeader("cache-control", "max-age=" + str(receivedResponse.answer[0].ttl))
def testDOHGetFromUDPCache(self):
"""
DOH with cache: Check that we can retrieve an answer received for a UDP query
"""
- name = 'doh-query-insert-udp.doh-with-cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ name = "doh-query-insert-udp.doh-with-cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.84')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.84")
response.answer.append(rrset)
# first query to fill the cache
self.assertEqual(response, receivedResponse)
# now we send the exact same query over DoH, we should get a cache hit
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False
+ )
self.assertTrue(receivedResponse)
self.assertEqual(response, receivedResponse)
"""
DOH with cache: Check that we can retrieve an answer received for a DoH query from UDP
"""
- name = 'udp-query-get-doh.doh-with-cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ name = "udp-query-get-doh.doh-with-cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.84')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.84")
response.answer.append(rrset)
# first query to fill the cache
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
# the query is first forwarded over UDP, leading to a TC=1 answer from the
# backend, then over TCP
- name = 'truncated-udp.doh-with-cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "truncated-udp.doh-with-cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 42
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 42
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# first response is a TC=1
tcResponse.flags |= dns.flags.TC
self._toResponderQueue.put(tcResponse, True, 2.0)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, response=response)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, response=response
+ )
# first query, received by the responder over UDP
self.assertTrue(receivedQuery)
receivedQuery.id = expectedQuery.id
self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
# now check the cache for a DoH query
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False
+ )
self.assertEqual(response, receivedResponse)
# The TC=1 answer received over UDP will not be cached, because we currently do not cache answers with no records (no TTL)
"""
DOH: Check that responses received over UDP are cached (with cache)
"""
- name = 'cached-udp.doh-with-cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "cached-udp.doh-with-cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, response=response)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, response=response
+ )
self.assertTrue(receivedQuery)
receivedQuery.id = expectedQuery.id
self.assertEqual(expectedQuery, receivedQuery)
self.assertEqual(response, receivedResponse)
# now check the cache for a DoH query
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False
+ )
self.assertEqual(response, receivedResponse)
# Check that the answer is usable for UDP queries as well
(_, receivedResponse) = self.sendUDPQuery(expectedQuery, response=None, useQueue=False)
self.assertEqual(response, receivedResponse)
+
class TestDOHWithCacheNGHTTP2(DOHWithCache, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
_verboseMode = True
-class DOHWithoutCacheControl(object):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+class DOHWithoutCacheControl(object):
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/" }, {sendCacheControlHeaders=false, library='%s'})
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary']
+ _config_params = ["_testServerPort", "_dohServerPort", "_serverCert", "_serverKey", "_dohLibrary"]
def testDOHSimple(self):
"""
DOH without cache-control
"""
- name = 'simple.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
self.assertEqual(expectedQuery, receivedQuery)
- self.checkNoHeader('cache-control')
+ self.checkNoHeader("cache-control")
self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
self.assertEqual(response, receivedResponse)
+
class TestDOHWithoutCacheControlNGHTTP2(DOHWithoutCacheControl, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
+
class DOHFFI(object):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _customResponseHeader1 = 'access-control-allow-origin: *'
- _customResponseHeader2 = 'user-agent: derp'
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _customResponseHeader1 = "access-control-allow-origin: *"
+ _customResponseHeader2 = "user-agent: derp"
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
end
addAction("http-lua-ffi.doh.tests.powerdns.com.", LuaFFIAction(dohHandler))
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary', '_serverName', '_dohServerPort']
+ _config_params = [
+ "_testServerPort",
+ "_dohServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_dohLibrary",
+ "_serverName",
+ "_dohServerPort",
+ ]
def testHTTPLuaFFIResponse(self):
"""
DOH: Lua FFI HTTP Response
"""
- name = 'http-lua-ffi.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "http-lua-ffi.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- (_, receivedResponse) = self.sendDOHPostQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False, rawResponse=True)
+ (_, receivedResponse) = self.sendDOHPostQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ query,
+ caFile=self._caCert,
+ useQueue=False,
+ rawResponse=True,
+ )
self.assertTrue(receivedResponse)
- self.assertEqual(receivedResponse, b'It works!')
+ self.assertEqual(receivedResponse, b"It works!")
self.assertEqual(self._rcode, 200)
- self.assertIn('content-type: text/plain', self._response_headers.decode())
+ self.assertIn("content-type: text/plain", self._response_headers.decode())
+
class TestDOHFFINGHTTP2(DOHFFI, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
+
class DOHForwardedFor(object):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
-- that code along with X-Forwarded-For support
setMaxTCPConnectionsPerClient(2)
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary']
+ _config_params = ["_testServerPort", "_dohServerPort", "_serverCert", "_serverKey", "_dohLibrary"]
def testDOHAllowedForwarded(self):
"""
DOH with X-Forwarded-For allowed
"""
- name = 'allowed.forwarded.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "allowed.forwarded.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert, customHeaders=['x-forwarded-for: 127.0.0.1:42, 127.0.0.1, 192.0.2.1:4200'])
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ customHeaders=["x-forwarded-for: 127.0.0.1:42, 127.0.0.1, 192.0.2.1:4200"],
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
DOH with X-Forwarded-For not allowed
"""
- name = 'not-allowed.forwarded.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "not-allowed.forwarded.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert, useQueue=False, rawResponse=True, customHeaders=['x-forwarded-for: 127.0.0.1:42, 127.0.0.1'])
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ useQueue=False,
+ rawResponse=True,
+ customHeaders=["x-forwarded-for: 127.0.0.1:42, 127.0.0.1"],
+ )
self.assertEqual(self._rcode, 403)
- self.assertEqual(receivedResponse, b'DoH query not allowed because of ACL')
+ self.assertEqual(receivedResponse, b"DoH query not allowed because of ACL")
+
class TestDOHForwardedForNGHTTP2(DOHForwardedFor, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
-class DOHForwardedForNoTrusted(object):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+class DOHForwardedForNoTrusted(object):
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
setACL('192.0.2.1/32')
addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/" }, {earlyACLDrop=true, library='%s'})
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary']
+ _config_params = ["_testServerPort", "_dohServerPort", "_serverCert", "_serverKey", "_dohLibrary"]
def testDOHForwardedUntrusted(self):
"""
DOH with X-Forwarded-For not trusted
"""
- name = 'not-trusted.forwarded.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "not-trusted.forwarded.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
dropped = False
try:
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert, useQueue=False, rawResponse=True, customHeaders=['x-forwarded-for: 192.0.2.1:4200'])
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ useQueue=False,
+ rawResponse=True,
+ customHeaders=["x-forwarded-for: 192.0.2.1:4200"],
+ )
self.assertEqual(self._rcode, 403)
- self.assertEqual(receivedResponse, b'DoH query not allowed because of ACL')
+ self.assertEqual(receivedResponse, b"DoH query not allowed because of ACL")
except pycurl.error as e:
dropped = True
self.assertTrue(dropped)
+
class TestDOHForwardedForNoTrustedNGHTTP2(DOHForwardedForNoTrusted, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
-class DOHFrontendLimits(object):
+class DOHFrontendLimits(object):
# this test suite uses a different responder port
# because it uses a different health check configuration
_testServerPort = pickAvailablePort()
_answerUnexpected = True
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_skipListeningOnCL = True
_maxTCPConnsPerDOHFrontend = 5
_config_template = """
newServer{address="127.0.0.1:%d"}
addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/" }, { maxConcurrentTCPConnections=%d, library='%s' })
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_maxTCPConnsPerDOHFrontend', '_dohLibrary']
- _alternateListeningAddr = '127.0.0.1'
+ _config_params = [
+ "_testServerPort",
+ "_dohServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_maxTCPConnsPerDOHFrontend",
+ "_dohLibrary",
+ ]
+ _alternateListeningAddr = "127.0.0.1"
_alternateListeningPort = _dohServerPort
def testTCPConnsPerDOHFrontend(self):
for idx in range(self._maxTCPConnsPerDOHFrontend + 1):
try:
- alpn = ['h2']
+ alpn = ["h2"]
conns.append(self.openTLSConnection(self._dohServerPort, self._serverName, self._caCert, alpn=alpn))
except Exception:
conns.append(None)
self.assertEqual(count, self._maxTCPConnsPerDOHFrontend)
self.assertEqual(failed, 1)
+
class TestDOHFrontendLimitsNGHTTP2(DOHFrontendLimits, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
+
class Protocols(object):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _customResponseHeader1 = 'access-control-allow-origin: *'
- _customResponseHeader2 = 'user-agent: derp'
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _customResponseHeader1 = "access-control-allow-origin: *"
+ _customResponseHeader2 = "user-agent: derp"
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_config_template = """
function checkDOH(dq)
if dq:getProtocol() ~= "DNS over HTTPS" then
newServer{address="127.0.0.1:%d"}
addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/" }, {library='%s'})
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary']
+ _config_params = ["_testServerPort", "_dohServerPort", "_serverCert", "_serverKey", "_dohLibrary"]
def testProtocolDOH(self):
"""
DoH: Test DNSQuestion.Protocol
"""
- name = 'protocols.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "protocols.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
self.assertEqual(response, receivedResponse)
+
class TestProtocolsNGHTTP2(Protocols, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
+
class DOHWithPKCS12Cert(object):
- _serverCert = 'server.p12'
- _pkcs12Password = 'passw0rd'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverCert = "server.p12"
+ _pkcs12Password = "passw0rd"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
cert=newTLSCertificate("%s", {password="%s"})
addDOHLocal("127.0.0.1:%d", cert, "", { "/" }, {library='%s'})
"""
- _config_params = ['_testServerPort', '_serverCert', '_pkcs12Password', '_dohServerPort', '_dohLibrary']
+ _config_params = ["_testServerPort", "_serverCert", "_pkcs12Password", "_dohServerPort", "_dohLibrary"]
def testPKCS12DOH(self):
"""
DoH: Test Simple DOH Query with a password protected PKCS12 file configured
"""
- name = 'simple.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
self.assertEqual(expectedQuery, receivedQuery)
+
class TestDOHWithPKCS12CertNGHTTP2(DOHWithPKCS12Cert, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
+
class DOHForwardedToTCPOnly(object):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_config_template = """
newServer{address="127.0.0.1:%d", tcpOnly=true}
addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/" }, {library='%s'})
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary']
+ _config_params = ["_testServerPort", "_dohServerPort", "_serverCert", "_serverKey", "_dohLibrary"]
def testDOHTCPOnly(self):
"""
DoH: Test a DoH query forwarded to a TCP-only server
"""
- name = 'tcponly.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tcponly.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 42
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = query.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
+
class TestDOHForwardedToTCPOnlyNGHTTP2(DOHForwardedToTCPOnly, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
+
class DOHLimits(object):
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
_maxTCPConnsPerClient = 3
_config_template = """
newServer{address="127.0.0.1:%d"}
addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/" }, {library='%s'})
setMaxTCPConnectionsPerClient(%d)
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary', '_maxTCPConnsPerClient']
+ _config_params = [
+ "_testServerPort",
+ "_dohServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_dohLibrary",
+ "_maxTCPConnsPerClient",
+ ]
def testConnsPerClient(self):
"""
DoH Limits: Maximum number of conns per client
"""
- name = 'maxconnsperclient.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "maxconnsperclient.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
url = self.getDOHGetURL(self._dohBaseURL, query)
conns = []
self.assertEqual(count, self._maxTCPConnsPerClient)
self.assertEqual(failed, 1)
+
class TestDOHLimitsNGHTTP2(DOHLimits, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+ _dohLibrary = "nghttp2"
+
class DOHXFR(object):
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
_maxTCPConnsPerClient = 3
_config_template = """
newServer{address="127.0.0.1:%d", tcpOnly=true}
addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/" }, {library='%s'})
"""
- _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_dohLibrary']
+ _config_params = ["_testServerPort", "_dohServerPort", "_serverCert", "_serverKey", "_dohLibrary"]
def testXFR(self):
"""
DoH XFR: Check that XFR requests over DoH are refused with NotImp
"""
- name = 'xfr.doh.tests.powerdns.com.'
+ name = "xfr.doh.tests.powerdns.com."
for xfrType in [dns.rdatatype.AXFR, dns.rdatatype.IXFR]:
- query = dns.message.make_query(name, xfrType, 'IN')
+ query = dns.message.make_query(name, xfrType, "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NOTIMP)
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False
+ )
self.assertEqual(receivedResponse, expectedResponse)
-class TestDOHXFRNGHTTP2(DOHXFR, DNSDistDOHTest):
- _dohLibrary = 'nghttp2'
+class TestDOHXFRNGHTTP2(DOHXFR, DNSDistDOHTest):
+ _dohLibrary = "nghttp2"
from dnsdisttests import pickAvailablePort
from quictests import QUICTests, QUICACLTests, QUICGetLocalAddressOnAnyBindTests, QUICXFRTests
+
class DOH3Common(object):
def getQUICConnection(self):
return self.getDOQConnection(self._doqServerPort, self._caCert)
def sendQUICQuery(self, query, response=None, useQueue=True, connection=None, passExceptions=False):
- return self.sendDOH3Query(self._doqServerPort, self._dohBaseURL, query, response=response, caFile=self._caCert, useQueue=useQueue, serverName=self._serverName, connection=connection, passExceptions=passExceptions)
+ return self.sendDOH3Query(
+ self._doqServerPort,
+ self._dohBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ useQueue=useQueue,
+ serverName=self._serverName,
+ connection=connection,
+ passExceptions=passExceptions,
+ )
+
class TestDOH3(DOH3Common, QUICTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doqServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doqServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
addDOH3Local("127.0.0.1:%d", "%s", "%s", {keyLogFile='/tmp/keys'})
"""
- _config_params = ['_testServerPort', '_serverName', '_doqServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_serverName", "_doqServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
_verboseMode = True
def testHeaderRule(self):
"""
DOH3: HeaderRule
"""
- name = 'header-rule.doh3.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "header-rule.doh3.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '2.3.4.5')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "2.3.4.5")
expectedResponse.answer.append(rrset)
# this header should match
- (_, receivedResponse) = self.sendDOH3Query(self._doqServerPort, self._dohBaseURL, query=query, response=None, useQueue=False, caFile=self._caCert, customHeaders={'x-powerdnS': 'aaaaa'})
+ (_, receivedResponse) = self.sendDOH3Query(
+ self._doqServerPort,
+ self._dohBaseURL,
+ query=query,
+ response=None,
+ useQueue=False,
+ caFile=self._caCert,
+ customHeaders={"x-powerdnS": "aaaaa"},
+ )
self.assertEqual(receivedResponse, expectedResponse)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.flags &= ~dns.flags.RD
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# this content of the header should NOT match
- (receivedQuery, receivedResponse) = self.sendDOH3Query(self._doqServerPort, self._dohBaseURL, query, response=response, caFile=self._caCert, customHeaders={'x-powerdnS': 'bbbbb'})
+ (receivedQuery, receivedResponse) = self.sendDOH3Query(
+ self._doqServerPort,
+ self._dohBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ customHeaders={"x-powerdnS": "bbbbb"},
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
DOH3: HTTPPath
"""
- name = 'http-path.doh3.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "http-path.doh3.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '3.4.5.6')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "3.4.5.6")
expectedResponse.answer.append(rrset)
# this path should match
- (_, receivedResponse) = self.sendDOH3Query(self._doqServerPort, self._dohBaseURL + 'PowerDNS', caFile=self._caCert, query=query, response=None, useQueue=False)
+ (_, receivedResponse) = self.sendDOH3Query(
+ self._doqServerPort,
+ self._dohBaseURL + "PowerDNS",
+ caFile=self._caCert,
+ query=query,
+ response=None,
+ useQueue=False,
+ )
self.assertEqual(receivedResponse, expectedResponse)
- expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ expectedQuery = dns.message.make_query(name, "A", "IN")
expectedQuery.id = 0
expectedQuery.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# this path should NOT match
- (receivedQuery, receivedResponse) = self.sendDOH3Query(self._doqServerPort, self._dohBaseURL + "PowerDNS2", query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOH3Query(
+ self._doqServerPort, self._dohBaseURL + "PowerDNS2", query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
DOH3: HTTPPathRegex
"""
- name = 'http-path-regex.doh3.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "http-path-regex.doh3.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 0
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '6.7.8.9')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "6.7.8.9")
expectedResponse.answer.append(rrset)
# this path should match
- (_, receivedResponse) = self.sendDOH3Query(self._doqServerPort, self._dohBaseURL + 'PowerDNS-999', caFile=self._caCert, query=query, response=None, useQueue=False)
+ (_, receivedResponse) = self.sendDOH3Query(
+ self._doqServerPort,
+ self._dohBaseURL + "PowerDNS-999",
+ caFile=self._caCert,
+ query=query,
+ response=None,
+ useQueue=False,
+ )
self.assertEqual(receivedResponse, expectedResponse)
- expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ expectedQuery = dns.message.make_query(name, "A", "IN")
expectedQuery.id = 0
expectedQuery.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# this path should NOT match
- (receivedQuery, receivedResponse) = self.sendDOH3Query(self._doqServerPort, self._dohBaseURL + "PowerDNS2", query, response=response, caFile=self._caCert)
+ (receivedQuery, receivedResponse) = self.sendDOH3Query(
+ self._doqServerPort, self._dohBaseURL + "PowerDNS2", query, response=response, caFile=self._caCert
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
"""
DOH3: HTTPStatusAction 200 OK
"""
- name = 'http-status-action.doh3.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "http-status-action.doh3.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- (receivedResponse, receivedHeaders) = self.sendDOH3Query(self._doqServerPort, self._dohBaseURL, query, caFile=self._caCert, useQueue=False, post=True, rawResponse=True)
+ (receivedResponse, receivedHeaders) = self.sendDOH3Query(
+ self._doqServerPort,
+ self._dohBaseURL,
+ query,
+ caFile=self._caCert,
+ useQueue=False,
+ post=True,
+ rawResponse=True,
+ )
self.assertTrue(receivedResponse)
- self.assertEqual(receivedResponse, b'Plaintext answer')
- self.assertIn(b':status', receivedHeaders)
- self.assertEqual(receivedHeaders[b':status'], b'200')
- self.assertIn(b'content-type', receivedHeaders)
- self.assertEqual(receivedHeaders[b'content-type'], b'text/plain')
+ self.assertEqual(receivedResponse, b"Plaintext answer")
+ self.assertIn(b":status", receivedHeaders)
+ self.assertEqual(receivedHeaders[b":status"], b"200")
+ self.assertIn(b"content-type", receivedHeaders)
+ self.assertEqual(receivedHeaders[b"content-type"], b"text/plain")
def testHTTPStatusAction307(self):
"""
DOH3: HTTPStatusAction 307
"""
- name = 'http-status-action-redirect.doh3.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "http-status-action-redirect.doh3.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- (receivedResponse, receivedHeaders) = self.sendDOH3Query(self._doqServerPort, self._dohBaseURL, query, caFile=self._caCert, useQueue=False, post=True, rawResponse=True)
+ (receivedResponse, receivedHeaders) = self.sendDOH3Query(
+ self._doqServerPort,
+ self._dohBaseURL,
+ query,
+ caFile=self._caCert,
+ useQueue=False,
+ post=True,
+ rawResponse=True,
+ )
self.assertTrue(receivedResponse)
- self.assertIn(b':status', receivedHeaders)
- self.assertEqual(receivedHeaders[b':status'], b'307')
- self.assertIn(b'location', receivedHeaders)
- self.assertEqual(receivedHeaders[b'location'], b'https://doh.powerdns.org')
+ self.assertIn(b":status", receivedHeaders)
+ self.assertEqual(receivedHeaders[b":status"], b"307")
+ self.assertIn(b"location", receivedHeaders)
+ self.assertEqual(receivedHeaders[b"location"], b"https://doh.powerdns.org")
def testHTTPLuaBindings(self):
"""
DOH3: Lua HTTP bindings
"""
- name = 'http-lua.doh3.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "http-lua.doh3.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- (receivedResponse, receivedHeaders) = self.sendDOH3Query(self._doqServerPort, self._dohBaseURL, query, caFile=self._caCert, useQueue=False, post=True, rawResponse=True)
+ (receivedResponse, receivedHeaders) = self.sendDOH3Query(
+ self._doqServerPort,
+ self._dohBaseURL,
+ query,
+ caFile=self._caCert,
+ useQueue=False,
+ post=True,
+ rawResponse=True,
+ )
self.assertTrue(receivedResponse)
- self.assertEqual(receivedResponse, b'It works!')
- self.assertIn(b':status', receivedHeaders)
- self.assertEqual(receivedHeaders[b':status'], b'200')
- self.assertIn(b'content-type', receivedHeaders)
- self.assertEqual(receivedHeaders[b'content-type'], b'text/plain')
+ self.assertEqual(receivedResponse, b"It works!")
+ self.assertIn(b":status", receivedHeaders)
+ self.assertEqual(receivedHeaders[b":status"], b"200")
+ self.assertIn(b"content-type", receivedHeaders)
+ self.assertEqual(receivedHeaders[b"content-type"], b"text/plain")
+
class TestDOH3Yaml(DOH3Common, QUICTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doqServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doqServerPort)
_config_template = ""
_config_params = []
_yaml_config_template = """---
type: "Pool"
pool_name: "this-pool-has-no-backend"
"""
- _yaml_config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _yaml_config_params = ["_testServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
+
class TestDOH3ACL(DOH3Common, QUICACLTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doqServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doqServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
setACL("192.0.2.1/32")
addDOH3Local("127.0.0.1:%d", "%s", "%s", {keyLogFile='/tmp/keys'})
"""
- _config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
_verboseMode = True
+
class TestDOH3Specifics(DOH3Common, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doqServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doqServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
addDOH3Local("127.0.0.1:%d", "%s", "%s", {keyLogFile='/tmp/keys'})
"""
- _config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
_verboseMode = True
def testDOH3Post(self):
"""
QUIC: Simple POST query
"""
- name = 'simple.post.doq.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple.post.doq.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendDOH3Query(self._doqServerPort, self._dohBaseURL, query, response=response, caFile=self._caCert, serverName=self._serverName, post=True)
+ (receivedQuery, receivedResponse) = self.sendDOH3Query(
+ self._doqServerPort,
+ self._dohBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ serverName=self._serverName,
+ post=True,
+ )
self.assertTrue(receivedQuery)
self.assertTrue(receivedResponse)
receivedQuery.id = expectedQuery.id
self.assertEqual(expectedQuery, receivedQuery)
self.assertEqual(receivedResponse, response)
+
class TestDOH3GetLocalAddressOnAnyBind(DOH3Common, QUICGetLocalAddressOnAnyBindTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doqServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doqServerPort)
_config_template = """
function answerBasedOnLocalAddress(dq)
local dest = tostring(dq.localaddr)
addDOH3Local("0.0.0.0:%d", "%s", "%s")
addDOH3Local("[::]:%d", "%s", "%s")
"""
- _config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey', '_doqServerPort','_serverCert', '_serverKey']
- _acl = ['127.0.0.1/32', '::1/128']
+ _config_params = [
+ "_testServerPort",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
+ _acl = ["127.0.0.1/32", "::1/128"]
_skipListeningOnCL = True
+
class TestDOH3XFR(DOH3Common, QUICXFRTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doqServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doqServerPort)
_config_template = """
newServer{address="127.0.0.1:%d", tcpOnly=true}
addDOH3Local("127.0.0.1:%d", "%s", "%s")
"""
- _config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
_verboseMode = True
from quictests import QUICTests, QUICWithCacheTests, QUICACLTests, QUICGetLocalAddressOnAnyBindTests, QUICXFRTests
import doqclient
+
class TestDOQBogus(DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_config_template = """
newServer{address="127.0.0.1:%d"}
addDOQLocal("127.0.0.1:%d", "%s", "%s")
"""
- _config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
def testDOQBogus(self):
"""
DOQ: Test a bogus query (wrong packed length)
"""
- name = 'bogus.doq.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "bogus.doq.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
try:
- doqclient.quic_bogus_query(query, '127.0.0.1', 2.0, self._doqServerPort, verify=self._caCert, server_hostname=self._serverName)
+ doqclient.quic_bogus_query(
+ query, "127.0.0.1", 2.0, self._doqServerPort, verify=self._caCert, server_hostname=self._serverName
+ )
self.fail()
- except doqclient.StreamResetError as e :
- self.assertEqual(e.error, 2);
+ except doqclient.StreamResetError as e:
+ self.assertEqual(e.error, 2)
+
class DOQCommon(object):
def getQUICConnection(self):
return self.getDOQConnection(self._doqServerPort, self._caCert)
def sendQUICQuery(self, query, response=None, useQueue=True, connection=None, passExceptions=False):
- return self.sendDOQQuery(self._doqServerPort, query, response=response, caFile=self._caCert, useQueue=useQueue, serverName=self._serverName, connection=connection, passExceptions=passExceptions)
+ return self.sendDOQQuery(
+ self._doqServerPort,
+ query,
+ response=response,
+ caFile=self._caCert,
+ useQueue=useQueue,
+ serverName=self._serverName,
+ connection=connection,
+ passExceptions=passExceptions,
+ )
+
class TestDOQ(DOQCommon, QUICTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_config_template = """
newServer{address="127.0.0.1:%d"}
addDOQLocal("127.0.0.1:%d", "%s", "%s")
"""
- _config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
+
class TestDOQYaml(DOQCommon, QUICTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_config_template = ""
_config_params = []
type: "Pool"
pool_name: "this-pool-has-no-backend"
"""
- _yaml_config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _yaml_config_params = ["_testServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
+
class TestDOQWithCache(DOQCommon, QUICWithCacheTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_config_template = """
newServer{address="127.0.0.1:%d"}
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
"""
- _config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
+
class TestDOQWithCacheAndBBR(DOQCommon, QUICWithCacheTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_config_template = """
newServer{address="127.0.0.1:%d"}
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
"""
- _config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
+
class TestDOQWithACL(DOQCommon, QUICACLTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_config_template = """
newServer{address="127.0.0.1:%d"}
setACL("192.0.2.1/32")
addDOQLocal("127.0.0.1:%d", "%s", "%s")
"""
- _config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
+
class TestDOQXFR(DOQCommon, QUICXFRTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_config_template = """
newServer{address="127.0.0.1:%d", tcpOnly=True}
addDOQLocal("127.0.0.1:%d", "%s", "%s")
"""
- _config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_doqServerPort", "_serverCert", "_serverKey"]
_verboseMode = True
+
class TestDOQCertificateReloading(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _serverKey = 'server-doq.key'
- _serverCert = 'server-doq.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _serverKey = "server-doq.key"
+ _serverCert = "server-doq.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_config_template = """
setKey("%s")
addDOQLocal("127.0.0.1:%d", "%s", "%s")
"""
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_doqServerPort','_serverCert', '_serverKey']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
@classmethod
def setUpClass(cls):
- cls.generateNewCertificateAndKey('server-doq')
+ cls.generateNewCertificateAndKey("server-doq")
cls.startResponders()
cls.startDNSDist()
cls.setUpSockets()
def testCertificateReloaded(self):
- name = 'certificate-reload.doq.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "certificate-reload.doq.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- (_, serial) = doqclient.quic_query(query, '127.0.0.1', 0.5, self._doqServerPort, verify=self._caCert, server_hostname=self._serverName)
+ (_, serial) = doqclient.quic_query(
+ query, "127.0.0.1", 0.5, self._doqServerPort, verify=self._caCert, server_hostname=self._serverName
+ )
- self.generateNewCertificateAndKey('server-doq')
+ self.generateNewCertificateAndKey("server-doq")
self.sendConsoleCommand("reloadAllCertificates()")
- (_, secondSerial) = doqclient.quic_query(query, '127.0.0.1', 0.5, self._doqServerPort, verify=self._caCert, server_hostname=self._serverName)
+ (_, secondSerial) = doqclient.quic_query(
+ query, "127.0.0.1", 0.5, self._doqServerPort, verify=self._caCert, server_hostname=self._serverName
+ )
# check that the serial is different
self.assertNotEqual(serial, secondSerial)
+
class TestDOQGetLocalAddressOnAnyBind(DOQCommon, QUICGetLocalAddressOnAnyBindTests, DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_config_template = """
function answerBasedOnLocalAddress(dq)
addDOQLocal("0.0.0.0:%d", "%s", "%s")
addDOQLocal("[::]:%d", "%s", "%s")
"""
- _config_params = ['_testServerPort', '_doqServerPort','_serverCert', '_serverKey', '_doqServerPort','_serverCert', '_serverKey']
- _acl = ['127.0.0.1/32', '::1/128']
+ _config_params = [
+ "_testServerPort",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
+ _acl = ["127.0.0.1/32", "::1/128"]
_skipListeningOnCL = True
import dns
from dnsdisttests import DNSDistTest
-class TestDeprecatedMakeRule(DNSDistTest):
+class TestDeprecatedMakeRule(DNSDistTest):
_config_template = """
addAction(makeRule("make-rule-suffix.deprecated.tests.powerdns.com."), SpoofAction("192.0.2.1"))
addAction("string-suffix.deprecated.tests.powerdns.com.", SpoofAction("192.0.2.2"))
"""
Deprecated: makeRule
"""
- name = 'prefix.make-rule-suffix.deprecated.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "prefix.make-rule-suffix.deprecated.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Deprecated: addAction string suffix
"""
- name = 'another.prefix.string-suffix.deprecated.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "another.prefix.string-suffix.deprecated.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Deprecated: addAction list of string suffixes
"""
- name = 'yet.another.prefix.list-of-string-suffixes.deprecated.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "yet.another.prefix.list-of-string-suffixes.deprecated.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.3')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.3")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
def checkDnstapBase(testinstance, dnstap, protocol, initiator, response_port):
testinstance.assertTrue(dnstap)
- testinstance.assertTrue(dnstap.HasField('identity'))
- testinstance.assertEqual(dnstap.identity, b'a.server')
- testinstance.assertTrue(dnstap.HasField('version'))
- testinstance.assertIn(b'dnsdist ', dnstap.version)
- testinstance.assertTrue(dnstap.HasField('type'))
+ testinstance.assertTrue(dnstap.HasField("identity"))
+ testinstance.assertEqual(dnstap.identity, b"a.server")
+ testinstance.assertTrue(dnstap.HasField("version"))
+ testinstance.assertIn(b"dnsdist ", dnstap.version)
+ testinstance.assertTrue(dnstap.HasField("type"))
testinstance.assertEqual(dnstap.type, dnstap.MESSAGE)
- testinstance.assertTrue(dnstap.HasField('message'))
- testinstance.assertTrue(dnstap.message.HasField('socket_protocol'))
+ testinstance.assertTrue(dnstap.HasField("message"))
+ testinstance.assertTrue(dnstap.message.HasField("socket_protocol"))
testinstance.assertEqual(dnstap.message.socket_protocol, protocol)
- testinstance.assertTrue(dnstap.message.HasField('socket_family'))
+ testinstance.assertTrue(dnstap.message.HasField("socket_family"))
testinstance.assertEqual(dnstap.message.socket_family, dnstap_pb2.INET)
- testinstance.assertTrue(dnstap.message.HasField('query_address'))
+ testinstance.assertTrue(dnstap.message.HasField("query_address"))
testinstance.assertEqual(socket.inet_ntop(socket.AF_INET, dnstap.message.query_address), initiator)
- testinstance.assertTrue(dnstap.message.HasField('response_address'))
+ testinstance.assertTrue(dnstap.message.HasField("response_address"))
testinstance.assertEqual(socket.inet_ntop(socket.AF_INET, dnstap.message.response_address), initiator)
- testinstance.assertTrue(dnstap.message.HasField('response_port'))
+ testinstance.assertTrue(dnstap.message.HasField("response_port"))
testinstance.assertEqual(dnstap.message.response_port, response_port)
-def checkDnstapQuery(testinstance, dnstap, protocol, query, initiator='127.0.0.1', response_port=0, http_protocol=0):
+def checkDnstapQuery(testinstance, dnstap, protocol, query, initiator="127.0.0.1", response_port=0, http_protocol=0):
testinstance.assertEqual(dnstap.message.type, dnstap_pb2.Message.CLIENT_QUERY)
- if response_port == 0 :
+ if response_port == 0:
response_port = testinstance._dnsDistPort
checkDnstapBase(testinstance, dnstap, protocol, initiator, response_port)
- testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
- testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_sec"))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_nsec"))
- if http_protocol != 0 :
- testinstance.assertTrue(dnstap.message.HasField('http_protocol'))
+ if http_protocol != 0:
+ testinstance.assertTrue(dnstap.message.HasField("http_protocol"))
testinstance.assertEqual(dnstap.message.http_protocol, http_protocol)
- testinstance.assertTrue(dnstap.message.HasField('query_message'))
+ testinstance.assertTrue(dnstap.message.HasField("query_message"))
wire_message = dns.message.from_wire(dnstap.message.query_message)
testinstance.assertEqual(wire_message, query)
def checkDnstapExtra(testinstance, dnstap, expected):
- testinstance.assertTrue(dnstap.HasField('extra'))
+ testinstance.assertTrue(dnstap.HasField("extra"))
testinstance.assertEqual(dnstap.extra, expected)
+
def checkDnstapNoExtra(testinstance, dnstap):
- testinstance.assertFalse(dnstap.HasField('extra'))
+ testinstance.assertFalse(dnstap.HasField("extra"))
-def checkDnstapResponse(testinstance, dnstap, protocol, response, initiator='127.0.0.1', response_port=0):
+def checkDnstapResponse(testinstance, dnstap, protocol, response, initiator="127.0.0.1", response_port=0):
testinstance.assertEqual(dnstap.message.type, dnstap_pb2.Message.CLIENT_RESPONSE)
- if response_port == 0 :
+ if response_port == 0:
response_port = testinstance._dnsDistPort
checkDnstapBase(testinstance, dnstap, protocol, initiator, response_port)
- testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
- testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_sec"))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_nsec"))
- testinstance.assertTrue(dnstap.message.HasField('response_time_sec'))
- testinstance.assertTrue(dnstap.message.HasField('response_time_nsec'))
+ testinstance.assertTrue(dnstap.message.HasField("response_time_sec"))
+ testinstance.assertTrue(dnstap.message.HasField("response_time_nsec"))
- testinstance.assertTrue(dnstap.message.response_time_sec > dnstap.message.query_time_sec or \
- dnstap.message.response_time_nsec > dnstap.message.query_time_nsec)
+ testinstance.assertTrue(
+ dnstap.message.response_time_sec > dnstap.message.query_time_sec
+ or dnstap.message.response_time_nsec > dnstap.message.query_time_nsec
+ )
- testinstance.assertTrue(dnstap.message.HasField('response_message'))
+ testinstance.assertTrue(dnstap.message.HasField("response_message"))
wire_message = dns.message.from_wire(dnstap.message.response_message)
testinstance.assertEqual(wire_message, response)
return selected
+
class TestDnstapOverRemoteLogger(DNSDistTest):
_remoteLoggerServerPort = pickAvailablePort()
_remoteLoggerQueue = Queue()
_remoteLoggerCounter = 0
- _config_params = ['_testServerPort', '_remoteLoggerServerPort']
+ _config_params = ["_testServerPort", "_remoteLoggerServerPort"]
_config_template = """
extrasmn = newSuffixMatchNode()
extrasmn:add(newDNSName('extra.dnstap.tests.powerdns.com.'))
def startResponders(cls):
DNSDistTest.startResponders()
- cls._remoteLoggerListener = threading.Thread(name='RemoteLogger Listener', target=cls.RemoteLoggerListener, args=[cls._remoteLoggerServerPort])
+ cls._remoteLoggerListener = threading.Thread(
+ name="RemoteLogger Listener", target=cls.RemoteLoggerListener, args=[cls._remoteLoggerServerPort]
+ )
cls._remoteLoggerListener.daemon = True
cls._remoteLoggerListener.start()
"""
Dnstap: Send query and responses packed in dnstap to a remotelogger server
"""
- name = 'query.dnstap.tests.powerdns.com.'
+ name = "query.dnstap.tests.powerdns.com."
- target = 'target.dnstap.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.dnstap.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
"""
DnstapExtra: Send query and responses packed in dnstap to a remotelogger server. Extra data is filled out.
"""
- name = 'extra.dnstap.tests.powerdns.com.'
+ name = "extra.dnstap.tests.powerdns.com."
- target = 'target.dnstap.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.dnstap.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
checkDnstapResponse(self, dnstap, dnstap_pb2.TCP, response)
checkDnstapExtra(self, dnstap, b"Type,Response")
+
class TestDnstapOverRemoteLoggerPool(DNSDistTest):
_remoteLoggerServerPort = pickAvailablePort()
_remoteLoggerQueue = Queue()
_remoteLoggerCounter = 0
_poolConnectionCount = 8
- _config_params = ['_testServerPort', '_remoteLoggerServerPort', '_poolConnectionCount']
+ _config_params = ["_testServerPort", "_remoteLoggerServerPort", "_poolConnectionCount"]
_config_template = """
extrasmn = newSuffixMatchNode()
extrasmn:add(newDNSName('extra.dnstap.tests.powerdns.com.'))
def startResponders(cls):
DNSDistTest.startResponders()
- cls._remoteLoggerListener = threading.Thread(name='RemoteLogger Listener', target=cls.RemoteLoggerListener, args=[cls._remoteLoggerServerPort])
+ cls._remoteLoggerListener = threading.Thread(
+ name="RemoteLogger Listener", target=cls.RemoteLoggerListener, args=[cls._remoteLoggerServerPort]
+ )
cls._remoteLoggerListener.daemon = True
cls._remoteLoggerListener.start()
"""
Dnstap: Send query and responses packed in dnstap to a remotelogger server
"""
- name = 'query.dnstap.tests.powerdns.com.'
+ name = "query.dnstap.tests.powerdns.com."
- target = 'target.dnstap.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.dnstap.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
"""
DnstapExtra: Send query and responses packed in dnstap to a remotelogger server. Extra data is filled out.
"""
- name = 'extra.dnstap.tests.powerdns.com.'
+ name = "extra.dnstap.tests.powerdns.com."
- target = 'target.dnstap.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.dnstap.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
checkDnstapExtra(self, dnstap, b"Type,Response")
-
def fstrm_get_control_frame_type(data):
(t,) = struct.unpack("!L", data[0:4])
return t
def fstrm_make_control_frame_reply(cft, data):
if cft == FSTRM_CONTROL_READY:
# Reply with ACCEPT frame and content-type
- contenttype = b'protobuf:dnstap.Dnstap'
- frame = struct.pack('!LLL', FSTRM_CONTROL_ACCEPT, 1,
- len(contenttype)) + contenttype
+ contenttype = b"protobuf:dnstap.Dnstap"
+ frame = struct.pack("!LLL", FSTRM_CONTROL_ACCEPT, 1, len(contenttype)) + contenttype
buf = struct.pack("!LL", 0, len(frame)) + frame
return buf
elif cft == FSTRM_CONTROL_START:
return None
else:
- raise Exception('unhandled control frame ' + cft)
+ raise Exception("unhandled control frame " + cft)
def fstrm_read_and_dispatch_control_frame(conn):
data = conn.recv(4)
if not data:
- raise Exception('length of control frame payload could not be read')
+ raise Exception("length of control frame payload could not be read")
(datalen,) = struct.unpack("!L", data)
data = conn.recv(datalen)
cft = fstrm_get_control_frame_type(data)
if exit_early:
break
+
class TestDnstapOverFrameStreamUnixLogger(DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
_doh3ServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
- _fstrmLoggerAddress = '/tmp/fslutest.sock'
+ _fstrmLoggerAddress = "/tmp/fslutest.sock"
_fstrmLoggerQueue = Queue()
_fstrmLoggerCounter = 0
- _config_params = ['_testServerPort', '_fstrmLoggerAddress', '_dohServerPort', '_serverCert', '_serverKey', '_doh3ServerPort', '_serverCert', '_serverKey']
+ _config_params = [
+ "_testServerPort",
+ "_fstrmLoggerAddress",
+ "_dohServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doh3ServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
_config_template = """
newServer{address="127.0.0.1:%d", useClientSubnet=true}
fslu = newFrameStreamUnixLogger('%s')
try:
while True:
(conn, _) = sock.accept()
- fstrm_handle_bidir_connection(conn, lambda data: \
- cls._fstrmLoggerQueue.put(data, True, timeout=2.0))
+ fstrm_handle_bidir_connection(conn, lambda data: cls._fstrmLoggerQueue.put(data, True, timeout=2.0))
conn.close()
finally:
sock.close()
def startResponders(cls):
DNSDistTest.startResponders()
- cls._fstrmLoggerListener = threading.Thread(name='FrameStreamUnixListener', target=cls.FrameStreamUnixListener, args=[cls._fstrmLoggerAddress])
+ cls._fstrmLoggerListener = threading.Thread(
+ name="FrameStreamUnixListener", target=cls.FrameStreamUnixListener, args=[cls._fstrmLoggerAddress]
+ )
cls._fstrmLoggerListener.daemon = True
cls._fstrmLoggerListener.start()
"""
Dnstap: Send query packed in dnstap to a unix socket fstrmlogger server
"""
- name = 'query.dnstap.tests.powerdns.com.'
+ name = "query.dnstap.tests.powerdns.com."
- target = 'target.dnstap.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.dnstap.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
"""
DOH and DOH3: Make sure http protocol field is correctly set
"""
- name = 'simple.doh.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple.doh.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
protocols = [
- {"method": "sendDOH3QueryWrapper", "port": self._doh3ServerPort, "expected_protocol": dnstap_pb2.HttpProtocol.HTTP3},
- {"method": "sendDOHQueryWrapper", "port": self._dohServerPort, "expected_protocol": dnstap_pb2.HttpProtocol.HTTP2},
+ {
+ "method": "sendDOH3QueryWrapper",
+ "port": self._doh3ServerPort,
+ "expected_protocol": dnstap_pb2.HttpProtocol.HTTP3,
+ },
+ {
+ "method": "sendDOHQueryWrapper",
+ "port": self._dohServerPort,
+ "expected_protocol": dnstap_pb2.HttpProtocol.HTTP2,
+ },
]
- for protocol in protocols :
+ for protocol in protocols:
sender = getattr(self, protocol["method"])
(receivedQuery, receivedResponse) = sender(query, response)
receivedQuery.id = query.id
# check the dnstap message corresponding to the UDP query
dnstap = self.getFirstDnstap()
- checkDnstapQuery(self, dnstap, dnstap_pb2.DOH, query, '127.0.0.1', protocol["port"], protocol["expected_protocol"])
+ checkDnstapQuery(
+ self, dnstap, dnstap_pb2.DOH, query, "127.0.0.1", protocol["port"], protocol["expected_protocol"]
+ )
checkDnstapNoExtra(self, dnstap)
+
class TestDnstapOverRemotePoolUnixLogger(DNSDistTest):
- _fstrmLoggerAddress = '/tmp/fslutest.sock'
+ _fstrmLoggerAddress = "/tmp/fslutest.sock"
_fstrmLoggerQueue = Queue()
_fstrmLoggerCounter = 0
_poolConnectionCount = 5
- _config_params = ['_testServerPort', '_fstrmLoggerAddress', '_poolConnectionCount']
+ _config_params = ["_testServerPort", "_fstrmLoggerAddress", "_poolConnectionCount"]
_config_template = """
newServer{address="127.0.0.1:%d", useClientSubnet=true}
fslu = newFrameStreamUnixLogger('%s', { connectionCount = %d })
sock.listen(100)
def handle_connection(conn):
- fstrm_handle_bidir_connection(conn, lambda data: \
- cls._fstrmLoggerQueue.put(data, True, timeout=2.0), exit_early=True)
+ fstrm_handle_bidir_connection(
+ conn, lambda data: cls._fstrmLoggerQueue.put(data, True, timeout=2.0), exit_early=True
+ )
conn.close()
threads = []
def startResponders(cls):
DNSDistTest.startResponders()
- cls._fstrmLoggerListener = threading.Thread(name='FrameStreamUnixListener', target=cls.FrameStreamUnixListener, args=[cls._fstrmLoggerAddress])
+ cls._fstrmLoggerListener = threading.Thread(
+ name="FrameStreamUnixListener", target=cls.FrameStreamUnixListener, args=[cls._fstrmLoggerAddress]
+ )
cls._fstrmLoggerListener.daemon = True
cls._fstrmLoggerListener.start()
Dnstap: Send query packed in dnstap to a unix socket fstrmlogger server
"""
for i in range(self._poolConnectionCount):
- name = 'query.dnstap.tests.powerdns.com.'
+ name = "query.dnstap.tests.powerdns.com."
- target = 'target.dnstap.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.dnstap.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
_fstrmLoggerPort = pickAvailablePort()
_fstrmLoggerQueue = Queue()
_fstrmLoggerCounter = 0
- _config_params = ['_testServerPort', '_fstrmLoggerPort']
+ _config_params = ["_testServerPort", "_fstrmLoggerPort"]
_config_template = """
newServer{address="127.0.0.1:%d", useClientSubnet=true}
fslu = newFrameStreamTcpLogger('127.0.0.1:%d')
try:
while True:
(conn, _) = sock.accept()
- fstrm_handle_bidir_connection(conn, lambda data: \
- cls._fstrmLoggerQueue.put(data, True, timeout=2.0))
+ fstrm_handle_bidir_connection(conn, lambda data: cls._fstrmLoggerQueue.put(data, True, timeout=2.0))
conn.close()
finally:
sock.close()
def startResponders(cls):
DNSDistTest.startResponders()
- cls._fstrmLoggerListener = threading.Thread(name='FrameStreamTcpListener', target=cls.FrameStreamTcpListener, args=[cls._fstrmLoggerPort])
+ cls._fstrmLoggerListener = threading.Thread(
+ name="FrameStreamTcpListener", target=cls.FrameStreamTcpListener, args=[cls._fstrmLoggerPort]
+ )
cls._fstrmLoggerListener.daemon = True
cls._fstrmLoggerListener.start()
"""
Dnstap: Send query packed in dnstap to a tcp socket fstrmlogger server
"""
- name = 'query.dnstap.tests.powerdns.com.'
+ name = "query.dnstap.tests.powerdns.com."
- target = 'target.dnstap.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.dnstap.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
checkDnstapQuery(self, dnstap, dnstap_pb2.UDP, query)
checkDnstapNoExtra(self, dnstap)
+
class TestDnstapOverFrameStreamTcpLoggerYAML(DNSDistTest):
_fstrmLoggerPort = pickAvailablePort()
_fstrmLoggerQueue = Queue()
_fstrmLoggerCounter = 0
_config_params = []
- _yaml_config_params = ['_dnsDistPort', '_testServerPort', '_fstrmLoggerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort", "_fstrmLoggerPort"]
_yaml_config_template = """
binds:
- listen_address: "127.0.0.1:%d"
try:
while True:
(conn, _) = sock.accept()
- fstrm_handle_bidir_connection(conn, lambda data: \
- cls._fstrmLoggerQueue.put(data, True, timeout=2.0))
+ fstrm_handle_bidir_connection(conn, lambda data: cls._fstrmLoggerQueue.put(data, True, timeout=2.0))
conn.close()
finally:
sock.close()
def startResponders(cls):
DNSDistTest.startResponders()
- cls._fstrmLoggerListener = threading.Thread(name='FrameStreamTcpListener', target=cls.FrameStreamTcpListener, args=[cls._fstrmLoggerPort])
+ cls._fstrmLoggerListener = threading.Thread(
+ name="FrameStreamTcpListener", target=cls.FrameStreamTcpListener, args=[cls._fstrmLoggerPort]
+ )
cls._fstrmLoggerListener.daemon = True
cls._fstrmLoggerListener.start()
"""
Dnstap: Send query packed in dnstap to a tcp socket fstrmlogger server
"""
- name = 'query.dnstap-yaml.tests.powerdns.com.'
+ name = "query.dnstap-yaml.tests.powerdns.com."
- target = 'target.dnstap-yaml.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.dnstap-yaml.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
checkDnstapQuery(self, dnstap, dnstap_pb2.UDP, query)
checkDnstapNoExtra(self, dnstap)
+
class TestDnstapOverRemotePoolTcpLogger(DNSDistTest):
_fstrmLoggerPort = pickAvailablePort()
_fstrmLoggerQueue = Queue()
_fstrmLoggerCounter = 0
_poolConnectionCount = 5
- _config_params = ['_testServerPort', '_fstrmLoggerPort', '_poolConnectionCount']
+ _config_params = ["_testServerPort", "_fstrmLoggerPort", "_poolConnectionCount"]
_config_template = """
newServer{address="127.0.0.1:%d", useClientSubnet=true}
fslu = newFrameStreamTcpLogger('127.0.0.1:%d', { connectionCount = %d })
sock.listen(100)
def handle_connection(conn):
- fstrm_handle_bidir_connection(conn, lambda data: \
- cls._fstrmLoggerQueue.put(data, True, timeout=2.0), exit_early=True)
+ fstrm_handle_bidir_connection(
+ conn, lambda data: cls._fstrmLoggerQueue.put(data, True, timeout=2.0), exit_early=True
+ )
conn.close()
threads = []
def startResponders(cls):
DNSDistTest.startResponders()
- cls._fstrmLoggerListener = threading.Thread(name='FrameStreamTcpListener', target=cls.FrameStreamTcpListener, args=[cls._fstrmLoggerPort])
+ cls._fstrmLoggerListener = threading.Thread(
+ name="FrameStreamTcpListener", target=cls.FrameStreamTcpListener, args=[cls._fstrmLoggerPort]
+ )
cls._fstrmLoggerListener.daemon = True
cls._fstrmLoggerListener.start()
Dnstap: Send query packed in dnstap to a tcp socket fstrmlogger server
"""
for i in range(self._poolConnectionCount):
- name = 'query.dnstap.tests.powerdns.com.'
+ name = "query.dnstap.tests.powerdns.com."
- target = 'target.dnstap.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.dnstap.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
from dnsdisttests import DNSDistTest
from dnsdistDynBlockTests import DynBlocksTest, waitForMaintenanceToRun, _maintenanceWaitTime
-class TestDynBlockQPS(DynBlocksTest):
+class TestDynBlockQPS(DynBlocksTest):
_config_template = """
function maintenance()
addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d)
webserver("127.0.0.1:%d")
setWebserverConfig({password="%s", apiKey="%s"})
"""
- _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_dynBlockQPS",
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
def testDynBlocksQRate(self):
"""
Dyn Blocks: QRate
"""
- name = 'qrate.dynblocks.tests.powerdns.com.'
+ name = "qrate.dynblocks.tests.powerdns.com."
self.doTestQRate(name)
-class TestDynBlockQPSRefused(DynBlocksTest):
+class TestDynBlockQPSRefused(DynBlocksTest):
_config_template = """
function maintenance()
addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d)
"""
Dyn Blocks: QRate refused
"""
- name = 'qraterefused.dynblocks.tests.powerdns.com.'
+ name = "qraterefused.dynblocks.tests.powerdns.com."
self.doTestQRateRCode(name, dns.rcode.REFUSED)
-class TestDynBlockQPSActionRefused(DynBlocksTest):
+class TestDynBlockQPSActionRefused(DynBlocksTest):
_config_template = """
function maintenance()
addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Refused)
"""
Dyn Blocks: QRate refused (action)
"""
- name = 'qrateactionrefused.dynblocks.tests.powerdns.com.'
+ name = "qrateactionrefused.dynblocks.tests.powerdns.com."
self.doTestQRateRCode(name, dns.rcode.REFUSED)
-class TestDynBlockQPSActionNXD(DynBlocksTest):
+class TestDynBlockQPSActionNXD(DynBlocksTest):
_config_template = """
function maintenance()
addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Nxdomain)
"""
Dyn Blocks: QRate NXD (action)
"""
- name = 'qrateactionnxd.dynblocks.tests.powerdns.com.'
+ name = "qrateactionnxd.dynblocks.tests.powerdns.com."
self.doTestQRateRCode(name, dns.rcode.NXDOMAIN)
-class TestDynBlockQPSActionTruncated(DNSDistTest):
+class TestDynBlockQPSActionTruncated(DNSDistTest):
_dynBlockQPS = 10
_dynBlockPeriod = 2
# this needs to be greater than maintenanceWaitTime
_dynBlockDuration = _maintenanceWaitTime + 1
- _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _config_params = ["_dynBlockQPS", "_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
_config_template = """
function maintenance()
addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Truncate)
"""
Dyn Blocks: QRate truncated (action)
"""
- name = 'qrateactiontruncated.dynblocks.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "qrateactiontruncated.dynblocks.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
truncatedResponse = dns.message.make_response(query)
truncatedResponse.flags |= dns.flags.TC
self.assertEqual(allowed, sent)
-class TestDynBlockAllowlist(DynBlocksTest):
+class TestDynBlockAllowlist(DynBlocksTest):
_config_template = """
allowlisted = false
function maintenance()
"""
Dyn Blocks: Allowlisted from the dynamic blocks
"""
- name = 'allowlisted.dynblocks.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "allowlisted.dynblocks.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
allowed = 0
self.assertEqual(receivedResponse, receivedResponse)
# check that we would have been blocked without the allowlisting
- name = 'allowlisted-test.dynblocks.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "allowlisted-test.dynblocks.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.42')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.42")
expectedResponse.answer.append(rrset)
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
import unittest
from dnsdistDynBlockTests import DynBlocksTest
-@unittest.skipUnless('ENABLE_SUDO_TESTS' in os.environ, "sudo is not available")
-class TestDynBlockEBPFQPS(DynBlocksTest):
+@unittest.skipUnless("ENABLE_SUDO_TESTS" in os.environ, "sudo is not available")
+class TestDynBlockEBPFQPS(DynBlocksTest):
_config_template = """
bpf = newBPFFilter({ipv4MaxItems=10, ipv6MaxItems=10, qnamesMaxItems=10})
setDefaultBPFFilter(bpf)
webserver("127.0.0.1:%d")
setWebserverConfig({password="%s", apiKey="%s"})
"""
- _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_dynBlockQPS",
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_sudoMode = True
def testDynBlocksQRate(self):
"""
Dyn Blocks: QRate
"""
- name = 'qrate.dynblocks.tests.powerdns.com.'
+ name = "qrate.dynblocks.tests.powerdns.com."
self.doTestQRate(name, ebpf=True)
from dnsdisttests import DNSDistTest
from dnsdistDynBlockTests import DynBlocksTest, waitForMaintenanceToRun, _maintenanceWaitTime
-class TestDynBlockGroupQPS(DynBlocksTest):
+class TestDynBlockGroupQPS(DynBlocksTest):
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
webserver("127.0.0.1:%s")
setWebserverConfig({password="%s", apiKey="%s"})
"""
- _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_dynBlockQPS",
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
def testDynBlocksQRate(self):
"""
Dyn Blocks (Group): QRate
"""
- name = 'qrate.group.dynblocks.tests.powerdns.com.'
+ name = "qrate.group.dynblocks.tests.powerdns.com."
self.doTestQRate(name)
-class TestDynBlockGroupQTypeRate(DynBlocksTest):
+class TestDynBlockGroupQTypeRate(DynBlocksTest):
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setQTypeRate(DNSQType.ANY, %d, %d, "Exceeded qtype rate", %d)
setDynBlocksAction(DNSAction.Refused)
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_dynBlockANYQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _config_params = ["_dynBlockANYQPS", "_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
def testDynBlocksQTypeRate(self):
"""
Dyn Blocks (Group): QType Rate
"""
- name = 'qtype-rate.group.dynblocks.tests.powerdns.com.'
+ name = "qtype-rate.group.dynblocks.tests.powerdns.com."
self.doTestQTypeRate(name)
-class TestDynBlockGroupQTypeRateYAML(DynBlocksTest):
+class TestDynBlockGroupQTypeRateYAML(DynBlocksTest):
_yaml_config_template = """---
dynamic_rules:
- name: "Block client generating too many ANY queries"
protocol: Do53
"""
_config_params = []
- _yaml_config_params = ['_dynBlockANYQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _yaml_config_params = ["_dynBlockANYQPS", "_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
def testDynBlocksQTypeRate(self):
"""
Dyn Blocks (Group / YAML): QType Rate
"""
- name = 'qtype-rate-yaml.group.dynblocks.tests.powerdns.com.'
+ name = "qtype-rate-yaml.group.dynblocks.tests.powerdns.com."
self.doTestQTypeRate(name)
-class TestDynBlockGroupQPSRefused(DynBlocksTest):
+class TestDynBlockGroupQPSRefused(DynBlocksTest):
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
"""
Dyn Blocks (Group): QRate refused
"""
- name = 'qraterefused.group.dynblocks.tests.powerdns.com.'
+ name = "qraterefused.group.dynblocks.tests.powerdns.com."
self.doTestQRateRCode(name, dns.rcode.REFUSED)
-class TestDynBlockGroupQPSActionRefused(DynBlocksTest):
+class TestDynBlockGroupQPSActionRefused(DynBlocksTest):
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.Refused)
"""
Dyn Blocks (group): QRate refused (action)
"""
- name = 'qrateactionrefused.group.dynblocks.tests.powerdns.com.'
+ name = "qrateactionrefused.group.dynblocks.tests.powerdns.com."
self.doTestQRateRCode(name, dns.rcode.REFUSED)
-class TestDynBlockGroupExcluded(DynBlocksTest):
+class TestDynBlockGroupExcluded(DynBlocksTest):
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
"""
Dyn Blocks (group) : Excluded from the dynamic block rules
"""
- name = 'excluded.group.dynblocks.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "excluded.group.dynblocks.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
allowed = 0
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, receivedResponse)
-class TestDynBlockGroupExcludedViaNMG(DynBlocksTest):
+class TestDynBlockGroupExcludedViaNMG(DynBlocksTest):
_config_template = """
local nmg = newNMG()
nmg:addMask("127.0.0.1/32")
"""
Dyn Blocks (group) : Excluded (via NMG) from the dynamic block rules
"""
- name = 'excluded-nmg.group.dynblocks.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "excluded-nmg.group.dynblocks.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
allowed = 0
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, receivedResponse)
-class TestDynBlockGroupExcludedRange(DynBlocksTest):
+class TestDynBlockGroupExcludedRange(DynBlocksTest):
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
"""
Dyn Blocks (group) : Excluded from the dynamic block rules (range)
"""
- name = 'excluded.group.dynblocks.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "excluded.group.dynblocks.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
allowed = 0
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, receivedResponse)
-class TestDynBlockGroupNoOp(DynBlocksTest):
+class TestDynBlockGroupNoOp(DynBlocksTest):
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.NoOp)
webserver("127.0.0.1:%d")
setWebserverConfig({password="%s", apiKey="%s"})
"""
- _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_dynBlockQPS",
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
def testNoOp(self):
"""
Dyn Blocks (group) : NoOp
"""
- name = 'noop.group.dynblocks.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "noop.group.dynblocks.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
allowed = 0
self.assertEqual(receivedResponse, receivedResponse)
# check that the rule has been inserted
- self.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', 1, self._dynBlockDuration, 0, sent)
+ self.doTestDynBlockViaAPI("127.0.0.1/32", "Exceeded query rate", 1, self._dynBlockDuration, 0, sent)
-class TestDynBlockGroupWarning(DynBlocksTest):
+class TestDynBlockGroupWarning(DynBlocksTest):
_dynBlockWarningQPS = 5
_dynBlockQPS = 20
_config_template = """
webserver("127.0.0.1:%d")
setWebserverConfig({password="%s", apiKey="%s"})
"""
- _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_dynBlockWarningQPS', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_dynBlockQPS",
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_dynBlockWarningQPS",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
def testWarning(self):
"""
Dyn Blocks (group) : Warning
"""
- name = 'warning.group.dynblocks.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "warning.group.dynblocks.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
allowed = 0
self.assertEqual(receivedResponse, receivedResponse)
# check that the rule has been inserted
- self.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', 1, self._dynBlockDuration, 0, sent)
+ self.doTestDynBlockViaAPI("127.0.0.1/32", "Exceeded query rate", 1, self._dynBlockDuration, 0, sent)
self.doTestQRate(name)
-class TestDynBlockGroupPort(DNSDistTest):
+class TestDynBlockGroupPort(DNSDistTest):
_dynBlockQPS = 20
_dynBlockPeriod = 2
# this needs to be greater than maintenanceWaitTime
end
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _config_params = ["_dynBlockQPS", "_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
def testPort(self):
"""
Dyn Blocks (group): Exact port matching
"""
- name = 'port.group.dynblocks.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "port.group.dynblocks.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
allowed = 0
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-class TestDynBlockGroupQSuffixMatchYAML(DynBlocksTest):
+class TestDynBlockGroupQSuffixMatchYAML(DynBlocksTest):
_yaml_config_template = """---
dynamic_rules:
- name: "Check Suffix Match visitor from YAML"
protocol: Do53
"""
_config_params = []
- _yaml_config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _yaml_config_params = ["_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
def testSuffixMatchVisitorCalled(self):
"""
Dyn Blocks (Group / YAML): Visitor called
"""
- name = 'check-visitor.group.dynblocks.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "check-visitor.group.dynblocks.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NOERROR)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
method = "sendUDPQuery"
from dnsdisttests import pickAvailablePort
from proxyprotocol import ProxyProtocol
-class TestDynBlockGroupServFailsRatio(DynBlocksTest):
+class TestDynBlockGroupServFailsRatio(DynBlocksTest):
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 20 queries count!
_dynBlockPeriod = 6
- _config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _config_params = ["_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setRCodeRatio(DNSRCode.SERVFAIL, 0.2, %d, "Exceeded rcode ratio", %d, 20)
"""
Dyn Blocks (group): Server Failure Ratio
"""
- name = 'servfailratio.group.dynblocks.tests.powerdns.com.'
+ name = "servfailratio.group.dynblocks.tests.powerdns.com."
self.doTestRCodeRatio(name, dns.rcode.SERVFAIL, 10, 10)
-class TestDynBlockGroupServFailsRatioYaml(DynBlocksTest):
+class TestDynBlockGroupServFailsRatioYaml(DynBlocksTest):
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 40 queries count!
_dynBlockPeriod = 6
protocol: Do53
"""
_config_params = []
- _yaml_config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _yaml_config_params = ["_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
def testDynBlocksServFailRatio(self):
"""
Dyn Blocks (group / YAML): Server Failure Ratio
"""
- name = 'servfailratio-yaml.group.dynblocks.tests.powerdns.com.'
+ name = "servfailratio-yaml.group.dynblocks.tests.powerdns.com."
# we need more queries because of the sampling rate!
self.doTestRCodeRatio(name, dns.rcode.SERVFAIL, 20, 20)
+
class TestDynBlockGroupServFailsRatioDoH(DynBlocksTest):
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 20 queries count!
_dynBlockPeriod = 6
_dnsDistListeningAddr = "127.0.0.2"
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKey = 'apisecret'
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
+ _webServerBasicAuthPassword = "secret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKey = "apisecret"
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setRCodeRatio(DNSRCode.SERVFAIL, 0.2, %d, "Exceeded query rate", %d, 20)
webserver("127.0.0.1:%d")
setWebserverConfig({password="%s", apiKey="%s"})
"""
- _config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_dnsDistListeningAddr', '_dohServerPort', '_serverCert', '_serverKey', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_dnsDistListeningAddr",
+ "_dohServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
def testDynBlocksServFailRatio(self):
"""
Dyn Blocks (group): Server Failure Ratio via DoH
"""
- name = 'rcode-servfailratio-doh.group.dynblocks.tests.powerdns.com.'
- rcodeQuery = dns.message.make_query(name, 'A', 'IN')
+ name = "rcode-servfailratio-doh.group.dynblocks.tests.powerdns.com."
+ rcodeQuery = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(rcodeQuery)
expectedResponse.set_rcode(dns.rcode.SERVFAIL)
sent = 0
allowed = 0
for _ in range(rcodecount):
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, rcodeQuery, response=expectedResponse, caFile=self._caCert, customHeaders=['x-forwarded-for: 192.0.2.1'])
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ rcodeQuery,
+ response=expectedResponse,
+ caFile=self._caCert,
+ customHeaders=["x-forwarded-for: 192.0.2.1"],
+ )
sent = sent + 1
if receivedQuery:
waitForMaintenanceToRun()
# we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, rcodeQuery, response=None, caFile=self._caCert, customHeaders=['x-forwarded-for: 192.0.2.1'], useQueue=False, timeout=1)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ rcodeQuery,
+ response=None,
+ caFile=self._caCert,
+ customHeaders=["x-forwarded-for: 192.0.2.1"],
+ useQueue=False,
+ timeout=1,
+ )
self.assertEqual(receivedResponse, None)
- self.doTestDynBlockViaAPI('192.0.2.1/32', 'Exceeded query rate', 1, self._dynBlockDuration, (sent-allowed)+1, (sent-allowed)+1, False)
+ self.doTestDynBlockViaAPI(
+ "192.0.2.1/32",
+ "Exceeded query rate",
+ 1,
+ self._dynBlockDuration,
+ (sent - allowed) + 1,
+ (sent - allowed) + 1,
+ False,
+ )
+
class TestDynBlockGroupServFailsRatioDoHCacheHit(DynBlocksTest):
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 20 queries count!
_dynBlockPeriod = 6
_dnsDistListeningAddr = "127.0.0.2"
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKey = 'apisecret'
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
+ _webServerBasicAuthPassword = "secret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKey = "apisecret"
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setRCodeRatio(DNSRCode.SERVFAIL, 0.2, %d, "Exceeded query rate", %d, 20)
webserver("127.0.0.1:%d")
setWebserverConfig({password="%s", apiKey="%s"})
"""
- _config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_dnsDistListeningAddr', '_dohServerPort', '_serverCert', '_serverKey', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_dnsDistListeningAddr",
+ "_dohServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
def testDynBlocksServFailRatio(self):
"""
Dyn Blocks (group): Server Failure Ratio via DoH (cache hits)
"""
- name = 'rcode-servfailratio-doh-cache-hits.group.dynblocks.tests.powerdns.com.'
- rcodeQuery = dns.message.make_query(name, 'A', 'IN')
+ name = "rcode-servfailratio-doh-cache-hits.group.dynblocks.tests.powerdns.com."
+ rcodeQuery = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(rcodeQuery)
expectedResponse.set_rcode(dns.rcode.SERVFAIL)
firstQuery = True
for _ in range(rcodecount):
if firstQuery:
- (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, rcodeQuery, response=expectedResponse, caFile=self._caCert, customHeaders=['x-forwarded-for: 192.0.2.1'])
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ rcodeQuery,
+ response=expectedResponse,
+ caFile=self._caCert,
+ customHeaders=["x-forwarded-for: 192.0.2.1"],
+ )
firstQuery = False
else:
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, rcodeQuery, response=None, caFile=self._caCert, customHeaders=['x-forwarded-for: 192.0.2.1'], useQueue=False)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ rcodeQuery,
+ response=None,
+ caFile=self._caCert,
+ customHeaders=["x-forwarded-for: 192.0.2.1"],
+ useQueue=False,
+ )
sent = sent + 1
if receivedQuery:
waitForMaintenanceToRun()
# we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
- (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, rcodeQuery, response=None, caFile=self._caCert, customHeaders=['x-forwarded-for: 192.0.2.1'], useQueue=False, timeout=1)
+ (_, receivedResponse) = self.sendDOHQuery(
+ self._dohServerPort,
+ self._serverName,
+ self._dohBaseURL,
+ rcodeQuery,
+ response=None,
+ caFile=self._caCert,
+ customHeaders=["x-forwarded-for: 192.0.2.1"],
+ useQueue=False,
+ timeout=1,
+ )
self.assertEqual(receivedResponse, None)
- self.doTestDynBlockViaAPI('192.0.2.1/32', 'Exceeded query rate', 1, self._dynBlockDuration, (sent-allowed)+1, (sent-allowed)+1, False)
+ self.doTestDynBlockViaAPI(
+ "192.0.2.1/32",
+ "Exceeded query rate",
+ 1,
+ self._dynBlockDuration,
+ (sent - allowed) + 1,
+ (sent - allowed) + 1,
+ False,
+ )
-class TestDynBlockGroupServFailsRatioDoQ(DynBlocksTest):
+class TestDynBlockGroupServFailsRatioDoQ(DynBlocksTest):
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 20 queries count!
_dynBlockPeriod = 6
_dnsDistListeningAddr = "127.0.0.2"
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_config_template = """
local dbr = dynBlockRulesGroup()
addDOQLocal("%s:%d", "%s", "%s")
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_dnsDistListeningAddr', '_doqServerPort', '_serverCert', '_serverKey', '_testServerPort']
+ _config_params = [
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_dnsDistListeningAddr",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_testServerPort",
+ ]
def testDynBlocksServFailRatio(self):
"""
Dyn Blocks (group): Server Failure Ratio via DoQ
"""
- name = 'servfailratio-doq.group.dynblocks.tests.powerdns.com.'
+ name = "servfailratio-doq.group.dynblocks.tests.powerdns.com."
self.doTestRCodeRatioViaProtocol(name, dns.rcode.SERVFAIL, 10, 10, "sendDOQQueryWrapper")
-class TestDynBlockGroupServFailsRatioDoQCacheHit(DynBlocksTest):
+class TestDynBlockGroupServFailsRatioDoQCacheHit(DynBlocksTest):
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 20 queries count!
_dynBlockPeriod = 6
_dnsDistListeningAddr = "127.0.0.2"
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_config_template = """
local dbr = dynBlockRulesGroup()
addDOQLocal("%s:%d", "%s", "%s")
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_dnsDistListeningAddr', '_doqServerPort', '_serverCert', '_serverKey', '_testServerPort']
+ _config_params = [
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_dnsDistListeningAddr",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_testServerPort",
+ ]
def testDynBlocksServFailRatio(self):
"""
Dyn Blocks (group): Server Failure Ratio via DoQ (cache hits)
"""
- name = 'servfailratio-doq-hits.group.dynblocks.tests.powerdns.com.'
+ name = "servfailratio-doq-hits.group.dynblocks.tests.powerdns.com."
self.doTestRCodeRatioViaProtocol(name, dns.rcode.SERVFAIL, 10, 10, "sendDOQQueryWrapper", cached=True)
-class TestDynBlockGroupServFailsRatioDoH3(DynBlocksTest):
+class TestDynBlockGroupServFailsRatioDoH3(DynBlocksTest):
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 20 queries count!
_dynBlockPeriod = 6
_dnsDistListeningAddr = "127.0.0.2"
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doh3ServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doh3ServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doh3ServerPort)
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setRCodeRatio(DNSRCode.SERVFAIL, 0.2, %d, "Exceeded query rate", %d, 20)
addDOH3Local("%s:%d", "%s", "%s")
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_dnsDistListeningAddr', '_doh3ServerPort', '_serverCert', '_serverKey', '_testServerPort']
+ _config_params = [
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_dnsDistListeningAddr",
+ "_doh3ServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_testServerPort",
+ ]
def testDynBlocksServFailRatio(self):
"""
Dyn Blocks (group): Server Failure Ratio via DoH3
"""
- name = 'servfailratio-doh3.group.dynblocks.tests.powerdns.com.'
+ name = "servfailratio-doh3.group.dynblocks.tests.powerdns.com."
self.doTestRCodeRatioViaProtocol(name, dns.rcode.SERVFAIL, 10, 10, "sendDOH3QueryWrapper")
-class TestDynBlockGroupServFailsRatioDoH3CacheHit(DynBlocksTest):
+class TestDynBlockGroupServFailsRatioDoH3CacheHit(DynBlocksTest):
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 20 queries count!
_dynBlockPeriod = 6
_dnsDistListeningAddr = "127.0.0.2"
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doh3ServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doh3ServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doh3ServerPort)
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setRCodeRatio(DNSRCode.SERVFAIL, 0.2, %d, "Exceeded query rate", %d, 20)
addDOH3Local("%s:%d", "%s", "%s")
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_dnsDistListeningAddr', '_doh3ServerPort', '_serverCert', '_serverKey', '_testServerPort']
+ _config_params = [
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_dnsDistListeningAddr",
+ "_doh3ServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_testServerPort",
+ ]
def testDynBlocksServFailRatio(self):
"""
Dyn Blocks (group): Server Failure Ratio via DoH3 (cache hits)
"""
- name = 'servfailratio-doh3-hits.group.dynblocks.tests.powerdns.com.'
+ name = "servfailratio-doh3-hits.group.dynblocks.tests.powerdns.com."
self.doTestRCodeRatioViaProtocol(name, dns.rcode.SERVFAIL, 10, 10, "sendDOH3QueryWrapper", cached=True)
-class TestDynBlockGroupAllowedRCodesRatioYaml(DynBlocksTest):
+class TestDynBlockGroupAllowedRCodesRatioYaml(DynBlocksTest):
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 40 queries count!
_dynBlockPeriod = 6
protocol: Do53
"""
_config_params = []
- _yaml_config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _yaml_config_params = ["_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
def testDynBlocksAllowedRCodesRatio(self):
"""
Dyn Blocks (group / YAML): Allowed rcodes ratio
"""
- name = 'allowed-rcodes-ratio-yaml.group.dynblocks.tests.powerdns.com.'
+ name = "allowed-rcodes-ratio-yaml.group.dynblocks.tests.powerdns.com."
# we need more queries because of the sampling rate!
self.doTestRCodeRatio(name, dns.rcode.SERVFAIL, 20, 20)
class TestDynBlockGroupCacheMissRatio(DynBlocksTest):
-
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 20 queries count!
_dynBlockPeriod = 6
- _config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _config_params = ["_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setCacheMissRatio(0.8, %d, "Exceeded cache miss ratio", %d, 20, 0.0)
"""
Dyn Blocks (group): Cache miss ratio
"""
- name = 'cachemissratio.group.dynblocks.tests.powerdns.com.'
+ name = "cachemissratio.group.dynblocks.tests.powerdns.com."
self.doTestCacheMissRatio(name, 3, 17)
-class TestDynBlockGroupCacheMissRatioSetTag(DynBlocksTest):
+class TestDynBlockGroupCacheMissRatioSetTag(DynBlocksTest):
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 20 queries count!
_dynBlockPeriod = 6
- _config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _config_params = ["_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setCacheMissRatio(0.8, %d, "Exceeded cache miss ratio", %d, 20, 0.0, DNSAction.SetTag, 0.0, { tagName='dyn-miss-ratio', tagValue='hit' })
"""
Dyn Blocks (group): Cache miss ratio with SetTag
"""
- name = 'cachemissratio-settag.group.dynblocks.tests.powerdns.com.'
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ name = "cachemissratio-settag.group.dynblocks.tests.powerdns.com."
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
cacheHits = 3
cacheMisses = 17
for idx in range(cacheMisses):
- query = dns.message.make_query(str(idx) + '.' + name, 'A', 'IN')
+ query = dns.message.make_query(str(idx) + "." + name, "A", "IN")
response = dns.message.make_response(query)
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
# let's clear the response queue
self.clearToResponderQueue()
- query = dns.message.make_query('0.' + name, 'A', 'IN')
+ query = dns.message.make_query("0." + name, "A", "IN")
response = dns.message.make_response(query)
response.answer.append(rrset)
for _ in range(cacheHits):
# we should now get REFUSED for cache misses for up to self._dynBlockDuration + self._dynBlockPeriod
# cache miss
- query = dns.message.make_query(str(cacheMisses + 1) + '.' + name, 'A', 'IN')
+ query = dns.message.make_query(str(cacheMisses + 1) + "." + name, "A", "IN")
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
self.assertEqual(receivedResponse, expectedResponse)
# but a cache hit should be OK
- query = dns.message.make_query('0.' + name, 'A', 'IN')
+ query = dns.message.make_query("0." + name, "A", "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False, timeout=0.5)
# this specific query will match the query rules before triggering a cache miss
# so we can check that the tag is correctly set for query rules as well
- query = dns.message.make_query('test-query-rules.' + name, 'A', 'IN')
+ query = dns.message.make_query("test-query-rules." + name, "A", "IN")
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- queryRulesRRset = dns.rrset.from_text('test-query-rules.' + name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.2')
+ queryRulesRRset = dns.rrset.from_text(
+ "test-query-rules." + name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2"
+ )
expectedResponse.answer.append(queryRulesRRset)
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False, timeout=0.5)
self.assertEqual(receivedResponse, expectedResponse)
time.sleep(self._dynBlockDuration + self._dynBlockPeriod)
# this one should succeed
- query = dns.message.make_query(str(cacheMisses + 2) + '.' + name, 'A', 'IN')
+ query = dns.message.make_query(str(cacheMisses + 2) + "." + name, "A", "IN")
response = dns.message.make_response(query)
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
+
class TestDynBlockGroupServFailsRatioProxyProtocol(DynBlocksTest):
# we need this period to be quite long because we request the valid
# queries to be still looked at to reach the 20 queries count!
_dynBlockPeriod = 6
_dnsDistListeningAddr = "127.0.0.2"
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKey = 'apisecret'
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
+ _webServerBasicAuthPassword = "secret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKey = "apisecret"
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setRCodeRatio(DNSRCode.SERVFAIL, 0.2, %d, "Exceeded query rate", %d, 20)
webserver("127.0.0.1:%d")
setWebserverConfig({password="%s", apiKey="%s"})
"""
- _config_params = ['_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
def testDynBlocksServFailRatio(self):
"""
Dyn Blocks (group): Server Failure Ratio with incoming proxy protocol
"""
- name = 'rcode-servfailratio-incoming-proxyprotocol.group.dynblocks.tests.powerdns.com.'
- rcodeQuery = dns.message.make_query(name, 'A', 'IN')
+ name = "rcode-servfailratio-incoming-proxyprotocol.group.dynblocks.tests.powerdns.com."
+ rcodeQuery = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(rcodeQuery)
expectedResponse.set_rcode(dns.rcode.SERVFAIL)
udpPayload = ProxyProtocol.getPayload(False, False, True, srcAddr, destAddr, srcPort, destPort, [])
for _ in range(rcodecount):
- (receivedQuery, receivedResponse) = self.sendUDPQuery(udpPayload + rcodeQuery.to_wire(), response=expectedResponse, rawQuery=True)
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(
+ udpPayload + rcodeQuery.to_wire(), response=expectedResponse, rawQuery=True
+ )
sent = sent + 1
if receivedQuery:
waitForMaintenanceToRun()
# we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
- (_, receivedResponse) = self.sendUDPQuery(udpPayload + rcodeQuery.to_wire(), response=None, useQueue=False, rawQuery=True)
+ (_, receivedResponse) = self.sendUDPQuery(
+ udpPayload + rcodeQuery.to_wire(), response=None, useQueue=False, rawQuery=True
+ )
self.assertEqual(receivedResponse, None)
- self.doTestDynBlockViaAPI(f'{srcAddr}/128', 'Exceeded query rate', 1, self._dynBlockDuration, (sent-allowed)+1, (sent-allowed)+1, False)
+ self.doTestDynBlockViaAPI(
+ f"{srcAddr}/128",
+ "Exceeded query rate",
+ 1,
+ self._dynBlockDuration,
+ (sent - allowed) + 1,
+ (sent - allowed) + 1,
+ False,
+ )
# TCP now (with different addresses!)
sent = 0
tcpPayload = ProxyProtocol.getPayload(False, True, False, srcAddr, destAddr, srcPort, destPort, [])
for _ in range(rcodecount):
- (receivedQuery, receivedResponse) = self.sendTCPQuery(rcodeQuery, response=expectedResponse, prependPayload=tcpPayload)
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(
+ rcodeQuery, response=expectedResponse, prependPayload=tcpPayload
+ )
sent = sent + 1
if receivedQuery:
(_, receivedResponse) = self.sendTCPQuery(rcodeQuery, response=None, useQueue=False, prependPayload=tcpPayload)
self.assertEqual(receivedResponse, None)
- self.doTestDynBlockViaAPI(f'{srcAddr}/32', 'Exceeded query rate', 1, self._dynBlockDuration, (sent-allowed)+1, (sent-allowed)+1, False)
+ self.doTestDynBlockViaAPI(
+ f"{srcAddr}/32",
+ "Exceeded query rate",
+ 1,
+ self._dynBlockDuration,
+ (sent - allowed) + 1,
+ (sent - allowed) + 1,
+ False,
+ )
from dnsdisttests import DNSDistTest
from dnsdistDynBlockTests import DynBlocksTest
-class TestDynBlockResponseBytes(DynBlocksTest):
+class TestDynBlockResponseBytes(DynBlocksTest):
_dynBlockBytesPerSecond = 200
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_dynBlockBytesPerSecond', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_dynBlockBytesPerSecond",
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_testServerPort",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
"""
Dyn Blocks: Response Byte Rate
"""
- name = 'responsebyterate.dynblocks.tests.powerdns.com.'
+ name = "responsebyterate.dynblocks.tests.powerdns.com."
self.doTestResponseByteRate(name, self._dynBlockBytesPerSecond)
-class TestDynBlockGroupResponseBytes(DynBlocksTest):
+class TestDynBlockGroupResponseBytes(DynBlocksTest):
_dynBlockBytesPerSecond = 200
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_dynBlockBytesPerSecond', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_dynBlockBytesPerSecond",
+ "_dynBlockPeriod",
+ "_dynBlockDuration",
+ "_testServerPort",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
"""
Dyn Blocks (group) : Response Byte Rate
"""
- name = 'responsebyterate.group.dynblocks.tests.powerdns.com.'
+ name = "responsebyterate.group.dynblocks.tests.powerdns.com."
self.doTestResponseByteRate(name, self._dynBlockBytesPerSecond)
import dns
from dnsdistDynBlockTests import DynBlocksTest, waitForMaintenanceToRun
-class TestDynBlockServFails(DynBlocksTest):
+class TestDynBlockServFails(DynBlocksTest):
_config_template = """
function maintenance()
addDynBlocks(exceedServFails(%d, %d), "Exceeded servfail rate", %d)
"""
Dyn Blocks: Server Failure Rate
"""
- name = 'servfailrate.dynblocks.tests.powerdns.com.'
+ name = "servfailrate.dynblocks.tests.powerdns.com."
self.doTestRCodeRate(name, dns.rcode.SERVFAIL)
-class TestDynBlockServFailsCached(DynBlocksTest):
+class TestDynBlockServFailsCached(DynBlocksTest):
_config_template = """
pc = newPacketCache(10000, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false})
getPool(""):setCache(pc)
"""
Dyn Blocks: Make sure cache hit responses also gets inserted into rings
"""
- name = 'servfailrate.dynblocks.tests.powerdns.com.'
+ name = "servfailrate.dynblocks.tests.powerdns.com."
rcode = dns.rcode.SERVFAIL
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(rcode)
-
for method in ("sendUDPQuery", "sendTCPQuery"):
print(method, "()")
sender = getattr(self, method)
(receivedQuery, receivedResponse) = sender(query, response=None)
self.assertEqual(expectedResponse, receivedResponse)
-class TestDynBlockGroupServFails(DynBlocksTest):
+class TestDynBlockGroupServFails(DynBlocksTest):
_config_template = """
local dbr = dynBlockRulesGroup()
dbr:setRCodeRate(DNSRCode.SERVFAIL, %d, %d, "Exceeded query rate", %d)
"""
Dyn Blocks (group): Server Failure Rate
"""
- name = 'servfailrate.group.dynblocks.tests.powerdns.com.'
+ name = "servfailrate.group.dynblocks.tests.powerdns.com."
self.doTestRCodeRate(name, dns.rcode.SERVFAIL)
-class TestDynBlockGroupServFailsYAML(DynBlocksTest):
+class TestDynBlockGroupServFailsYAML(DynBlocksTest):
_yaml_config_template = """---
dynamic_rules:
- name: "Block client generating too many ServFails"
protocol: Do53
"""
_config_params = []
- _yaml_config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _yaml_config_params = ["_dynBlockQPS", "_dynBlockPeriod", "_dynBlockDuration", "_testServerPort"]
def testDynBlocksServFailRate(self):
"""
Dyn Blocks (group / YAML): Server Failure Rate
"""
- name = 'servfailrate.group.dynblocks.tests.powerdns.com.'
+ name = "servfailrate.group.dynblocks.tests.powerdns.com."
self.doTestRCodeRate(name, dns.rcode.SERVFAIL)
from dnsdisttests import DNSDistTest, pickAvailablePort
-@unittest.skipUnless('ENABLE_SUDO_TESTS' in os.environ, "sudo is not available")
-class TestSimpleEBPF(DNSDistTest):
+@unittest.skipUnless("ENABLE_SUDO_TESTS" in os.environ, "sudo is not available")
+class TestSimpleEBPF(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_dohWithNGHTTP2ServerPort = pickAvailablePort()
_doqServerPort = pickAvailablePort()
_doh3ServerPort = pickAvailablePort()
- _dohWithNGHTTP2BaseURL = ("https://%s:%d/" % (_serverName, _dohWithNGHTTP2ServerPort))
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doh3ServerPort))
+ _dohWithNGHTTP2BaseURL = "https://%s:%d/" % (_serverName, _dohWithNGHTTP2ServerPort)
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doh3ServerPort)
_config_template = """
setKey("%s")
addDOH3Local("127.0.0.1:%d", "%s", "%s")
"""
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_dohWithNGHTTP2ServerPort', '_serverCert', '_serverKey', '_doqServerPort', '_serverCert', '_serverKey', '_doh3ServerPort', '_serverCert', '_serverKey']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_dohWithNGHTTP2ServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doh3ServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
_sudoMode = True
def testNotBlocked(self):
# unblock 127.0.0.1, just in case
self.sendConsoleCommand('bpf:unblock(newCA("127.0.0.1"))')
- name = 'simplea.ebpf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simplea.ebpf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- for method in ["sendUDPQuery", "sendTCPQuery", "sendDOTQueryWrapper", "sendDOHWithNGHTTP2QueryWrapper", "sendDOQQueryWrapper", "sendDOH3QueryWrapper"]:
+ for method in [
+ "sendUDPQuery",
+ "sendTCPQuery",
+ "sendDOTQueryWrapper",
+ "sendDOHWithNGHTTP2QueryWrapper",
+ "sendDOQQueryWrapper",
+ "sendDOH3QueryWrapper",
+ ]:
sender = getattr(self, method)
(receivedQuery, receivedResponse) = sender(query, response, timeout=1)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
- if method == 'sendDOQQueryWrapper':
+ if method == "sendDOQQueryWrapper":
# dnspython sets the ID to 0
receivedResponse.id = response.id
self.assertEqual(response, receivedResponse)
# unblock 127.0.0.1, just in case
self.sendConsoleCommand('bpf:unblock(newCA("127.0.0.1"))')
- name = 'blocked.ebpf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "blocked.ebpf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# should be blocked over Do53 UDP
for method in ["sendUDPQuery"]:
self.assertEqual(receivedResponse, None)
# not over other protocols
- for method in ["sendTCPQuery", "sendDOTQueryWrapper", "sendDOHWithNGHTTP2QueryWrapper", "sendDOQQueryWrapper", "sendDOH3QueryWrapper"]:
+ for method in [
+ "sendTCPQuery",
+ "sendDOTQueryWrapper",
+ "sendDOHWithNGHTTP2QueryWrapper",
+ "sendDOQQueryWrapper",
+ "sendDOH3QueryWrapper",
+ ]:
sender = getattr(self, method)
(receivedQuery, receivedResponse) = sender(query, response, timeout=1)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
- if method == 'sendDOQQueryWrapper':
+ if method == "sendDOQQueryWrapper":
# dnspython sets the ID to 0
receivedResponse.id = response.id
self.assertEqual(response, receivedResponse)
# unblock 127.0.0.1, just in case
self.sendConsoleCommand('bpf:unblock(newCA("127.0.0.1"))')
- name = 'blocked-any-only.ebpf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'ANY', 'IN', use_edns=False)
+ name = "blocked-any-only.ebpf.tests.powerdns.com."
+ query = dns.message.make_query(name, "ANY", "IN", use_edns=False)
# ANY should be blocked over Do53 UDP
for method in ["sendUDPQuery"]:
(_, receivedResponse) = sender(query, response=None, useQueue=False, timeout=0.5)
self.assertEqual(receivedResponse, None)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# but A should NOT be blocked
for method in ["sendUDPQuery"]:
# block 127.0.0.1
self.sendConsoleCommand('bpf:block(newCA("127.0.0.1"))')
- name = 'ip-blocked.ebpf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "ip-blocked.ebpf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# should be blocked over Do53 UDP, Do53 TCP, DoH
for method in ["sendUDPQuery", "sendTCPQuery", "sendDOTQueryWrapper", "sendDOHWithNGHTTP2QueryWrapper"]:
(receivedQuery, receivedResponse) = sender(query, response, timeout=1)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
- if method == 'sendDOQQueryWrapper':
+ if method == "sendDOQQueryWrapper":
# dnspython sets the ID to 0
receivedResponse.id = response.id
self.assertEqual(response, receivedResponse)
import dns
from dnsdisttests import DNSDistTest
-class TestBasics(DNSDistTest):
+class TestBasics(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
"""
EDE: No EDNS
"""
- name = 'no-edns.ede.tests.powerdns.com.'
+ name = "no-edns.ede.tests.powerdns.com."
# no EDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
"""
EDE: Backend response
"""
- name = 'backend-response.ede.tests.powerdns.com.'
- ede = extendederrors.ExtendedErrorOption(16, b'my extended error status')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "backend-response.ede.tests.powerdns.com."
+ ede = extendederrors.ExtendedErrorOption(16, b"my extended error status")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
backendResponse = dns.message.make_response(query)
backendResponse.use_edns(edns=True, payload=4096, options=[])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
backendResponse.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=4096, options=[ede])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDE: Backend response (DO)
"""
- name = 'backend-response-do.ede.tests.powerdns.com.'
- ede = extendederrors.ExtendedErrorOption(16, b'my extended error status')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, want_dnssec=True)
+ name = "backend-response-do.ede.tests.powerdns.com."
+ ede = extendederrors.ExtendedErrorOption(16, b"my extended error status")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, want_dnssec=True)
backendResponse = dns.message.make_response(query)
backendResponse.use_edns(edns=True, payload=4096, options=[])
backendResponse.want_dnssec(True)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
backendResponse.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=4096, options=[ede])
expectedResponse.want_dnssec(True)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDE: Backend response with existing EDE
"""
- name = 'backend-response-existing-ede.ede.tests.powerdns.com.'
- ede = extendederrors.ExtendedErrorOption(16, b'my extended error status')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "backend-response-existing-ede.ede.tests.powerdns.com."
+ ede = extendederrors.ExtendedErrorOption(16, b"my extended error status")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
backendResponse = dns.message.make_response(query)
- backendEDE = extendederrors.ExtendedErrorOption(3, b'Stale answer')
+ backendEDE = extendederrors.ExtendedErrorOption(3, b"Stale answer")
backendResponse.use_edns(edns=True, payload=4096, options=[backendEDE])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
backendResponse.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=4096, options=[ede])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDE: Self-answered
"""
- name = 'self-answered.ede.tests.powerdns.com.'
- ede = extendederrors.ExtendedErrorOption(42, b'my self-answered extended error status')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "self-answered.ede.tests.powerdns.com."
+ ede = extendederrors.ExtendedErrorOption(42, b"my self-answered extended error status")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
# dnsdist sets RA = RD for self-generated responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=1232, options=[ede])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDE: Self-answered via Lua FFI
"""
- name = 'self-answered-ffi.ede.tests.powerdns.com.'
- ede = extendederrors.ExtendedErrorOption(29, b'Synthesized from Lua')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "self-answered-ffi.ede.tests.powerdns.com."
+ ede = extendederrors.ExtendedErrorOption(29, b"Synthesized from Lua")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
# dnsdist sets RA = RD for self-generated responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=1232, options=[ede])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDE: Self-answered via Lua FFI without any extra text
"""
- name = 'self-answered-ffi-no-extra.ede.tests.powerdns.com.'
- ede = extendederrors.ExtendedErrorOption(29, b'')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "self-answered-ffi-no-extra.ede.tests.powerdns.com."
+ ede = extendederrors.ExtendedErrorOption(29, b"")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
# dnsdist sets RA = RD for self-generated responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=1232, options=[ede])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.checkMessageEDNS(expectedResponse, receivedResponse)
-class TestMultipleEDE(DNSDistTest):
+class TestMultipleEDE(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
"""
EDE: Backend response with existing EDE
"""
- name = 'backend-response-existing-ede.ede.tests.powerdns.com.'
- ede = extendederrors.ExtendedErrorOption(16, b'my extended error status')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "backend-response-existing-ede.ede.tests.powerdns.com."
+ ede = extendederrors.ExtendedErrorOption(16, b"my extended error status")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
backendResponse = dns.message.make_response(query)
- backendEDE = extendederrors.ExtendedErrorOption(3, b'Stale answer')
+ backendEDE = extendederrors.ExtendedErrorOption(3, b"Stale answer")
backendResponse.use_edns(edns=True, payload=4096, options=[backendEDE])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
backendResponse.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=4096, options=[backendEDE, ede])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDE: Backend response with existing EDE that is replaced
"""
- name = 'backend-response-existing-ede-replace.ede.tests.powerdns.com.'
- replacingEde = extendederrors.ExtendedErrorOption(15, b'my replaced error status')
- ede = extendederrors.ExtendedErrorOption(16, b'my extended error status')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "backend-response-existing-ede-replace.ede.tests.powerdns.com."
+ replacingEde = extendederrors.ExtendedErrorOption(15, b"my replaced error status")
+ ede = extendederrors.ExtendedErrorOption(16, b"my extended error status")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
backendResponse = dns.message.make_response(query)
- backendEDE = extendederrors.ExtendedErrorOption(3, b'Stale answer')
+ backendEDE = extendederrors.ExtendedErrorOption(3, b"Stale answer")
backendResponse.use_edns(edns=True, payload=4096, options=[backendEDE])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
backendResponse.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=4096, options=[replacingEde, ede])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDE: Self-answered
"""
- name = 'self-answered.ede.tests.powerdns.com.'
- allEDE = extendederrors.ExtendedErrorOption(16, b'my extended error status')
- ede = extendederrors.ExtendedErrorOption(42, b'my self-answered extended error status')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "self-answered.ede.tests.powerdns.com."
+ allEDE = extendederrors.ExtendedErrorOption(16, b"my extended error status")
+ ede = extendederrors.ExtendedErrorOption(42, b"my self-answered extended error status")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
# dnsdist sets RA = RD for self-generated responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=1232, options=[allEDE, ede])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDE: Self-answered via Lua FFI
"""
- name = 'self-answered-ffi.ede.tests.powerdns.com.'
- allEDE = extendederrors.ExtendedErrorOption(16, b'my extended error status')
- ede = extendederrors.ExtendedErrorOption(29, b'Synthesized from Lua')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "self-answered-ffi.ede.tests.powerdns.com."
+ allEDE = extendederrors.ExtendedErrorOption(16, b"my extended error status")
+ ede = extendederrors.ExtendedErrorOption(29, b"Synthesized from Lua")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
# dnsdist sets RA = RD for self-generated responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=1232, options=[allEDE, ede])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDE: Self-answered via Lua FFI without any extra text
"""
- name = 'self-answered-ffi-no-extra.ede.tests.powerdns.com.'
- allEDE = extendederrors.ExtendedErrorOption(16, b'my extended error status')
- ede = extendederrors.ExtendedErrorOption(29, b'')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "self-answered-ffi-no-extra.ede.tests.powerdns.com."
+ allEDE = extendederrors.ExtendedErrorOption(16, b"my extended error status")
+ ede = extendederrors.ExtendedErrorOption(29, b"")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
# dnsdist sets RA = RD for self-generated responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=1232, options=[allEDE, ede])
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
import cookiesoption
from dnsdisttests import DNSDistTest
+
class EDNSOptionsBase(DNSDistTest):
_ednsTestFunction = """
function testEDNSOptions(dq)
end
"""
-class TestEDNSOptions(EDNSOptionsBase):
+class TestEDNSOptions(EDNSOptionsBase):
_config_template = """
%s
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_ednsTestFunction', '_testServerPort']
+ _config_params = ["_ednsTestFunction", "_testServerPort"]
def testWithoutEDNS(self):
"""
EDNS Options: No EDNS
"""
- name = 'noedns.ednsoptions.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "noedns.ednsoptions.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.255')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.255")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: Cookie
"""
- name = 'cookie.ednsoptions.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco])
+ name = "cookie.ednsoptions.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[eco])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: ECS4
"""
- name = 'ecs4.ednsoptions.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.4', 32)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "ecs4.ednsoptions.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("1.2.3.4", 32)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: ECS6
"""
- name = 'ecs6.ednsoptions.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('2001:DB8::1', 128)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "ecs6.ednsoptions.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("2001:DB8::1", 128)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: Cookie + ECS6
"""
- name = 'cookie-ecs6.ednsoptions.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('2001:DB8::1', 128)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso,eco])
+ name = "cookie-ecs6.ednsoptions.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("2001:DB8::1", 128)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso, eco])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: Two Cookies + ECS6
"""
- name = 'multiplecookies-ecs6.ednsoptions.tests.powerdns.com.'
- eco1 = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('2001:DB8::1', 128)
- eco2 = cookiesoption.CookiesOption(b'deadc0de', b'deadc0de')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco1, ecso, eco2])
+ name = "multiplecookies-ecs6.ednsoptions.tests.powerdns.com."
+ eco1 = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("2001:DB8::1", 128)
+ eco2 = cookiesoption.CookiesOption(b"deadc0de", b"deadc0de")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[eco1, ecso, eco2])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
-class TestEDNSOptionsAddingECS(EDNSOptionsBase):
+class TestEDNSOptionsAddingECS(EDNSOptionsBase):
_config_template = """
%s
newServer{address="127.0.0.1:%d", useClientSubnet=true}
"""
- _config_params = ['_ednsTestFunction', '_testServerPort']
+ _config_params = ["_ednsTestFunction", "_testServerPort"]
def testWithoutEDNS(self):
"""
EDNS Options: No EDNS (adding ECS)
"""
- name = 'noedns.ednsoptions-ecs.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "noedns.ednsoptions-ecs.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: Cookie (adding ECS)
"""
- name = 'cookie.ednsoptions-ecs.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=512, options=[eco])
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[eco,ecso], payload=512)
+ name = "cookie.ednsoptions-ecs.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=512, options=[eco])
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[eco, ecso], payload=512)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: ECS4 (adding ECS)
"""
- name = 'ecs4.ednsoptions-ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.4', 32)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
- ecsoResponse = clientsubnetoption.ClientSubnetOption('1.2.3.4', 24, scope=24)
+ name = "ecs4.ednsoptions-ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("1.2.3.4", 32)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("1.2.3.4", 24, scope=24)
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[ecsoResponse])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: ECS6 (adding ECS)
"""
- name = 'ecs6.ednsoptions-ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('2001:DB8::1', 128)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
- ecsoResponse = clientsubnetoption.ClientSubnetOption('2001:DB8::1', 128, scope=56)
+ name = "ecs6.ednsoptions-ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("2001:DB8::1", 128)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("2001:DB8::1", 128, scope=56)
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[ecsoResponse])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: Cookie + ECS6 (adding ECS)
"""
- name = 'cookie-ecs6.ednsoptions-ecs.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('2001:DB8::1', 128)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso,eco])
- ecsoResponse = clientsubnetoption.ClientSubnetOption('2001:DB8::1', 128, scope=56)
+ name = "cookie-ecs6.ednsoptions-ecs.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("2001:DB8::1", 128)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso, eco])
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("2001:DB8::1", 128, scope=56)
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[ecsoResponse])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: Two Cookies + ECS6
"""
- name = 'multiplecookies-ecs6.ednsoptions.tests.powerdns.com.'
- eco1 = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('2001:DB8::1', 128)
- eco2 = cookiesoption.CookiesOption(b'deadc0de', b'deadc0de')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco1, ecso, eco2])
+ name = "multiplecookies-ecs6.ednsoptions.tests.powerdns.com."
+ eco1 = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("2001:DB8::1", 128)
+ eco2 = cookiesoption.CookiesOption(b"deadc0de", b"deadc0de")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[eco1, ecso, eco2])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
-class TestEDNSOptionsLuaFFI(DNSDistTest):
+class TestEDNSOptionsLuaFFI(DNSDistTest):
_config_template = """
local ffi = require("ffi")
"""
EDNS Options: No EDNS (FFI)
"""
- name = 'noedns.ednsoptions.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "noedns.ednsoptions.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.255')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.255")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: Cookie (FFI)
"""
- name = 'cookie.ednsoptions.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco])
+ name = "cookie.ednsoptions.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[eco])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: ECS4 (FFI)
"""
- name = 'ecs4.ednsoptions.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.4', 32)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "ecs4.ednsoptions.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("1.2.3.4", 32)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: ECS6 (FFI)
"""
- name = 'ecs6.ednsoptions.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('2001:DB8::1', 128)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "ecs6.ednsoptions.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("2001:DB8::1", 128)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: Cookie + ECS6 (FFI)
"""
- name = 'cookie-ecs6.ednsoptions.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('2001:DB8::1', 128)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso,eco])
+ name = "cookie-ecs6.ednsoptions.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("2001:DB8::1", 128)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso, eco])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
EDNS Options: Two Cookies + ECS6 (FFI)
"""
- name = 'multiplecookies-ecs6.ednsoptions.tests.powerdns.com.'
- eco1 = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecso = clientsubnetoption.ClientSubnetOption('2001:DB8::1', 128)
- eco2 = cookiesoption.CookiesOption(b'deadc0de', b'deadc0de')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco1, ecso, eco2])
+ name = "multiplecookies-ecs6.ednsoptions.tests.powerdns.com."
+ eco1 = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecso = clientsubnetoption.ClientSubnetOption("2001:DB8::1", 128)
+ eco2 = cookiesoption.CookiesOption(b"deadc0de", b"deadc0de")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[eco1, ecso, eco2])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
import clientsubnetoption
from dnsdisttests import DNSDistTest
+
class TestEDNSSelfGenerated(DNSDistTest):
"""
Check that dnsdist sends correct EDNS data on
"""
EDNS on Self-Generated: No existing EDNS
"""
- name = 'no-edns.rcode.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "no-edns.rcode.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
- name = 'no-edns.tc.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "no-edns.tc.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
- name = 'no-edns.lua.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "no-edns.lua.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NXDOMAIN)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
- name = 'no-edns.spoof.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "no-edns.spoof.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=1042)
- expectedResponse.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1', '192.0.2.2'))
+ expectedResponse.answer.append(
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.2")
+ )
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
"""
EDNS on Self-Generated: EDNS with DO=0
"""
- name = 'edns-no-do.rcode.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=False)
+ name = "edns-no-do.rcode.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=False)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=1042)
expectedResponse.set_rcode(dns.rcode.REFUSED)
self.assertFalse(receivedResponse.ednsflags & dns.flags.DO)
self.assertEqual(receivedResponse.payload, 1042)
- name = 'edns-no-do.tc.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=False)
+ name = "edns-no-do.tc.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=False)
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=1042)
self.assertFalse(receivedResponse.ednsflags & dns.flags.DO)
self.assertEqual(receivedResponse.payload, 1042)
- name = 'edns-no-do.lua.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=False)
+ name = "edns-no-do.lua.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=False)
expectedResponse = dns.message.make_response(query, our_payload=1042)
expectedResponse.set_rcode(dns.rcode.NXDOMAIN)
self.assertFalse(receivedResponse.ednsflags & dns.flags.DO)
self.assertEqual(receivedResponse.payload, 1042)
- name = 'edns-no-do.spoof.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=False)
+ name = "edns-no-do.spoof.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=False)
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=1042)
- expectedResponse.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1', '192.0.2.2'))
+ expectedResponse.answer.append(
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.2")
+ )
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
"""
EDNS on Self-Generated: EDNS with DO=1
"""
- name = 'edns-do.rcode.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=True)
+ name = "edns-do.rcode.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=True)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=1042)
expectedResponse.want_dnssec(True)
self.assertTrue(receivedResponse.ednsflags & dns.flags.DO)
self.assertEqual(receivedResponse.payload, 1042)
- name = 'edns-do.tc.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=True)
+ name = "edns-do.tc.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=True)
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=1042)
self.assertTrue(receivedResponse.ednsflags & dns.flags.DO)
self.assertEqual(receivedResponse.payload, 1042)
- name = 'edns-do.lua.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=True)
+ name = "edns-do.lua.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=True)
expectedResponse = dns.message.make_response(query, our_payload=1042)
expectedResponse.want_dnssec(True)
expectedResponse.set_rcode(dns.rcode.NXDOMAIN)
self.assertTrue(receivedResponse.ednsflags & dns.flags.DO)
self.assertEqual(receivedResponse.payload, 1042)
- name = 'edns-do.spoof.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=True)
+ name = "edns-do.spoof.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=True)
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=1042)
expectedResponse.want_dnssec(True)
- expectedResponse.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1', '192.0.2.2'))
+ expectedResponse.answer.append(
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.2")
+ )
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
"""
EDNS on Self-Generated: EDNS with options in the query
"""
- name = 'edns-options.rcode.edns-self.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512, want_dnssec=True)
+ name = "edns-options.rcode.edns-self.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512, want_dnssec=True)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=1042)
expectedResponse.set_rcode(dns.rcode.REFUSED)
self.assertTrue(receivedResponse.ednsflags & dns.flags.DO)
self.assertEqual(receivedResponse.payload, 1042)
- name = 'edns-options.tc.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512, want_dnssec=True)
+ name = "edns-options.tc.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512, want_dnssec=True)
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=1042)
self.assertTrue(receivedResponse.ednsflags & dns.flags.DO)
self.assertEqual(receivedResponse.payload, 1042)
- name = 'edns-options.lua.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512, want_dnssec=True)
+ name = "edns-options.lua.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512, want_dnssec=True)
expectedResponse = dns.message.make_response(query, our_payload=1042)
expectedResponse.want_dnssec(True)
expectedResponse.set_rcode(dns.rcode.NXDOMAIN)
self.assertTrue(receivedResponse.ednsflags & dns.flags.DO)
self.assertEqual(receivedResponse.payload, 1042)
- name = 'edns-options.spoof.edns-self.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512, want_dnssec=True)
+ name = "edns-options.spoof.edns-self.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512, want_dnssec=True)
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=1042)
expectedResponse.want_dnssec(True)
- expectedResponse.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1', '192.0.2.2'))
+ expectedResponse.answer.append(
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.2")
+ )
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
"""
EDNS on Self-Generated (disabled): EDNS with DO=0
"""
- name = 'edns-no-do.rcode.edns-self-disabled.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=False)
+ name = "edns-no-do.rcode.edns-self-disabled.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=False)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
- name = 'edns-no-do.tc.edns-self-disabled.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=False)
+ name = "edns-no-do.tc.edns-self-disabled.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=False)
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
- name = 'edns-no-do.lua.edns-self-disabled.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=False)
+ name = "edns-no-do.lua.edns-self-disabled.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=False)
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NXDOMAIN)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
- name = 'edns-no-do.spoof.edns-self-disabled.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=False)
+ name = "edns-no-do.spoof.edns-self-disabled.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=False)
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- expectedResponse.answer.append(dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1', '192.0.2.2'))
+ expectedResponse.answer.append(
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.2")
+ )
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
import cookiesoption
from dnsdisttests import DNSDistTest
+
class TestEdnsClientSubnetNoOverride(DNSDistTest):
"""
dnsdist is configured to add the EDNS0 Client Subnet
and that the response received from dnsdist does not
have an EDNS pseudo-RR.
"""
- name = 'withoutedns.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "withoutedns.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
response = dns.message.make_response(expectedQuery)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
has a valid ECS value and that the response
received from dnsdist contains an EDNS pseudo-RR.
"""
- name = 'withednsnoecs.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "withednsnoecs.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(expectedQuery)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
and that the response received from dnsdist contains
an EDNS pseudo-RR.
"""
- name = 'withednsecs.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('1.2.3.4', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "withednsecs.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("1.2.3.4", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
-
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
(receivedQuery, receivedResponse) = sender(query, response)
This time the response returned by the backend contains
an ECS option with scope set.
"""
- name = 'withoutedns.bereturnsecs.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "withoutedns.bereturnsecs.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
response = dns.message.make_response(expectedQuery)
- ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24)
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24, scope=24)
response.use_edns(edns=True, payload=4096, options=[ecsoResponse])
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
This time the response returned by the backend contains
an ECS option with scope set.
"""
- name = 'withednsnoecs.bereturnsecs.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "withednsnoecs.bereturnsecs.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(expectedQuery)
- ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24)
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24, scope=24)
response.use_edns(edns=True, payload=4096, options=[ecsoResponse])
expectedResponse = dns.message.make_response(query, our_payload=4096)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
This time the response returned by the backend contains
one cookies then one ECS option.
"""
- name = 'withednsnoecs.bereturnscookiesthenecs.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "withednsnoecs.bereturnscookiesthenecs.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(expectedQuery)
- ecoResponse = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24)
+ ecoResponse = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24, scope=24)
response.use_edns(edns=True, payload=4096, options=[ecoResponse, ecsoResponse])
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=4096, options=[ecoResponse])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
expectedResponse.use_edns(edns=True, payload=4096, options=[ecoResponse])
This time the response returned by the backend contains
one ECS then one Cookies option.
"""
- name = 'withednsnoecs.bereturnsecsthencookies.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "withednsnoecs.bereturnsecsthencookies.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(expectedQuery)
- ecoResponse = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24)
+ ecoResponse = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24, scope=24)
response.use_edns(edns=True, payload=4096, options=[ecsoResponse, ecoResponse])
expectedResponse = dns.message.make_response(query, our_payload=4096)
expectedResponse.use_edns(edns=True, payload=4096, options=[ecoResponse])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
response.use_edns(edns=True, payload=4096, options=[ecoResponse])
This time the response returned by the backend contains
one Cookies, one ECS then one Cookies option.
"""
- name = 'withednsnoecs.bereturnscookiesecscookies.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "withednsnoecs.bereturnscookiesecscookies.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(expectedQuery)
- ecoResponse = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24)
+ ecoResponse = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ ecsoResponse = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24, scope=24)
response.use_edns(edns=True, payload=4096, options=[ecoResponse, ecsoResponse, ecoResponse])
expectedResponse = dns.message.make_response(query, our_payload=4096)
expectedResponse.use_edns(edns=True, payload=4096, options=[ecoResponse, ecoResponse])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
self.checkQueryEDNSWithECS(expectedQuery, receivedQuery)
self.checkResponseEDNSWithoutECS(expectedResponse, receivedResponse, withCookies=2)
+
class TestEdnsClientSubnetOverride(DNSDistTest):
"""
dnsdist is configured to add the EDNS0 Client Subnet
and that the response received from dnsdist does not
have an EDNS pseudo-RR.
"""
- name = 'withoutedns.overridden.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "withoutedns.overridden.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
response = dns.message.make_response(expectedQuery)
response.use_edns(edns=True, payload=4096, options=[ecso])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
has a valid ECS value and that the response
received from dnsdist contains an EDNS pseudo-RR.
"""
- name = 'withednsnoecs.overridden.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "withednsnoecs.overridden.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(expectedQuery)
response.use_edns(edns=True, payload=4096, options=[ecso])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query, our_payload=4096)
expectedResponse.answer.append(rrset)
The initial ECS value is shorter than the one it will be
replaced with.
"""
- name = 'withednsecs.overridden.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 8)
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[rewrittenEcso])
+ name = "withednsecs.overridden.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 8)
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[rewrittenEcso])
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[rewrittenEcso])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
The initial ECS value is longer than the one it will
replaced with.
"""
- name = 'withednsecs.overridden.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[rewrittenEcso])
+ name = "withednsecs.overridden.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[rewrittenEcso])
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[rewrittenEcso])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
The initial ECS value is exactly the same size as
the one it will replaced with.
"""
- name = 'withednsecs.overridden.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24)
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[rewrittenEcso])
+ name = "withednsecs.overridden.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 24)
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[rewrittenEcso])
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[rewrittenEcso])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
has a valid ECS value and that the response
received from dnsdist contains an EDNS pseudo-RR.
"""
- name = 'withecs-followedbyanother.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24)
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
-
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco,ecso,eco])
+ name = "withecs-followedbyanother.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 24)
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
+
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[eco, ecso, eco])
# I would have loved to use a TSIG here but I can't find how to make dnspython ignore
# it while parsing the message in the receiver :-/
query.additional.append(rrset)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco,eco,rewrittenEcso])
+ expectedQuery = dns.message.make_query(
+ name, "A", "IN", use_edns=True, payload=4096, options=[eco, eco, rewrittenEcso]
+ )
expectedQuery.additional.append(rrset)
response = dns.message.make_response(expectedQuery)
has a valid ECS value and that the response
received from dnsdist contains an EDNS pseudo-RR.
"""
- name = 'record-in-an-withecs.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24)
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
-
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco,ecso,eco])
+ name = "record-in-an-withecs.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 24)
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
+
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[eco, ecso, eco])
query.answer.append(rrset)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco,eco,rewrittenEcso])
+ expectedQuery = dns.message.make_query(
+ name, "A", "IN", use_edns=True, payload=4096, options=[eco, eco, rewrittenEcso]
+ )
expectedQuery.answer.append(rrset)
response = dns.message.make_response(expectedQuery)
has a valid ECS value and that the response
received from dnsdist contains an EDNS pseudo-RR.
"""
- name = 'record-in-an-withecs.ecs.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24)
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
-
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco,ecso,eco])
+ name = "record-in-an-withecs.ecs.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 24)
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
+
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[eco, ecso, eco])
query.authority.append(rrset)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco,eco,rewrittenEcso])
+ expectedQuery = dns.message.make_query(
+ name, "A", "IN", use_edns=True, payload=4096, options=[eco, eco, rewrittenEcso]
+ )
expectedQuery.authority.append(rrset)
response = dns.message.make_response(expectedQuery)
has a valid ECS value and that the response
received from dnsdist contains an EDNS pseudo-RR.
"""
- name = 'withedns-no-ecs-followedbyanother.ecs.tests.powerdns.com.'
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
-
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco])
+ name = "withedns-no-ecs-followedbyanother.ecs.tests.powerdns.com."
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
+
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[eco])
# I would have loved to use a TSIG here but I can't find how to make dnspython ignore
# it while parsing the message in the receiver :-/
query.additional.append(rrset)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco,rewrittenEcso])
+ expectedQuery = dns.message.make_query(
+ name, "A", "IN", use_edns=True, payload=4096, options=[eco, rewrittenEcso]
+ )
expectedQuery.additional.append(rrset)
response = dns.message.make_response(expectedQuery)
self.checkQueryEDNSWithECS(expectedQuery, receivedQuery, 1)
self.checkResponseEDNSWithoutECS(expectedResponse, receivedResponse, 2)
+
class TestECSDisabledByRuleOrLua(DNSDistTest):
"""
dnsdist is configured to add the EDNS0 Client Subnet
"""
ECS Disable: ECS enabled in the backend
"""
- name = 'notdisabled.ecsrules.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 16)
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "notdisabled.ecsrules.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 16)
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
response = dns.message.make_response(expectedQuery)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
"""
ECS Disable: ECS enabled in the backend, but disabled by a rule
"""
- name = 'disabled.ecsrules.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "disabled.ecsrules.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
ECS Disable: ECS enabled in the backend, but disabled via Lua
"""
- name = 'disabledvialua.ecsrules.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "disabledvialua.ecsrules.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.checkQueryNoEDNS(query, receivedQuery)
self.checkResponseNoEDNS(response, receivedResponse)
+
class TestECSOverrideSetByRuleOrLua(DNSDistTest):
"""
dnsdist is configured to set the EDNS0 Client Subnet
"""
ECS Override: not set via Lua or a rule
"""
- name = 'notoverridden.ecsrules.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ name = "notoverridden.ecsrules.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[ecso])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
ECS Override: set with a rule
"""
- name = 'overridden.ecsrules.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24)
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[rewrittenEcso])
+ name = "overridden.ecsrules.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 24)
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[rewrittenEcso])
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[rewrittenEcso])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
ECS Override: set via Lua
"""
- name = 'overriddenvialua.ecsrules.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24)
- rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[rewrittenEcso])
+ name = "overriddenvialua.ecsrules.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 24)
+ rewrittenEcso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[ecso])
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, options=[rewrittenEcso])
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[rewrittenEcso])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.checkQueryEDNSWithECS(expectedQuery, receivedQuery)
self.checkResponseEDNSWithECS(response, receivedResponse)
+
class TestECSPrefixLengthSetByRuleOrLua(DNSDistTest):
"""
dnsdist is configured to set the EDNS0 Client Subnet
"""
ECS Prefix Length: not overridden via Lua or a rule
"""
- name = 'notoverriddenprefixlength.ecsrules.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "notoverriddenprefixlength.ecsrules.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[ecso])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
"""
ECS Prefix Length: overridden with a rule
"""
- name = 'overriddenprefixlength.ecsrules.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 32)
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "overriddenprefixlength.ecsrules.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 32)
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
response = dns.message.make_response(expectedQuery)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
"""
ECS Prefix Length: overridden via Lua
"""
- name = 'overriddenprefixlengthvialua.ecsrules.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 32)
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "overriddenprefixlengthvialua.ecsrules.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 32)
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
response = dns.message.make_response(expectedQuery)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
self.checkQueryEDNSWithECS(expectedQuery, receivedQuery)
self.checkResponseNoEDNS(expectedResponse, receivedResponse)
+
class TestECSPrefixSetByRule(DNSDistTest):
"""
dnsdist is configured to set the EDNS0 Client Subnet
"""
ECS Prefix: not set
"""
- name = 'notsetecsaction.ecsrules.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 32)
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "notsetecsaction.ecsrules.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 32)
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=4096, options=[ecso])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
"""
ECS Prefix: set with SetECSAction
"""
- name = 'setecsaction.ecsrules.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ name = "setecsaction.ecsrules.tests.powerdns.com."
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
response = dns.message.make_response(expectedQuery)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
import queue
from dnsdisttests import DNSDistTest, pickAvailablePort, ResponderDropAction
+
class HealthCheckTest(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
- _webServerAPIKey = 'apisecret'
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
- _config_params = ['_consoleKeyB64', '_consolePort', '_webServerPort', '_webServerAPIKeyHashed', '_testServerPort']
+ _webServerAPIKey = "apisecret"
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
+ _config_params = ["_consoleKeyB64", "_consolePort", "_webServerPort", "_webServerAPIKeyHashed", "_testServerPort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
return self.sendConsoleCommand("if getServer(0):isUp() then return 'up' else return 'down' end").strip("\n")
def getBackendMetric(self, backendID, metricName):
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertIn('servers', content)
- servers = content['servers']
+ self.assertIn("servers", content)
+ servers = content["servers"]
server = servers[backendID]
return int(server[metricName])
+
class TestDefaultHealthCheck(HealthCheckTest):
# this test suite uses a different responder port
# because we need fresh counters
before = TestDefaultHealthCheck._healthCheckCounter
time.sleep(1.5)
self.assertGreater(TestDefaultHealthCheck._healthCheckCounter, before)
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), 0)
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), 0)
+ self.assertEqual(self.getBackendStatus(), "up")
self.sendConsoleCommand("getServer(0):setUp()")
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendStatus(), "up")
self.assertEqual(self.sendConsoleCommand("getServer(0):getHealthCheckMode()").rstrip(), "active")
before = TestDefaultHealthCheck._healthCheckCounter
self.assertEqual(TestDefaultHealthCheck._healthCheckCounter, before)
self.sendConsoleCommand("getServer(0):setDown()")
- self.assertEqual(self.getBackendStatus(), 'down')
+ self.assertEqual(self.getBackendStatus(), "down")
before = TestDefaultHealthCheck._healthCheckCounter
time.sleep(1.5)
self.sendConsoleCommand("getServer(0):setAuto()")
# we get back the previous state, which was up
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendStatus(), "up")
self.assertEqual(self.sendConsoleCommand("getServer(0):getHealthCheckMode()").rstrip(), "active")
before = TestDefaultHealthCheck._healthCheckCounter
time.sleep(1.5)
self.assertGreater(TestDefaultHealthCheck._healthCheckCounter, before)
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendStatus(), "up")
self.sendConsoleCommand("getServer(0):setDown()")
- self.assertEqual(self.getBackendStatus(), 'down')
+ self.assertEqual(self.getBackendStatus(), "down")
self.assertEqual(self.sendConsoleCommand("getServer(0):getHealthCheckMode()").rstrip(), "active")
self.sendConsoleCommand("getServer(0):setAuto(false)")
before = TestDefaultHealthCheck._healthCheckCounter
time.sleep(1.5)
self.assertGreater(TestDefaultHealthCheck._healthCheckCounter, before)
- self.assertEqual(self.getBackendStatus(), 'up')
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), 0)
+ self.assertEqual(self.getBackendStatus(), "up")
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), 0)
self.assertEqual(self.sendConsoleCommand("getServer(0):getHealthCheckMode()").rstrip(), "active")
self.sendConsoleCommand("getServer(0):setLazyAuto()")
self.sendConsoleCommand("getServer(0):setActiveAuto()")
self.assertEqual(self.sendConsoleCommand("getServer(0):getHealthCheckMode()").rstrip(), "active")
+
class TestHealthCheckForcedUP(HealthCheckTest):
# this test suite uses a different responder port
# because we need fresh counters
before = TestHealthCheckForcedUP._healthCheckCounter
time.sleep(1.5)
self.assertEqual(TestHealthCheckForcedUP._healthCheckCounter, before)
- self.assertEqual(self.getBackendStatus(), 'up')
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), 0)
+ self.assertEqual(self.getBackendStatus(), "up")
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), 0)
+
class TestHealthCheckForcedDown(HealthCheckTest):
# this test suite uses a different responder port
before = TestHealthCheckForcedDown._healthCheckCounter
time.sleep(1.5)
self.assertEqual(TestHealthCheckForcedDown._healthCheckCounter, before)
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), 0)
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), 0)
+
class TestHealthCheckCustomName(HealthCheckTest):
# this test suite uses a different responder port
# because it uses a different health check name
_testServerPort = pickAvailablePort()
- _healthCheckName = 'powerdns.com.'
- _config_params = ['_consoleKeyB64', '_consolePort', '_webServerPort', '_webServerAPIKeyHashed', '_testServerPort', '_healthCheckName']
+ _healthCheckName = "powerdns.com."
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_webServerPort",
+ "_webServerAPIKeyHashed",
+ "_testServerPort",
+ "_healthCheckName",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
before = TestHealthCheckCustomName._healthCheckCounter
time.sleep(1.5)
self.assertGreater(TestHealthCheckCustomName._healthCheckCounter, before)
- self.assertEqual(self.getBackendStatus(), 'up')
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), 0)
+ self.assertEqual(self.getBackendStatus(), "up")
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), 0)
+
class TestHealthCheckCustomNameNoAnswer(HealthCheckTest):
# this test suite uses a different responder port
before = TestHealthCheckCustomNameNoAnswer._healthCheckCounter
time.sleep(1.5)
self.assertEqual(TestHealthCheckCustomNameNoAnswer._healthCheckCounter, before)
- self.assertEqual(self.getBackendStatus(), 'down')
- self.assertGreater(self.getBackendMetric(0, 'healthCheckFailures'), 0)
- self.assertGreater(self.getBackendMetric(0, 'healthCheckFailuresTimeout'), 0)
+ self.assertEqual(self.getBackendStatus(), "down")
+ self.assertGreater(self.getBackendMetric(0, "healthCheckFailures"), 0)
+ self.assertGreater(self.getBackendMetric(0, "healthCheckFailuresTimeout"), 0)
+
class TestHealthCheckCustomFunction(HealthCheckTest):
# this test suite uses a different responder port
_testServerPort = pickAvailablePort()
_answerUnexpected = False
- _healthCheckName = 'powerdns.com.'
+ _healthCheckName = "powerdns.com."
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
before = TestHealthCheckCustomFunction._healthCheckCounter
time.sleep(1.5)
self.assertGreater(TestHealthCheckCustomFunction._healthCheckCounter, before)
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendStatus(), "up")
+
class TestHealthCheckCustomResponseValidationFunction(HealthCheckTest):
# this test suite uses a different responder port
# because it uses a different health check configuration
_testServerPort = pickAvailablePort()
- _healthCheckName = 'powerdns.com.'
+ _healthCheckName = "powerdns.com."
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
backend:setHealthCheckResponseValidator(myHealthCheckValidationFunction)
setVerboseHealthChecks(true)
"""
- _config_params = ['_consoleKeyB64', '_consolePort', '_webServerPort', '_webServerAPIKeyHashed', '_healthCheckName', '_testServerPort', '_healthCheckName']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_webServerPort",
+ "_webServerAPIKeyHashed",
+ "_healthCheckName",
+ "_testServerPort",
+ "_healthCheckName",
+ ]
_verboseMode = True
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP health-check Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, cls.healthCallback])
+ cls._UDPResponder = threading.Thread(
+ name="UDP health-check Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, cls.healthCallback],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
@classmethod
def healthCallback(cls, request):
print(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.2')
+ rrset = dns.rrset.from_text(request.question[0].name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.2")
response = dns.message.make_response(request)
response.answer.append(rrset)
return response.to_wire()
HealthChecks: Custom response validation function
"""
time.sleep(1.5)
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendStatus(), "up")
+
_do53HealthCheckQueries = 0
_dotHealthCheckQueries = 0
_dohHealthCheckQueries = 0
+
class TestLazyHealthChecks(HealthCheckTest):
_extraStartupSleep = 1
_do53Port = pickAvailablePort()
_dohPort = pickAvailablePort()
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_do53Port', '_dotPort', '_dohPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_do53Port", "_dotPort", "_dohPort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
@staticmethod
def HandleDNSQuery(request):
response = dns.message.make_response(request)
- if str(request.question[0].name).startswith('server-failure'):
+ if str(request.question[0].name).startswith("server-failure"):
response.set_rcode(dns.rcode.SERVFAIL)
return response.to_wire()
@classmethod
def Do53Callback(cls, request):
global _do53HealthCheckQueries
- if str(request.question[0].name).startswith('a.root-servers.net'):
+ if str(request.question[0].name).startswith("a.root-servers.net"):
_do53HealthCheckQueries = _do53HealthCheckQueries + 1
response = dns.message.make_response(request)
return response.to_wire()
@classmethod
def DoTCallback(cls, request):
global _dotHealthCheckQueries
- if str(request.question[0].name).startswith('a.root-servers.net'):
+ if str(request.question[0].name).startswith("a.root-servers.net"):
_dotHealthCheckQueries = _dotHealthCheckQueries + 1
response = dns.message.make_response(request)
return response.to_wire()
@classmethod
def DoHCallback(cls, request, requestHeaders, fromQueue, toQueue):
global _dohHealthCheckQueries
- if str(request.question[0].name).startswith('a.root-servers.net'):
+ if str(request.question[0].name).startswith("a.root-servers.net"):
_dohHealthCheckQueries = _dohHealthCheckQueries + 1
response = dns.message.make_response(request)
return 200, response.to_wire()
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
- Do53Responder = threading.Thread(name='Do53 Lazy Responder', target=cls.UDPResponder, args=[cls._do53Port, cls._toResponderQueue, cls._fromResponderQueue, False, cls.Do53Callback])
+ Do53Responder = threading.Thread(
+ name="Do53 Lazy Responder",
+ target=cls.UDPResponder,
+ args=[cls._do53Port, cls._toResponderQueue, cls._fromResponderQueue, False, cls.Do53Callback],
+ )
Do53Responder.daemon = True
Do53Responder.start()
- Do53TCPResponder = threading.Thread(name='Do53 TCP Lazy Responder', target=cls.TCPResponder, args=[cls._do53Port, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.Do53Callback])
+ Do53TCPResponder = threading.Thread(
+ name="Do53 TCP Lazy Responder",
+ target=cls.TCPResponder,
+ args=[cls._do53Port, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.Do53Callback],
+ )
Do53TCPResponder.daemon = True
Do53TCPResponder.start()
- DoTResponder = threading.Thread(name='DoT Lazy Responder', target=cls.TCPResponder, args=[cls._dotPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.DoTCallback, tlsContext])
+ DoTResponder = threading.Thread(
+ name="DoT Lazy Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._dotPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.DoTCallback,
+ tlsContext,
+ ],
+ )
DoTResponder.daemon = True
DoTResponder.start()
- DoHResponder = threading.Thread(name='DoH Lazy Responder', target=cls.DOHResponder, args=[cls._dohPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.DoHCallback, tlsContext])
+ DoHResponder = threading.Thread(
+ name="DoH Lazy Responder",
+ target=cls.DOHResponder,
+ args=[
+ cls._dohPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.DoHCallback,
+ tlsContext,
+ ],
+ )
DoHResponder.daemon = True
DoHResponder.start()
time.sleep(1)
self.assertEqual(_do53HealthCheckQueries, 1)
- name = 'do53.lazy.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "do53.lazy.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- failedQuery = dns.message.make_query('server-failure.do53.lazy.test.powerdns.com.', 'A', 'IN')
+ failedQuery = dns.message.make_query("server-failure.do53.lazy.test.powerdns.com.", "A", "IN")
failedResponse = dns.message.make_response(failedQuery)
failedResponse.set_rcode(dns.rcode.SERVFAIL)
time.sleep(1.5)
self.assertEqual(_do53HealthCheckQueries, 2)
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendStatus(), "up")
def testDoTLazy(self):
"""
time.sleep(1)
self.assertEqual(_dotHealthCheckQueries, 1)
- name = 'dot.lazy.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "dot.lazy.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- failedQuery = dns.message.make_query('server-failure.dot.lazy.test.powerdns.com.', 'A', 'IN')
+ failedQuery = dns.message.make_query("server-failure.dot.lazy.test.powerdns.com.", "A", "IN")
failedResponse = dns.message.make_response(failedQuery)
failedResponse.set_rcode(dns.rcode.SERVFAIL)
time.sleep(1.5)
self.assertEqual(_dotHealthCheckQueries, 2)
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendStatus(), "up")
def testDoHLazy(self):
"""
time.sleep(1)
self.assertEqual(_dohHealthCheckQueries, 1)
- name = 'doh.lazy.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "doh.lazy.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- failedQuery = dns.message.make_query('server-failure.doh.lazy.test.powerdns.com.', 'A', 'IN')
+ failedQuery = dns.message.make_query("server-failure.doh.lazy.test.powerdns.com.", "A", "IN")
failedResponse = dns.message.make_response(failedQuery)
failedResponse.set_rcode(dns.rcode.SERVFAIL)
time.sleep(1.5)
self.assertEqual(_dohHealthCheckQueries, 2)
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendStatus(), "up")
-class HealthCheckUpdateParams(HealthCheckTest):
+class HealthCheckUpdateParams(HealthCheckTest):
_healthQueue = queue.Queue()
_dropHealthCheck = False
_delayResponse = None
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, cls.healthCallback])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, cls.healthCallback],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
@classmethod
def healthCallback(cls, request):
if cls._dropHealthCheck:
- cls._healthQueue.put(False)
- return ResponderDropAction()
+ cls._healthQueue.put(False)
+ return ResponderDropAction()
response = dns.message.make_response(request)
if cls._delayResponse is not None:
time.sleep(cls._delayResponse)
def setDelay(cls, delay):
cls._delayResponse = delay
-class TestUpdateHCParamsCombo1(HealthCheckUpdateParams):
+class TestUpdateHCParamsCombo1(HealthCheckUpdateParams):
# this test suite uses a different responder port
_testServerPort = pickAvailablePort()
"""
# consume health checks upon sys init
try:
- while self.wait1(False): pass
- except queue.Empty: pass
+ while self.wait1(False):
+ pass
+ except queue.Empty:
+ pass
self.assertEqual(self.wait1(), True)
time.sleep(0.1)
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), 0)
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), 0)
+ self.assertEqual(self.getBackendStatus(), "up")
self.sendConsoleCommand("getServer(0):setHealthCheckParams({maxCheckFailures=2,rise=2})")
self.setDrop()
i = 1
while i <= 3:
rc = self.wait1()
- if rc is False: break
+ if rc is False:
+ break
i += 1
self.assertGreater(3, i)
time.sleep(1.1)
# should have failures but still up
- self.assertGreater(self.getBackendMetric(0, 'healthCheckFailures'), 0)
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertGreater(self.getBackendMetric(0, "healthCheckFailures"), 0)
+ self.assertEqual(self.getBackendStatus(), "up")
# wait for 2nd failure
self.assertEqual(self.wait1(), False)
time.sleep(1.1)
# should have more failures and down
- self.assertGreater(self.getBackendMetric(0, 'healthCheckFailures'), 1)
- self.assertEqual(self.getBackendStatus(), 'down')
+ self.assertGreater(self.getBackendMetric(0, "healthCheckFailures"), 1)
+ self.assertEqual(self.getBackendStatus(), "down")
self.setDrop(False)
i = 1
while i <= 3:
rc = self.wait1()
- if rc is True: break
+ if rc is True:
+ break
i += 1
self.assertGreater(3, i)
time.sleep(0.1)
# still down
- self.assertEqual(self.getBackendStatus(), 'down')
+ self.assertEqual(self.getBackendStatus(), "down")
- beforeFailure = self.getBackendMetric(0, 'healthCheckFailures')
+ beforeFailure = self.getBackendMetric(0, "healthCheckFailures")
# wati for 2nd success
self.assertEqual(self.wait1(), True)
time.sleep(0.1)
# should have no more failures, back to up
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), beforeFailure)
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), beforeFailure)
+ self.assertEqual(self.getBackendStatus(), "up")
-class TestUpdateHCParamsCombo2(HealthCheckUpdateParams):
+class TestUpdateHCParamsCombo2(HealthCheckUpdateParams):
# this test suite uses a different responder port
_testServerPort = pickAvailablePort()
"""
# consume health checks upon sys init
try:
- while self.wait1(False): pass
- except queue.Empty: pass
+ while self.wait1(False):
+ pass
+ except queue.Empty:
+ pass
self.assertEqual(self.wait1(), True)
time.sleep(0.1)
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), 0)
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), 0)
+ self.assertEqual(self.getBackendStatus(), "up")
self.sendConsoleCommand("getServer(0):setHealthCheckParams({checkInterval=2})")
self.assertEqual(self.wait1(), True)
t2 = time.time()
# intervals shall be greater than 1
- self.assertGreater(t2-t1, 1.5)
+ self.assertGreater(t2 - t1, 1.5)
self.sendConsoleCommand("getServer(0):setHealthCheckParams({checkTimeout=2000})")
self.setDrop()
i = 1
while i <= 3:
rc = self.wait1()
- if rc is False: break
+ if rc is False:
+ break
i += 1
self.assertGreater(3, i)
- beforeFailure = self.getBackendMetric(0, 'healthCheckFailures')
+ beforeFailure = self.getBackendMetric(0, "healthCheckFailures")
time.sleep(1.5)
# not timeout yet, should have no failure increase
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), beforeFailure)
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), beforeFailure)
time.sleep(1)
# now should timeout and failure increased
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), beforeFailure+1)
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), beforeFailure + 1)
-class TestHealthCheckLatency(HealthCheckUpdateParams):
+class TestHealthCheckLatency(HealthCheckUpdateParams):
# this test suite uses a different responder port
_testServerPort = pickAvailablePort()
"""
# consume health checks upon sys init
try:
- while self.wait1(False): pass
- except queue.Empty: pass
+ while self.wait1(False):
+ pass
+ except queue.Empty:
+ pass
self.assertEqual(self.wait1(), True)
time.sleep(0.1)
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), 0)
- self.assertEqual(self.getBackendStatus(), 'up')
- latency = self.getBackendMetric(0, 'healthCheckLatency')
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), 0)
+ self.assertEqual(self.getBackendStatus(), "up")
+ latency = self.getBackendMetric(0, "healthCheckLatency")
# less than 500 ms
self.assertLess(latency, 500)
self.wait1(True)
# should have no failures, still up
- self.assertEqual(self.getBackendMetric(0, 'healthCheckFailures'), 0)
- self.assertEqual(self.getBackendStatus(), 'up')
- latency = self.getBackendMetric(0, 'healthCheckLatency')
+ self.assertEqual(self.getBackendMetric(0, "healthCheckFailures"), 0)
+ self.assertEqual(self.getBackendStatus(), "up")
+ latency = self.getBackendMetric(0, "healthCheckLatency")
# should be at least 500 ms
self.assertGreaterEqual(latency, 500)
self.setDelay(None)
-class TestServerStateChange(HealthCheckTest):
+class TestServerStateChange(HealthCheckTest):
_healthQueue = queue.Queue()
_dropHealthCheck = False
_config_template = """
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, cls.healthCallback])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, cls.healthCallback],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
@classmethod
def healthCallback(cls, request):
if cls._dropHealthCheck:
- cls._healthQueue.put(False)
- print("health check received drop")
- return ResponderDropAction()
+ cls._healthQueue.put(False)
+ print("health check received drop")
+ return ResponderDropAction()
response = dns.message.make_response(request)
cls._healthQueue.put(True)
print("health check received return")
time.sleep(1)
# server initial up shall have been hit
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendStatus(), "up")
self.assertEqual(self.getCount(nameAddr, True), 1)
self.assertEqual(self.getCount(nameAddr, False), 0)
self.setDrop(True)
time.sleep(2.5)
# up count did not change, down count increased by 1
- self.assertEqual(self.getBackendStatus(), 'down')
+ self.assertEqual(self.getBackendStatus(), "down")
self.assertEqual(self.getCount(nameAddr, True), 1)
self.assertEqual(self.getCount(nameAddr, False), 1)
self.setDrop(False)
time.sleep(1.5)
# up count increased again, down count did not change
- self.assertEqual(self.getBackendStatus(), 'up')
+ self.assertEqual(self.getBackendStatus(), "up")
self.assertEqual(self.getCount(nameAddr, True), 2)
self.assertEqual(self.getCount(nameAddr, False), 1)
import dns
from dnsdisttests import DNSDistTest
+
def get_loopback_itf():
interfaces = socket.if_nameindex()
for itf in interfaces:
- if itf[1] == 'lo':
- return 'lo'
+ if itf[1] == "lo":
+ return "lo"
return None
+
class TestIncomingInterface(DNSDistTest):
_lo_itf = get_loopback_itf()
_config_template = """
addResponseAction(AllRule(), LuaResponseAction(checkItfResponse))
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_lo_itf', '_dnsDistPort', '_testServerPort']
+ _config_params = ["_lo_itf", "_dnsDistPort", "_testServerPort"]
_skipListeningOnCL = True
def testItfName(self):
Advanced: Check incoming interface name
"""
if get_loopback_itf() is None:
- raise unittest.SkipTest('No lo interface')
+ raise unittest.SkipTest("No lo interface")
- name = 'incoming-interface.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "incoming-interface.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '4.3.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "4.3.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
+
class TestIncomingInterfaceNotSet(DNSDistTest):
_lo_itf = get_loopback_itf()
_config_template = """
addResponseAction(AllRule(), LuaResponseAction(checkItfResponse))
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_lo_itf', '_dnsDistPort', '_testServerPort']
+ _config_params = ["_lo_itf", "_dnsDistPort", "_testServerPort"]
_skipListeningOnCL = True
def testItfName(self):
Advanced: Check incoming interface name (not set)
"""
if get_loopback_itf() is None:
- raise unittest.SkipTest('No lo interface')
+ raise unittest.SkipTest("No lo interface")
- name = 'incoming-interface-not-set.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "incoming-interface-not-set.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.4')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
import dns
from dnsdisttests import DNSDistTest, pickAvailablePort
+
class IncomingProtocol:
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_dohWithNGHTTP2ServerPort = pickAvailablePort()
- _dohWithNGHTTP2BaseURL = ("https://%s:%d/dns-query" % (_serverName, _dohWithNGHTTP2ServerPort))
+ _dohWithNGHTTP2BaseURL = "https://%s:%d/dns-query" % (_serverName, _dohWithNGHTTP2ServerPort)
_doqServerPort = pickAvailablePort()
_doh3ServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doh3ServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doh3ServerPort)
def testIncomingProtocolRule(self):
"""
Incoming protocol
"""
- name = 'incoming-protocol.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "incoming-protocol.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
- for method in ["sendUDPQuery", "sendTCPQuery", "sendDOTQueryWrapper", "sendDOHWithNGHTTP2QueryWrapper", "sendDOQQueryWrapper", "sendDOH3QueryWrapper"]:
+ for method in [
+ "sendUDPQuery",
+ "sendTCPQuery",
+ "sendDOTQueryWrapper",
+ "sendDOHWithNGHTTP2QueryWrapper",
+ "sendDOQQueryWrapper",
+ "sendDOH3QueryWrapper",
+ ]:
sender = getattr(self, method)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- method + ".")
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.CNAME, method + ".")
expectedResponse.answer.append(rrset)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
- if method in ['sendDOQQueryWrapper', 'sendDOH3QueryWrapper']:
+ if method in ["sendDOQQueryWrapper", "sendDOH3QueryWrapper"]:
# dnspython sets the ID to 0
receivedResponse.id = expectedResponse.id
self.assertEqual(expectedResponse, receivedResponse)
+
class IncomingProtocolLuaConfig(DNSDistTest, IncomingProtocol):
_config_template = """
newServer{address="127.0.0.1:%d"}
addAction(IncomingProtocolRule("DoQ"), SpoofCNAMEAction("sendDOQQueryWrapper"))
addAction(IncomingProtocolRule("DoH3"), SpoofCNAMEAction("sendDOH3QueryWrapper"))
"""
- _config_params = ['_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_dohWithNGHTTP2ServerPort', '_serverCert', '_serverKey', '_doqServerPort', '_serverCert', '_serverKey', '_doh3ServerPort', '_serverCert', '_serverKey']
+ _config_params = [
+ "_testServerPort",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_dohWithNGHTTP2ServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doh3ServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
+
class IncomingProtocolYAMLConfig(DNSDistTest, IncomingProtocol):
_yaml_config_template = """
cname: "sendDOH3QueryWrapper"
"""
_config_params = []
- _yaml_config_params = ['_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_dohWithNGHTTP2ServerPort', '_serverCert', '_serverKey', '_doqServerPort', '_serverCert', '_serverKey', '_doh3ServerPort', '_serverCert', '_serverKey']
- _checkConfigExpectedOutput = b"DNS over HTTPS configured\nConfiguration 'configs/dnsdist_IncomingProtocolYAMLConfig.yml' OK!\n"
+ _yaml_config_params = [
+ "_testServerPort",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_dohWithNGHTTP2ServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doh3ServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
+ _checkConfigExpectedOutput = (
+ b"DNS over HTTPS configured\nConfiguration 'configs/dnsdist_IncomingProtocolYAMLConfig.yml' OK!\n"
+ )
from dnsdisttests import DNSDistTest
-@unittest.skipIf('SKIP_LMDB_TESTS' in os.environ, 'LMDB tests are disabled')
-class TestLMDB(DNSDistTest):
- _lmdbFileName = '/tmp/test-lmdb-db'
- _lmdbDBName = 'db-name'
+@unittest.skipIf("SKIP_LMDB_TESTS" in os.environ, "LMDB tests are disabled")
+class TestLMDB(DNSDistTest):
+ _lmdbFileName = "/tmp/test-lmdb-db"
+ _lmdbDBName = "db-name"
_config_template = """
newServer{address="127.0.0.1:%d"}
-- otherwise, spoof a different response
addAction(AllRule(), SpoofAction('9.9.9.9'))
"""
- _config_params = ['_testServerPort', '_lmdbFileName', '_lmdbDBName']
+ _config_params = ["_testServerPort", "_lmdbFileName", "_lmdbDBName"]
@classmethod
def setUpLMDB(cls):
- env = lmdb.open(cls._lmdbFileName, map_size=1014*1024, max_dbs=1024, subdir=False)
+ env = lmdb.open(cls._lmdbFileName, map_size=1014 * 1024, max_dbs=1024, subdir=False)
db = env.open_db(key=cls._lmdbDBName.encode())
with env.begin(db=db, write=True) as txn:
- txn.put(b'\x05qname\x04lmdb\x05tests\x08powerdns\x03com\x00', b'this is the value of the qname tag')
- txn.put(socket.inet_aton('127.0.0.1'), b'this is the value of the source address tag')
- txn.put(b'this is the value of the qname tag', b'this is the value of the second tag')
- txn.put(b'\x06suffix\x04lmdb\x05tests\x08powerdns\x03com\x00', b'this is the value of the suffix tag')
- txn.put(b'qname-plaintext.lmdb.tests.powerdns.com', b'this is the value of the plaintext tag')
- txn.put(b'kvs-rule.lmdb.tests.powerdns.com', b'the value does not matter')
+ txn.put(b"\x05qname\x04lmdb\x05tests\x08powerdns\x03com\x00", b"this is the value of the qname tag")
+ txn.put(socket.inet_aton("127.0.0.1"), b"this is the value of the source address tag")
+ txn.put(b"this is the value of the qname tag", b"this is the value of the second tag")
+ txn.put(b"\x06suffix\x04lmdb\x05tests\x08powerdns\x03com\x00", b"this is the value of the suffix tag")
+ txn.put(b"qname-plaintext.lmdb.tests.powerdns.com", b"this is the value of the plaintext tag")
+ txn.put(b"kvs-rule.lmdb.tests.powerdns.com", b"the value does not matter")
@classmethod
def setUpClass(cls):
"""
LMDB: Match on source address
"""
- name = 'source-ip.lmdb.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "source-ip.lmdb.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '5.6.7.8')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "5.6.7.8")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
LMDB: Match on qname then does a second lookup using the value of the first lookup
"""
- name = 'qname.lmdb.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "qname.lmdb.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.4')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
LMDB: Match on the qname via a suffix lookup
"""
- name = 'sub.sub.suffix.lmdb.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "sub.sub.suffix.lmdb.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '42.42.42.42')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "42.42.42.42")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
LMDB: Match on qname in plain text format
"""
- name = 'qname-plaintext.lmdb.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "qname-plaintext.lmdb.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '9.10.11.12')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "9.10.11.12")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
LMDB: KeyValueStoreLookupRule
"""
- name = 'kvs-rule.lmdb.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "kvs-rule.lmdb.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '13.14.15.16')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "13.14.15.16")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(receivedResponse)
self.assertEqual(expectedResponse, receivedResponse)
-class TestLMDBYaml(TestLMDB):
- _lmdbFileName = '/tmp/test-lmdb-db'
- _lmdbDBName = 'db-name'
+class TestLMDBYaml(TestLMDB):
+ _lmdbFileName = "/tmp/test-lmdb-db"
+ _lmdbDBName = "db-name"
_config_template = ""
_config_params = []
_yaml_config_template = """---
ips:
- "9.9.9.9"
"""
- _yaml_config_params = ['_testServerPort', '_lmdbFileName', '_lmdbDBName']
+ _yaml_config_params = ["_testServerPort", "_lmdbFileName", "_lmdbDBName"]
-class TestLMDBIPInRange(DNSDistTest):
- _lmdbFileName = '/tmp/test-lmdb-range-1-db'
- _lmdbDBName = 'db-name'
+class TestLMDBIPInRange(DNSDistTest):
+ _lmdbFileName = "/tmp/test-lmdb-range-1-db"
+ _lmdbDBName = "db-name"
_config_template = """
newServer{address="127.0.0.1:%d"}
-- otherwise, spoof a different response
addAction(AllRule(), SpoofAction('9.9.9.9'))
"""
- _config_params = ['_testServerPort', '_lmdbFileName', '_lmdbDBName']
+ _config_params = ["_testServerPort", "_lmdbFileName", "_lmdbDBName"]
@classmethod
def setUpLMDB(cls):
- env = lmdb.open(cls._lmdbFileName, map_size=1014*1024, max_dbs=1024, subdir=False)
+ env = lmdb.open(cls._lmdbFileName, map_size=1014 * 1024, max_dbs=1024, subdir=False)
db = env.open_db(key=cls._lmdbDBName.encode())
with env.begin(db=db, write=True) as txn:
- txn.put(socket.inet_aton('127.255.255.255') + struct.pack("!H", 255), socket.inet_aton('127.0.0.0') + struct.pack("!H", 0) + b'this is the value of the source address tag')
+ txn.put(
+ socket.inet_aton("127.255.255.255") + struct.pack("!H", 255),
+ socket.inet_aton("127.0.0.0") + struct.pack("!H", 0) + b"this is the value of the source address tag",
+ )
@classmethod
def setUpClass(cls):
"""
LMDB range: Match on source address
"""
- name = 'source-ip.lmdb-range.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "source-ip.lmdb-range.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '5.6.7.8')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "5.6.7.8")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(receivedResponse)
self.assertEqual(expectedResponse, receivedResponse)
-class TestLMDBIPNotInRange(DNSDistTest):
- _lmdbFileName = '/tmp/test-lmdb-range-2-db'
- _lmdbDBName = 'db-name'
+class TestLMDBIPNotInRange(DNSDistTest):
+ _lmdbFileName = "/tmp/test-lmdb-range-2-db"
+ _lmdbDBName = "db-name"
_config_template = """
newServer{address="127.0.0.1:%d"}
-- otherwise, spoof a different response
addAction(AllRule(), SpoofAction('9.9.9.9'))
"""
- _config_params = ['_testServerPort', '_lmdbFileName', '_lmdbDBName']
+ _config_params = ["_testServerPort", "_lmdbFileName", "_lmdbDBName"]
@classmethod
def setUpLMDB(cls):
- env = lmdb.open(cls._lmdbFileName, map_size=1014*1024, max_dbs=1024, subdir=False)
+ env = lmdb.open(cls._lmdbFileName, map_size=1014 * 1024, max_dbs=1024, subdir=False)
db = env.open_db(key=cls._lmdbDBName.encode())
with env.begin(db=db, write=True) as txn:
- txn.put(socket.inet_aton('127.0.0.0') + struct.pack("!H", 255), socket.inet_aton('127.0.0.0') + struct.pack("!H", 0) + b'this is the value of the source address tag')
+ txn.put(
+ socket.inet_aton("127.0.0.0") + struct.pack("!H", 255),
+ socket.inet_aton("127.0.0.0") + struct.pack("!H", 0) + b"this is the value of the source address tag",
+ )
@classmethod
def setUpClass(cls):
"""
LMDB not in range: Match on source address
"""
- name = 'source-ip.lmdb-not-in-range.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "source-ip.lmdb-not-in-range.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '9.9.9.9')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "9.9.9.9")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
import time
from dnsdisttests import DNSDistTest
+
class TestLuaThread(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _config_params = ['_consoleKeyB64', '_consolePort']
+ _config_params = ["_consoleKeyB64", "_consolePort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
"""
LuaThread: Test the lua newThread interface
"""
- count1 = self.sendConsoleCommand('counter')
+ count1 = self.sendConsoleCommand("counter")
time.sleep(3)
- count2 = self.sendConsoleCommand('counter')
+ count2 = self.sendConsoleCommand("counter")
self.assertGreater(count2, count1)
+
class TestLuaDNSHeaderBindings(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
"""
LuaDNSHeaders: TC
"""
- name = 'notset.check-tc.lua-dnsheaders.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "notset.check-tc.lua-dnsheaders.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'tc-not-set.check-tc.lua-dnsheaders.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name, 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "tc-not-set.check-tc.lua-dnsheaders.tests.powerdns.com."
+ )
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(response, receivedResponse)
- name = 'set.check-tc.lua-dnsheaders.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "set.check-tc.lua-dnsheaders.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
query.flags |= dns.flags.TC
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
+
class TestLuaFrontendBindings(DNSDistTest):
_config_template = """
setStructuredLogging(false)
"""
LuaFrontendBindings: Test Lua frontend bindings
"""
- name = 'basic.lua-frontend-bindings.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "basic.lua-frontend-bindings.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
+
class TestLuaPoolBindings(DNSDistTest):
_config_template = """
local serverPort = %d
"""
LuaPoolBindings: Test Lua pool bindings
"""
- name = 'basic.lua-pool-bindings.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "basic.lua-pool-bindings.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
(_, receivedResponse) = sender(query, response=response)
self.assertEqual(receivedResponse, response)
+
class TestLuaError(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _config_params = ['_consoleKeyB64', '_consolePort']
+ _config_params = ["_consoleKeyB64", "_consolePort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
LuaError: Test exception handling while debug module is obscured
"""
res = self.sendConsoleCommand('error("expected" .. " " .. "error")')
- self.assertIn('expected error', res)
+ self.assertIn("expected error", res)
import dns
from dnsdisttests import DNSDistTest
-class TestAdvancedLuaFFI(DNSDistTest):
+class TestAdvancedLuaFFI(DNSDistTest):
_config_template = """
local ffi = require("ffi")
"""
Lua FFI: Test the Lua FFI interface
"""
- name = 'luaffi.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "luaffi.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Lua FFI: Test the Lua FFI interface via an update
"""
- name = 'luaffi.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'SOA', 'IN')
+ name = "luaffi.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "SOA", "IN")
query.set_opcode(dns.opcode.UPDATE)
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
-class TestAdvancedLuaFFIPerThread(DNSDistTest):
+class TestAdvancedLuaFFIPerThread(DNSDistTest):
_config_template = """
local rulefunction = [[
"""
Lua FFI: Test the Lua FFI per-thread interface
"""
- name = 'luaffiperthread.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "luaffiperthread.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Lua FFI: Test the Lua FFI per-thread interface via an update
"""
- name = 'luaffiperthread.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'SOA', 'IN')
+ name = "luaffiperthread.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "SOA", "IN")
query.set_opcode(dns.opcode.UPDATE)
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
-class TestLuaFFIHeader(DNSDistTest):
+class TestLuaFFIHeader(DNSDistTest):
_config_template = """
local bit = require("bit")
local ffi = require("ffi")
"""
Lua FFI: Set AA=1
"""
- name = 'dnsheader-set-aa.luaffi.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "dnsheader-set-aa.luaffi.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
expectedResponse.flags |= dns.flags.AA
"""
Lua FFI: check AA=0, return REFUSED otherwise
"""
- name = 'dnsheader-get-aa.luaffi.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "dnsheader-get-aa.luaffi.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
response.flags |= dns.flags.AA
expectedResponse = dns.message.make_response(query)
self.assertEqual(query, receivedQuery)
self.assertEqual(expectedResponse, receivedResponse)
-class TestLuaFFISetAlternateName(DNSDistTest):
+class TestLuaFFISetAlternateName(DNSDistTest):
_config_template = """
local ffi = require("ffi")
"""
Lua FFI: Set alternate name
"""
- name = 'alternate-name.luaffi.tests.powerdns.com.'
- alternateName = 'dnsdist.org.'
- query = dns.message.make_query(name, 'A', 'IN')
- alternateQuery = dns.message.make_query(alternateName, 'A', 'IN')
+ name = "alternate-name.luaffi.tests.powerdns.com."
+ alternateName = "dnsdist.org."
+ query = dns.message.make_query(name, "A", "IN")
+ alternateQuery = dns.message.make_query(alternateName, "A", "IN")
response = dns.message.make_response(alternateQuery)
- rrset = dns.rrset.from_text(alternateName,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(alternateName, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
import dns
from dnsdisttests import DNSDistTest, pickAvailablePort
-class RuleMetricsTest(object):
+class RuleMetricsTest(object):
_config_template = """
addTLSLocal("127.0.0.1:%d", "%s", "%s", { provider="openssl" })
addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/"})
"""
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
- _webServerAPIKey = 'apisecret'
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _webServerAPIKey = "apisecret"
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_dohServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
- _config_params = ['_tlsServerPort', '_serverCert', '_serverKey', '_dohServerPort', '_serverCert', '_serverKey', '_testServerPort', '_webServerPort', '_webServerAPIKeyHashed']
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort)
+ _config_params = [
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_dohServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_testServerPort",
+ "_webServerPort",
+ "_webServerAPIKeyHashed",
+ ]
@classmethod
def setUpClass(cls):
cls.startResponders()
cls.startDNSDist()
cls.setUpSockets()
- cls.waitForTCPSocket('127.0.0.1', cls._webServerPort)
+ cls.waitForTCPSocket("127.0.0.1", cls._webServerPort)
print("Launching tests..")
def getMetric(self, name):
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- stats = content['statistics']
+ stats = content["statistics"]
self.assertIn(name, stats)
return int(stats[name])
def getPoolMetric(self, poolName, metricName):
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/pool?name=' + poolName
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost/pool?name=" + poolName
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- stats = content['stats']
+ stats = content["stats"]
self.assertIn(metricName, stats)
return int(stats[metricName])
"""
Metrics: Check that metrics are correctly updated for RCodeAction
"""
- rcodes = [
- ( 'nxdomain', dns.rcode.NXDOMAIN ),
- ( 'refused', dns.rcode.REFUSED ),
- ( 'servfail', dns.rcode.SERVFAIL )
- ]
- servfailBackendResponses = self.getMetric('servfail-responses')
-
- for (name, rcode) in rcodes:
- qname = 'rcode-' + name + '.metrics.tests.powerdns.com.'
- query = dns.message.make_query(qname, 'A', 'IN')
+ rcodes = [("nxdomain", dns.rcode.NXDOMAIN), ("refused", dns.rcode.REFUSED), ("servfail", dns.rcode.SERVFAIL)]
+ servfailBackendResponses = self.getMetric("servfail-responses")
+
+ for name, rcode in rcodes:
+ qname = "rcode-" + name + ".metrics.tests.powerdns.com."
+ query = dns.message.make_query(qname, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(rcode)
- ruleMetricBefore = self.getMetric('rule-' + name)
- if name != 'refused':
- frontendMetricBefore = self.getMetric('frontend-' + name)
+ ruleMetricBefore = self.getMetric("rule-" + name)
+ if name != "refused":
+ frontendMetricBefore = self.getMetric("frontend-" + name)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
- self.assertEqual(self.getMetric('rule-' + name), ruleMetricBefore + 2)
- if name != 'refused':
- self.assertEqual(self.getMetric('frontend-' + name), frontendMetricBefore + 2)
+ self.assertEqual(self.getMetric("rule-" + name), ruleMetricBefore + 2)
+ if name != "refused":
+ self.assertEqual(self.getMetric("frontend-" + name), frontendMetricBefore + 2)
# self-generated responses should not increase this metric
- self.assertEqual(self.getMetric('servfail-responses'), servfailBackendResponses)
+ self.assertEqual(self.getMetric("servfail-responses"), servfailBackendResponses)
def testCacheMetrics(self):
"""
"""
for method in ("sendUDPQuery", "sendTCPQuery", "sendDOTQueryWrapper", "sendDOHQueryWrapper"):
- qname = method + '.cache.metrics.tests.powerdns.com.'
- query = dns.message.make_query(qname, 'A', 'IN')
+ qname = method + ".cache.metrics.tests.powerdns.com."
+ query = dns.message.make_query(qname, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(qname,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(qname, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- responsesBefore = self.getMetric('responses')
- cacheHitsBefore = self.getMetric('cache-hits')
- cacheMissesBefore = self.getMetric('cache-misses')
- poolCacheHitsBefore = self.getPoolMetric('cache', 'cacheHits')
- poolCacheMissesBefore = self.getPoolMetric('cache', 'cacheMisses')
+ responsesBefore = self.getMetric("responses")
+ cacheHitsBefore = self.getMetric("cache-hits")
+ cacheMissesBefore = self.getMetric("cache-misses")
+ poolCacheHitsBefore = self.getPoolMetric("cache", "cacheHits")
+ poolCacheMissesBefore = self.getPoolMetric("cache", "cacheMisses")
sender = getattr(self, method)
# first time, cache miss
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
- self.assertEqual(self.getMetric('responses'), responsesBefore + 2)
- self.assertEqual(self.getMetric('cache-hits'), cacheHitsBefore + 1)
- self.assertEqual(self.getMetric('cache-misses'), cacheMissesBefore + 1)
- self.assertEqual(self.getPoolMetric('cache', 'cacheHits'), poolCacheHitsBefore + 1)
- self.assertEqual(self.getPoolMetric('cache', 'cacheMisses'), poolCacheMissesBefore + 1)
+ self.assertEqual(self.getMetric("responses"), responsesBefore + 2)
+ self.assertEqual(self.getMetric("cache-hits"), cacheHitsBefore + 1)
+ self.assertEqual(self.getMetric("cache-misses"), cacheMissesBefore + 1)
+ self.assertEqual(self.getPoolMetric("cache", "cacheHits"), poolCacheHitsBefore + 1)
+ self.assertEqual(self.getPoolMetric("cache", "cacheMisses"), poolCacheMissesBefore + 1)
def testServFailMetrics(self):
"""
"""
for method in ("sendUDPQuery", "sendTCPQuery", "sendDOTQueryWrapper", "sendDOHQueryWrapper"):
- qname = method + '.servfail.cache.metrics.tests.powerdns.com.'
- query = dns.message.make_query(qname, 'A', 'IN')
+ qname = method + ".servfail.cache.metrics.tests.powerdns.com."
+ query = dns.message.make_query(qname, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.SERVFAIL)
- frontendBefore = self.getMetric('frontend-servfail')
- servfailBefore = self.getMetric('servfail-responses')
- ruleBefore = self.getMetric('rule-servfail')
+ frontendBefore = self.getMetric("frontend-servfail")
+ servfailBefore = self.getMetric("servfail-responses")
+ ruleBefore = self.getMetric("rule-servfail")
sender = getattr(self, method)
# first time, cache miss
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, response)
- self.assertEqual(self.getMetric('frontend-servfail'), frontendBefore + 2)
- self.assertEqual(self.getMetric('servfail-responses'), servfailBefore + 1)
- self.assertEqual(self.getMetric('rule-servfail'), ruleBefore)
+ self.assertEqual(self.getMetric("frontend-servfail"), frontendBefore + 2)
+ self.assertEqual(self.getMetric("servfail-responses"), servfailBefore + 1)
+ self.assertEqual(self.getMetric("rule-servfail"), ruleBefore)
+
class TestRuleMetricsDefault(RuleMetricsTest, DNSDistTest):
pass
+
class TestRuleMetricsRecvmmsg(RuleMetricsTest, DNSDistTest):
# test the metrics with recvmmsg/sendmmsg support enabled as well
- _config_template = RuleMetricsTest._config_template + """
+ _config_template = (
+ RuleMetricsTest._config_template
+ + """
setUDPMultipleMessagesVectorSize(10)
"""
+ )
import os
import subprocess
-class TestNetworkEndpointConfig(unittest.TestCase):
+class TestNetworkEndpointConfig(unittest.TestCase):
def checkDNSDistExitCode(self, configTemplate, expectedCode, clientMode=False, verboseMode=False):
- conffile = 'configs/dnsdist_TestNetworkEndpointConfig.conf'
- with open(conffile, 'w') as conf:
+ conffile = "configs/dnsdist_TestNetworkEndpointConfig.conf"
+ with open(conffile, "w") as conf:
conf.write("-- Autogenerated by dnsdisttests.py\n")
conf.write(configTemplate)
- dnsdistcmd = [os.environ['DNSDISTBIN'], '-C', conffile, '--check-config']
+ dnsdistcmd = [os.environ["DNSDISTBIN"], "-C", conffile, "--check-config"]
if clientMode:
- dnsdistcmd.append('-c')
+ dnsdistcmd.append("-c")
if verboseMode:
- dnsdistcmd.append('-v')
+ dnsdistcmd.append("-v")
output = None
returnCode = None
class DNSDistOCSPStaplingTest(DNSDistTest):
-
@classmethod
def checkOCSPStaplingStatus(cls, addr, port, serverName, caFile):
testcmd = [
)
output = process.communicate(input="")
except subprocess.CalledProcessError as exc:
- raise AssertionError(
- "openssl s_client failed (%d): %s" % (exc.returncode, exc.output)
- )
+ raise AssertionError("openssl s_client failed (%d): %s" % (exc.returncode, exc.output))
return output[0].decode()
@unittest.skipIf("SKIP_DOH_TESTS" in os.environ, "DNS over HTTPS tests are disabled")
class TestOCSPStaplingDOH(DNSDistOCSPStaplingTest):
-
_consoleKey = DNSDistTest.generateConsoleKey()
_consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_serverKey = "server-ocsp.key"
OCSP Stapling: DOH
"""
for port in [self._dohWithNGHTTP2ServerPort]:
- output = self.checkOCSPStaplingStatus(
- "127.0.0.1", port, self._serverName, self._caCert
- )
+ output = self.checkOCSPStaplingStatus("127.0.0.1", port, self._serverName, self._caCert)
self.assertIn("OCSP Response Status: successful (0x0)", output)
serialNumber = self.getOCSPSerial(output)
)
self.sendConsoleCommand("reloadAllCertificates()")
- output = self.checkOCSPStaplingStatus(
- "127.0.0.1", port, self._serverName, self._caCert
- )
+ output = self.checkOCSPStaplingStatus("127.0.0.1", port, self._serverName, self._caCert)
self.assertIn("OCSP Response Status: successful (0x0)", output)
serialNumber2 = self.getOCSPSerial(output)
self.assertTrue(serialNumber2)
class TestBrokenOCSPStaplingDoH(DNSDistOCSPStaplingTest):
-
_consoleKey = DNSDistTest.generateConsoleKey()
_consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_serverKey = "server-ocsp.key"
OCSP Stapling: Broken (DoH)
"""
for port in [self._dohWithNGHTTP2ServerPort]:
- output = self.checkOCSPStaplingStatus(
- "127.0.0.1", port, self._serverName, self._caCert
- )
+ output = self.checkOCSPStaplingStatus("127.0.0.1", port, self._serverName, self._caCert)
self.assertNotIn("OCSP Response Status: successful (0x0)", output)
class TestOCSPStaplingTLSGnuTLS(DNSDistOCSPStaplingTest):
-
_consoleKey = DNSDistTest.generateConsoleKey()
_consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_serverKey = "server-ocsp.key"
"""
OCSP Stapling: TLS (GnuTLS)
"""
- output = self.checkOCSPStaplingStatus(
- "127.0.0.1", self._tlsServerPort, self._serverName, self._caCert
- )
+ output = self.checkOCSPStaplingStatus("127.0.0.1", self._tlsServerPort, self._serverName, self._caCert)
self.assertIn("OCSP Response Status: successful (0x0)", output)
self.assertEqual(self.getTLSProvider(), "gnutls")
)
self.sendConsoleCommand("reloadAllCertificates()")
- output = self.checkOCSPStaplingStatus(
- "127.0.0.1", self._tlsServerPort, self._serverName, self._caCert
- )
+ output = self.checkOCSPStaplingStatus("127.0.0.1", self._tlsServerPort, self._serverName, self._caCert)
self.assertIn("OCSP Response Status: successful (0x0)", output)
serialNumber2 = self.getOCSPSerial(output)
self.assertTrue(serialNumber2)
class TestBrokenOCSPStaplingTLSGnuTLS(DNSDistOCSPStaplingTest):
-
_consoleKey = DNSDistTest.generateConsoleKey()
_consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_serverKey = "server-ocsp.key"
"""
OCSP Stapling: Broken (GnuTLS)
"""
- output = self.checkOCSPStaplingStatus(
- "127.0.0.1", self._tlsServerPort, self._serverName, self._caCert
- )
+ output = self.checkOCSPStaplingStatus("127.0.0.1", self._tlsServerPort, self._serverName, self._caCert)
self.assertNotIn("OCSP Response Status: successful (0x0)", output)
self.assertEqual(self.getTLSProvider(), "gnutls")
class TestOCSPStaplingTLSOpenSSL(DNSDistOCSPStaplingTest):
-
_consoleKey = DNSDistTest.generateConsoleKey()
_consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_serverKey = "server-ocsp.key"
"""
OCSP Stapling: TLS (OpenSSL)
"""
- output = self.checkOCSPStaplingStatus(
- "127.0.0.1", self._tlsServerPort, self._serverName, self._caCert
- )
+ output = self.checkOCSPStaplingStatus("127.0.0.1", self._tlsServerPort, self._serverName, self._caCert)
self.assertIn("OCSP Response Status: successful (0x0)", output)
self.assertEqual(self.getTLSProvider(), "openssl")
)
self.sendConsoleCommand("reloadAllCertificates()")
- output = self.checkOCSPStaplingStatus(
- "127.0.0.1", self._tlsServerPort, self._serverName, self._caCert
- )
+ output = self.checkOCSPStaplingStatus("127.0.0.1", self._tlsServerPort, self._serverName, self._caCert)
self.assertIn("OCSP Response Status: successful (0x0)", output)
serialNumber2 = self.getOCSPSerial(output)
self.assertTrue(serialNumber2)
class TestBrokenOCSPStaplingTLSOpenSSL(DNSDistOCSPStaplingTest):
-
_consoleKey = DNSDistTest.generateConsoleKey()
_consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_serverKey = "server-ocsp.key"
"""
OCSP Stapling: Broken (OpenSSL)
"""
- output = self.checkOCSPStaplingStatus(
- "127.0.0.1", self._tlsServerPort, self._serverName, self._caCert
- )
+ output = self.checkOCSPStaplingStatus("127.0.0.1", self._tlsServerPort, self._serverName, self._caCert)
self.assertNotIn("OCSP Response Status: successful (0x0)", output)
self.assertEqual(self.getTLSProvider(), "openssl")
import threading
from dnsdisttests import DNSDistTest, pickAvailablePort
-class OOORTCPResponder(object):
+class OOORTCPResponder(object):
def handleConnection(self, conn):
try:
-
while True:
try:
data = conn.recv(2)
# computing the correct ID for the response
request = dns.message.from_wire(data)
- #print("got a query for %s" % (request.question[0].name))
+ # print("got a query for %s" % (request.question[0].name))
if request.question[0].name == "0.simple.ooor.tests.powerdns.com":
time.sleep(1)
conn.settimeout(5.0)
OOORTCPResponder.numberOfConnections = OOORTCPResponder.numberOfConnections + 1
- thread = threading.Thread(name='Connection Handler',
- target=self.handleConnection,
- args=[conn])
+ thread = threading.Thread(name="Connection Handler", target=self.handleConnection, args=[conn])
thread.daemon = True
thread.start()
finally:
sock.close()
-class ReverseOOORTCPResponder(OOORTCPResponder):
+class ReverseOOORTCPResponder(OOORTCPResponder):
def handleConnection(self, conn):
try:
# short timeout since we want to answer only after receiving 5 requests
# computing the correct ID for the response
request = dns.message.from_wire(data)
- #print("got a query for %s" % (request.question[0].name))
+ # print("got a query for %s" % (request.question[0].name))
response = dns.message.make_response(request)
queuedResponses.append(response)
(conn, _) = sock.accept()
ReverseOOORTCPResponder.numberOfConnections = ReverseOOORTCPResponder.numberOfConnections + 1
- thread = threading.Thread(name='Connection Handler',
- target=self.handleConnection,
- args=[conn])
+ thread = threading.Thread(name="Connection Handler", target=self.handleConnection, args=[conn])
thread.daemon = True
thread.start()
OOORResponderPort = pickAvailablePort()
-ooorTCPResponder = threading.Thread(name='TCP Responder', target=OOORTCPResponder, args=[OOORResponderPort])
+ooorTCPResponder = threading.Thread(name="TCP Responder", target=OOORTCPResponder, args=[OOORResponderPort])
ooorTCPResponder.daemon = True
ooorTCPResponder.start()
ReverseOOORResponderPort = pickAvailablePort()
-ReverseOoorTCPResponder = threading.Thread(name='TCP Responder', target=ReverseOOORTCPResponder, args=[ReverseOOORResponderPort])
+ReverseOoorTCPResponder = threading.Thread(
+ name="TCP Responder", target=ReverseOOORTCPResponder, args=[ReverseOOORResponderPort]
+)
ReverseOoorTCPResponder.daemon = True
ReverseOoorTCPResponder.start()
+
class TestOOORWithClientNotBackend(DNSDistTest):
# this test suite uses a different responder port
_testServerPort = OOORResponderPort
addAction("more-queries.ooor.tests.powerdns.com.", PoolAction("more-queries"))
setLocal("%s:%d", {maxInFlight=%d})
"""
- _config_params = ['_testServerPort', '_testServerPort', '_dnsDistListeningAddr', '_dnsDistPort', '_concurrentQueriesFromClient']
+ _config_params = [
+ "_testServerPort",
+ "_testServerPort",
+ "_dnsDistListeningAddr",
+ "_dnsDistPort",
+ "_concurrentQueriesFromClient",
+ ]
_verboseMode = True
_skipListeningOnCL = True
OOORTCPResponder.numberOfConnections = 0
for idx in range(5):
- names.append('%d.simple.ooor.tests.powerdns.com.' % (idx))
+ names.append("%d.simple.ooor.tests.powerdns.com." % (idx))
conn = self.openTCPConnection()
counter = 0
for name in names:
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = counter
counter = counter + 1
for _ in names:
receivedResponse = self.recvTCPResponseOverConnection(conn)
self.assertTrue(receivedResponse)
- receivedResponses[str(receivedResponse.question[0].name)] = (receivedResponse)
+ receivedResponses[str(receivedResponse.question[0].name)] = receivedResponse
self.assertEqual(len(receivedResponses), 5)
for idx in range(5):
- self.assertIn('%d.simple.ooor.tests.powerdns.com.' % (idx), receivedResponses)
+ self.assertIn("%d.simple.ooor.tests.powerdns.com." % (idx), receivedResponses)
# we can get a response to one of the first query before they all have
# been read, reusing a backend connection
OOORTCPResponder.numberOfConnections = 0
for idx in range(100):
- names.append('%d.more-queries.ooor.tests.powerdns.com.' % (idx))
+ names.append("%d.more-queries.ooor.tests.powerdns.com." % (idx))
conn = self.openTCPConnection()
counter = 0
for name in names:
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = counter
counter = counter + 1
for _ in names:
receivedResponse = self.recvTCPResponseOverConnection(conn)
self.assertTrue(receivedResponse)
- receivedResponses[str(receivedResponse.question[0].name)] = (receivedResponse)
+ receivedResponses[str(receivedResponse.question[0].name)] = receivedResponse
self.assertEqual(len(receivedResponses), 100)
for idx in range(5):
- self.assertIn('%d.more-queries.ooor.tests.powerdns.com.' % (idx), receivedResponses)
+ self.assertIn("%d.more-queries.ooor.tests.powerdns.com." % (idx), receivedResponses)
self.assertLessEqual(OOORTCPResponder.numberOfConnections, self._concurrentQueriesFromClient)
+
class TestOOORWithClientAndBackend(DNSDistTest):
# this test suite uses a different responder port
_testServerPort = ReverseOOORResponderPort
addAction("more-queries.reverse-ooor.tests.powerdns.com.", PoolAction("more-queries"))
setLocal("%s:%d", {maxInFlight=%d})
"""
- _config_params = ['_testServerPort', '_concurrentQueriesToServer', '_testServerPort', '_concurrentQueriesToServer', '_dnsDistListeningAddr', '_dnsDistPort', '_concurrentQueriesFromClient']
+ _config_params = [
+ "_testServerPort",
+ "_concurrentQueriesToServer",
+ "_testServerPort",
+ "_concurrentQueriesToServer",
+ "_dnsDistListeningAddr",
+ "_dnsDistPort",
+ "_concurrentQueriesFromClient",
+ ]
_verboseMode = True
_skipListeningOnCL = True
ReverseOOORTCPResponder.numberOfConnections = 0
for idx in range(5):
- names.append('%d.simple.reverse-ooor.tests.powerdns.com.' % (idx))
+ names.append("%d.simple.reverse-ooor.tests.powerdns.com." % (idx))
conn = self.openTCPConnection()
counter = 0
for name in names:
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = counter
counter = counter + 1
for _ in names:
receivedResponse = self.recvTCPResponseOverConnection(conn)
self.assertTrue(receivedResponse)
- receivedResponses[str(receivedResponse.question[0].name)] = (receivedResponse)
+ receivedResponses[str(receivedResponse.question[0].name)] = receivedResponse
self.assertEqual(len(receivedResponses), 5)
for idx in range(5):
- self.assertIn('%d.simple.reverse-ooor.tests.powerdns.com.' % (idx), receivedResponses)
+ self.assertIn("%d.simple.reverse-ooor.tests.powerdns.com." % (idx), receivedResponses)
self.assertEqual(ReverseOOORTCPResponder.numberOfConnections, 1)
ReverseOOORTCPResponder.numberOfConnections = 0
for idx in range(100):
- names.append('%d.more-queries.reverse-ooor.tests.powerdns.com.' % (idx))
+ names.append("%d.more-queries.reverse-ooor.tests.powerdns.com." % (idx))
conn = self.openTCPConnection()
counter = 0
for name in names:
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = counter
counter = counter + 1
for _ in names:
receivedResponse = self.recvTCPResponseOverConnection(conn)
self.assertTrue(receivedResponse)
- receivedResponses[str(receivedResponse.question[0].name)] = (receivedResponse)
- #print("Received a response for %s" % (receivedResponse.question[0].name))
+ receivedResponses[str(receivedResponse.question[0].name)] = receivedResponse
+ # print("Received a response for %s" % (receivedResponse.question[0].name))
self.assertEqual(len(receivedResponses), 100)
for idx in range(5):
- self.assertIn('%d.more-queries.reverse-ooor.tests.powerdns.com.' % (idx), receivedResponses)
+ self.assertIn("%d.more-queries.reverse-ooor.tests.powerdns.com." % (idx), receivedResponses)
# in theory they could all be handled by the same backend if we get the responses
# fast enough, but over 100 queries that's very, very unlikely
if spanID != "":
ottrace.data += binascii.a2b_hex(spanID)
ottrace.data += b"\x00" # flags
- query = dns.message.make_query(
- name, "A", "IN", use_edns=True, options=[ottrace]
- )
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ottrace])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(
- name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target
- )
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(
- target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1"
- )
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
if useTCP:
# Ensure the values are correct
# TODO: query.remote with port
- msg_scope_attr_keys = [
- v["key"]
- for v in otData["resource_spans"][0]["scope_spans"][0]["scope"][
- "attributes"
- ]
- ]
+ msg_scope_attr_keys = [v["key"] for v in otData["resource_spans"][0]["scope_spans"][0]["scope"]["attributes"]]
self.assertListEqual(msg_scope_attr_keys, ["instance"])
root_span_attr_keys = [
- v["key"]
- for v in otData["resource_spans"][0]["scope_spans"][0]["spans"][0][
- "attributes"
- ]
+ v["key"] for v in otData["resource_spans"][0]["scope_spans"][0]["spans"][0]["attributes"]
]
self.assertListEqual(
root_span_attr_keys,
# No way to guess the test port, but check the rest of the values
root_span_attrs = {
v["key"]: v["value"]["string_value"]
- for v in otData["resource_spans"][0]["scope_spans"][0]["spans"][0][
- "attributes"
- ]
+ for v in otData["resource_spans"][0]["scope_spans"][0]["spans"][0]["attributes"]
if v["key"] not in ["query.remote.port"]
}
self.assertDictEqual(
root_span_attrs,
)
- msg_span_name = {
- v["name"] for v in otData["resource_spans"][0]["scope_spans"][0]["spans"]
- }
+ msg_span_name = {v["name"] for v in otData["resource_spans"][0]["scope_spans"][0]["spans"]}
funcs = {
"processQuery",
self.assertTrue(msg.HasField("openTelemetryData"))
traces_data = opentelemetry.proto.trace.v1.trace_pb2.TracesData()
traces_data.ParseFromString(msg.openTelemetryData)
- ot_data = google.protobuf.json_format.MessageToDict(
- traces_data, preserving_proto_field_name=True
- )
+ ot_data = google.protobuf.json_format.MessageToDict(traces_data, preserving_proto_field_name=True)
- self.checkOTData(
- ot_data, hasProcessResponseAfterRules, useTCP, extraFunctions=extraFunctions
- )
+ self.checkOTData(ot_data, hasProcessResponseAfterRules, useTCP, extraFunctions=extraFunctions)
traceId = base64.b64encode(msg.openTelemetryTraceID).decode()
for msg_span in ot_data["resource_spans"][0]["scope_spans"][0]["spans"]:
self.assertFalse(msg.HasField("openTelemetryData"))
-class DNSDistOpenTelemetryProtobufEnabledButUnsetYAML(
- DNSDistOpenTelemetryProtobufNoOTDataTest
-):
+class DNSDistOpenTelemetryProtobufEnabledButUnsetYAML(DNSDistOpenTelemetryProtobufNoOTDataTest):
_yaml_config_params = ["_testServerPort", "_protobufServerPort"]
_yaml_config_template = """---
self.doTest()
-class DNSDistOpenTelemetryProtobufEnabledButUnsetLua(
- DNSDistOpenTelemetryProtobufNoOTDataTest
-):
+class DNSDistOpenTelemetryProtobufEnabledButUnsetLua(DNSDistOpenTelemetryProtobufNoOTDataTest):
_config_params = ["_testServerPort", "_protobufServerPort"]
_config_template = """
newServer{address="127.0.0.1:%d"}
self.doTest()
-class DNSDistOpenTelemetryProtobufEnabledSetButTurnedOffYAML(
- DNSDistOpenTelemetryProtobufNoOTDataTest
-):
+class DNSDistOpenTelemetryProtobufEnabledSetButTurnedOffYAML(DNSDistOpenTelemetryProtobufNoOTDataTest):
"""Here we turn tracing on for the query, only to disable it after that"""
_yaml_config_params = ["_testServerPort", "_protobufServerPort"]
self.doTest()
-class DNSDistOpenTelemetryProtobufEnabledSetButTurnedOffLua(
- DNSDistOpenTelemetryProtobufNoOTDataTest
-):
+class DNSDistOpenTelemetryProtobufEnabledSetButTurnedOffLua(DNSDistOpenTelemetryProtobufNoOTDataTest):
_config_params = ["_testServerPort", "_protobufServerPort"]
_config_template = """
newServer{address="127.0.0.1:%d"}
self.doTest()
-class TestOpenTelemetryTracingBaseYAMLIncludedRemoteLoggerDropped(
- DNSDistOpenTelemetryProtobufTest
-):
+class TestOpenTelemetryTracingBaseYAMLIncludedRemoteLoggerDropped(DNSDistOpenTelemetryProtobufTest):
_yaml_config_params = [
"_testServerPort",
"_protobufServerPort",
msg = self.sendQueryAndGetProtobuf(useTCP=useTCP, dropped=True)
traces_data = opentelemetry.proto.trace.v1.trace_pb2.TracesData()
traces_data.ParseFromString(msg.openTelemetryData)
- ot_data = google.protobuf.json_format.MessageToDict(
- traces_data, preserving_proto_field_name=True
- )
+ ot_data = google.protobuf.json_format.MessageToDict(traces_data, preserving_proto_field_name=True)
funcs = extraFunctions.union(
{
"""
-class TestOpenTelemetryTracingBaseYAMLIncludedRemoteLoggerSpoofed(
- DNSDistOpenTelemetryProtobufTest
-):
+class TestOpenTelemetryTracingBaseYAMLIncludedRemoteLoggerSpoofed(DNSDistOpenTelemetryProtobufTest):
_yaml_config_params = [
"_testServerPort",
"_protobufServerPort",
"""
def doTest(self, useTCP=False, extraFunctions=set()):
- msg = self.sendQueryAndGetProtobuf(
- useTCP=useTCP, querySentByDNSDist=False, dropped=True
- )
+ msg = self.sendQueryAndGetProtobuf(useTCP=useTCP, querySentByDNSDist=False, dropped=True)
traces_data = opentelemetry.proto.trace.v1.trace_pb2.TracesData()
traces_data.ParseFromString(msg.openTelemetryData)
- ot_data = google.protobuf.json_format.MessageToDict(
- traces_data, preserving_proto_field_name=True
- )
+ ot_data = google.protobuf.json_format.MessageToDict(traces_data, preserving_proto_field_name=True)
funcs = extraFunctions.union({"Rule: Spoof A record"})
self.checkOTData(
return response.to_wire()
-class TestOpenTelemetryTracingStripIncomingTraceParent(
- DNSDistOpenTelemetryProtobufTest
-):
+class TestOpenTelemetryTracingStripIncomingTraceParent(DNSDistOpenTelemetryProtobufTest):
_yaml_config_params = [
"_testServerPort",
]
ottrace.data += binascii.a2b_hex("12345678901234567890123456789012")
ottrace.data += binascii.a2b_hex("1234567890123456")
ottrace.data += binascii.a2b_hex("00")
- query = dns.message.make_query(
- name, "A", "IN", use_edns=True, options=[ottrace]
- )
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ottrace])
if useTCP:
_, receivedResponse = self.sendTCPQuery(query, response=None)
print(request)
response = dns.message.make_response(request)
- traceparent: dns.edns.Option | None = next(
- (i for i in request.options if i.otype == 65500), None
- )
+ traceparent: dns.edns.Option | None = next((i for i in request.options if i.otype == 65500), None)
print(traceparent)
return response.to_wire()
-class TestOpenTelemetryTracingSendTraceparentDownstream(
- DNSDistOpenTelemetryProtobufTest
-):
+class TestOpenTelemetryTracingSendTraceparentDownstream(DNSDistOpenTelemetryProtobufTest):
_yaml_config_params = [
"_testServerPort",
]
self.doQuery(True)
-class TestOpenTelemetryTracingSendTraceparentDownstreamLua(
- TestOpenTelemetryTracingSendTraceparentDownstream
-):
+class TestOpenTelemetryTracingSendTraceparentDownstreamLua(TestOpenTelemetryTracingSendTraceparentDownstream):
_yaml_config_template = None
_config_params = [
"_testServerPort",
from dnsdisttests import DNSDistTest, pickAvailablePort
-class OutgoingDOHTests(object):
+class OutgoingDOHTests(object):
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerAPIKey = 'apisecret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
+ _webServerBasicAuthPassword = "secret"
+ _webServerAPIKey = "apisecret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
def checkOnlyDOHResponderHit(self, numberOfDOHQueries=1):
- self.assertNotIn('UDP Responder', self._responsesCounter)
- self.assertNotIn('TCP Responder', self._responsesCounter)
- self.assertNotIn('TLS Responder', self._responsesCounter)
- self.assertEqual(self._responsesCounter['DoH Connection Handler'], numberOfDOHQueries)
+ self.assertNotIn("UDP Responder", self._responsesCounter)
+ self.assertNotIn("TCP Responder", self._responsesCounter)
+ self.assertNotIn("TLS Responder", self._responsesCounter)
+ self.assertEqual(self._responsesCounter["DoH Connection Handler"], numberOfDOHQueries)
def getServerStat(self, key):
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertTrue(len(content['servers']), 1)
- server = content['servers'][0]
+ self.assertTrue(len(content["servers"]), 1)
+ server = content["servers"][0]
self.assertIn(key, server)
return server[key]
"""
Outgoing DOH: UDP query is sent via DOH
"""
- name = 'udp.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
- connsBefore = self.getServerStat('tcpReusedConnections')
+ connsBefore = self.getServerStat("tcpReusedConnections")
numberOfUDPQueries = 10
for _ in range(numberOfUDPQueries):
numberOfQueries = numberOfUDPQueries + 1
self.checkOnlyDOHResponderHit(numberOfUDPQueries)
- self.assertEqual(self.getServerStat('tcpNewConnections'), 1)
- self.assertEqual(self.getServerStat('tcpReusedConnections'), connsBefore + numberOfQueries - 1)
- self.assertEqual(self.getServerStat('tlsResumptions'), 0)
+ self.assertEqual(self.getServerStat("tcpNewConnections"), 1)
+ self.assertEqual(self.getServerStat("tcpReusedConnections"), connsBefore + numberOfQueries - 1)
+ self.assertEqual(self.getServerStat("tlsResumptions"), 0)
def testTCP(self):
"""
Outgoing DOH: TCP query is sent via DOH
"""
- name = 'tcp.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tcp.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
- connsBefore = self.getServerStat('tcpReusedConnections')
+ connsBefore = self.getServerStat("tcpReusedConnections")
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, expectedResponse)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, expectedResponse)
self.checkOnlyDOHResponderHit()
- self.assertEqual(self.getServerStat('tcpNewConnections'), 1)
- self.assertEqual(self.getServerStat('tcpReusedConnections'), connsBefore)
- self.assertEqual(self.getServerStat('tlsResumptions'), 0)
+ self.assertEqual(self.getServerStat("tcpNewConnections"), 1)
+ self.assertEqual(self.getServerStat("tcpReusedConnections"), connsBefore)
+ self.assertEqual(self.getServerStat("tlsResumptions"), 0)
def testUDPCache(self):
"""
Outgoing DOH: UDP query is sent via DOH, should be cached
"""
- name = 'udp.cached.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.cached.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, expectedResponse)
"""
Outgoing DOH: TCP query is sent via DOH, should be cached
"""
- name = 'tcp.cached.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tcp.cached.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, expectedResponse)
self.sendConsoleCommand("getServer(0):setAuto()")
time.sleep(2)
status = self.sendConsoleCommand("if getServer(0):isUp() then return 'up' else return 'down' end").strip("\n")
- self.assertEqual(status, 'up')
+ self.assertEqual(status, "up")
-class BrokenOutgoingDOHTests(object):
+class BrokenOutgoingDOHTests(object):
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerAPIKey = 'apisecret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
+ _webServerBasicAuthPassword = "secret"
+ _webServerAPIKey = "apisecret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
def checkNoResponderHit(self):
- self.assertNotIn('UDP Responder', self._responsesCounter)
- self.assertNotIn('TCP Responder', self._responsesCounter)
- self.assertNotIn('TLS Responder', self._responsesCounter)
- self.assertNotIn('DOH Responder', self._responsesCounter)
+ self.assertNotIn("UDP Responder", self._responsesCounter)
+ self.assertNotIn("TCP Responder", self._responsesCounter)
+ self.assertNotIn("TLS Responder", self._responsesCounter)
+ self.assertNotIn("DOH Responder", self._responsesCounter)
def testUDP(self):
"""
Outgoing DOH (broken): UDP query is sent via DOH
"""
- name = 'udp.broken-outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.broken-outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
"""
Outgoing DOH (broken): TCP query is sent via DOH
"""
- name = 'tcp.broken-outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tcp.broken-outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
self.checkNoResponderHit()
-class OutgoingDOHBrokenResponsesTests(object):
+class OutgoingDOHBrokenResponsesTests(object):
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerAPIKey = 'apisecret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
+ _webServerBasicAuthPassword = "secret"
+ _webServerAPIKey = "apisecret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
def testUDP(self):
"""
Outgoing DOH (broken responses): UDP query is sent via DOH
"""
- name = '500-status.broken-responses.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "500-status.broken-responses.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
- name = 'invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
- name = 'closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
# but a valid response should be successful
- name = 'valid.broken-responses.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "valid.broken-responses.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
(_, receivedResponse) = self.sendUDPQuery(query, response)
"""
Outgoing DOH (broken responses): TCP query is sent via DOH
"""
- name = '500-status.broken-responses.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "500-status.broken-responses.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
- name = 'invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
- name = 'closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
# but a valid response should be successful
- name = 'valid.broken-responses.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "valid.broken-responses.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
(_, receivedResponse) = self.sendTCPQuery(query, response)
# we can't check the received query because the responder does not populate the queue..
- #self.assertEqual(query, receivedQuery)
+ # self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
+
class TestOutgoingDOHOpenSSL(DNSDistTest, OutgoingDOHTests):
if os.path.exists("/tmp/dohkeys"):
os.remove("/tmp/dohkeys")
_tlsBackendPort = pickAvailablePort()
- _tlsProvider = 'openssl'
+ _tlsProvider = "openssl"
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_tlsBackendPort', '_tlsProvider', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_tlsBackendPort",
+ "_tlsProvider",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
@staticmethod
def sniCallback(sslSocket, sni, sslContext):
- assert(sni == 'powerdns.com')
+ assert sni == "powerdns.com"
return None
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
tlsContext.set_alpn_protocols(["h2"])
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
# requires Python 3.7+
- if hasattr(tlsContext, 'sni_callback'):
+ if hasattr(tlsContext, "sni_callback"):
tlsContext.sni_callback = cls.sniCallback
print("Launching DOH responder..")
- cls._DOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
+
class TestOutgoingDOHGnuTLS(DNSDistTest, OutgoingDOHTests):
_tlsBackendPort = pickAvailablePort()
- _tlsProvider = 'gnutls'
+ _tlsProvider = "gnutls"
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_tlsBackendPort', '_tlsProvider', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_tlsBackendPort",
+ "_tlsProvider",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
tlsContext.keylog_filename = "/tmp/keys"
print("Launching DOH responder..")
- cls._DOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
+
class TestOutgoingDOHOpenSSLYaml(DNSDistTest, OutgoingDOHTests):
_tlsBackendPort = pickAvailablePort()
- _tlsProvider = 'openssl'
+ _tlsProvider = "openssl"
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_config_params = []
_config_template = ""
_yaml_config_template = """---
type: "Pool"
pool_name: "cache"
"""
- _yaml_config_params = ['_consoleKeyB64', '_consolePort', '_tlsBackendPort', '_tlsProvider', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _yaml_config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_tlsBackendPort",
+ "_tlsProvider",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
@staticmethod
def sniCallback(sslSocket, sni, sslContext):
- assert(sni == 'powerdns.com')
+ assert sni == "powerdns.com"
return None
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
tlsContext.set_alpn_protocols(["h2"])
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
# requires Python 3.7+
- if hasattr(tlsContext, 'sni_callback'):
+ if hasattr(tlsContext, "sni_callback"):
tlsContext.sni_callback = cls.sniCallback
print("Launching DOH responder..")
- cls._DOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
+
class TestOutgoingDOHOpenSSLWrongCertName(DNSDistTest, BrokenOutgoingDOHTests):
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_tlsBackendPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='openssl', validateCertificates=true, caStore='ca.pem', subjectName='not-powerdns.com', dohPath='/dns-query'}:setUp()
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching DOH responder..")
- cls._DOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
+
class TestOutgoingDOHGnuTLSWrongCertName(DNSDistTest, BrokenOutgoingDOHTests):
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_tlsBackendPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='gnutls', validateCertificates=true, caStore='ca.pem', subjectName='not-powerdns.com', dohPath='/dns-query'}:setUp()
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching DOH responder..")
- cls._DOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
+
class TestOutgoingDOHOpenSSLWrongCertNameButNoCheck(DNSDistTest, OutgoingDOHTests):
_tlsBackendPort = pickAvailablePort()
- _tlsProvider = 'openssl'
+ _tlsProvider = "openssl"
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_tlsBackendPort', '_tlsProvider', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_tlsBackendPort",
+ "_tlsProvider",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching DOH responder..")
- cls._DOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
+
class TestOutgoingDOHGnuTLSWrongCertNameButNoCheck(DNSDistTest, OutgoingDOHTests):
_tlsBackendPort = pickAvailablePort()
- _tlsProvider = 'gnutls'
+ _tlsProvider = "gnutls"
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_tlsBackendPort', '_tlsProvider', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_tlsBackendPort",
+ "_tlsProvider",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching DOH responder..")
- cls._DOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
+
class TestOutgoingDOHBrokenResponsesOpenSSL(DNSDistTest, OutgoingDOHBrokenResponsesTests):
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_tlsBackendPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='openssl', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com', dohPath='/dns-query', pool={'', 'cache'}}:setUp()
def callback(request, headers, fromQueue, toQueue):
- if str(request.question[0].name) == '500-status.broken-responses.outgoing-doh.test.powerdns.com.':
+ if str(request.question[0].name) == "500-status.broken-responses.outgoing-doh.test.powerdns.com.":
print("returning 500")
- return 500, b'Server error'
+ return 500, b"Server error"
- if str(request.question[0].name) == 'invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com.':
- return 200, b'not DNS'
+ if str(request.question[0].name) == "invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com.":
+ return 200, b"not DNS"
- if str(request.question[0].name) == 'closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com.':
+ if str(request.question[0].name) == "closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com.":
return 200, None
print("Returning default for %s" % (request.question[0].name))
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
tlsContext.set_alpn_protocols(["h2"])
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching DOH responder..")
- cls._DOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.callback, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[
+ cls._tlsBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.callback,
+ tlsContext,
+ ],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
+
class TestOutgoingDOHBrokenResponsesGnuTLS(DNSDistTest, OutgoingDOHBrokenResponsesTests):
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_tlsBackendPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='gnutls', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com', dohPath='/dns-query'}:setUp()
def callback(request, headers, fromQueue, toQueue):
- if str(request.question[0].name) == '500-status.broken-responses.outgoing-doh.test.powerdns.com.':
+ if str(request.question[0].name) == "500-status.broken-responses.outgoing-doh.test.powerdns.com.":
print("returning 500")
- return 500, b'Server error'
+ return 500, b"Server error"
- if str(request.question[0].name) == 'invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com.':
- return 200, b'not DNS'
+ if str(request.question[0].name) == "invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com.":
+ return 200, b"not DNS"
- if str(request.question[0].name) == 'closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com.':
+ if str(request.question[0].name) == "closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com.":
return 200, None
print("Returning default for %s" % (request.question[0].name))
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
tlsContext.set_alpn_protocols(["h2"])
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching DOH responder..")
- cls._DOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.callback, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[
+ cls._tlsBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.callback,
+ tlsContext,
+ ],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
-class TestOutgoingDOHProxyProtocol(DNSDistTest):
+class TestOutgoingDOHProxyProtocol(DNSDistTest):
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort']
+ _config_params = ["_tlsBackendPort"]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='gnutls', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com', dohPath='/dns-query', useProxyProtocol=true}:setUp()
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
tlsContext.set_alpn_protocols(["h2"])
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching DOH with Proxy Protocol responder..")
- cls._DOHResponder = threading.Thread(name='DOH with Proxy Protocol Responder', target=cls.DOHResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext, True])
+ cls._DOHResponder = threading.Thread(
+ name="DOH with Proxy Protocol Responder",
+ target=cls.DOHResponder,
+ args=[
+ cls._tlsBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ None,
+ tlsContext,
+ True,
+ ],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
"""
Outgoing DOH with Proxy Protocol
"""
- name = 'proxy-protocol.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "proxy-protocol.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
(receivedProxyPayload, receivedResponse) = self.sendUDPQuery(query, expectedResponse)
receivedQuery = self._fromResponderQueue.get(True, 1.0)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, expectedResponse)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', False)
+ self.checkMessageProxyProtocol(receivedProxyPayload, "127.0.0.1", "127.0.0.1", False)
(receivedProxyPayload, receivedResponse) = self.sendTCPQuery(query, expectedResponse)
receivedQuery = self._fromResponderQueue.get(True, 1.0)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, expectedResponse)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True)
+ self.checkMessageProxyProtocol(receivedProxyPayload, "127.0.0.1", "127.0.0.1", True)
+
class TestOutgoingDOHXForwarded(DNSDistTest):
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort']
+ _config_params = ["_tlsBackendPort"]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='gnutls', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com', dohPath='/dns-query', addXForwardedHeaders=true}
def callback(request, headersList, fromQueue, toQueue):
- if str(request.question[0].name) == 'a.root-servers.net.':
+ if str(request.question[0].name) == "a.root-servers.net.":
# do not check headers on health-check queries
return 200, dns.message.make_response(request).to_wire()
headers = {}
if headersList:
- for k,v in headersList:
+ for k, v in headersList:
headers[k] = v
- if not b'x-forwarded-for' in headers:
+ if not b"x-forwarded-for" in headers:
print("missing X-Forwarded-For")
- return 406, b'Missing X-Forwarded-For header'
- if not b'x-forwarded-port' in headers:
+ return 406, b"Missing X-Forwarded-For header"
+ if not b"x-forwarded-port" in headers:
print("missing X-Forwarded-Port")
- return 406, b'Missing X-Forwarded-Port header'
- if not b'x-forwarded-proto' in headers:
+ return 406, b"Missing X-Forwarded-Port header"
+ if not b"x-forwarded-proto" in headers:
print("missing X-Forwarded-Proto")
- return 406, b'Missing X-Forwarded-Proto header'
+ return 406, b"Missing X-Forwarded-Proto header"
toQueue.put(request, True, 1.0)
response = fromQueue.get(True, 1.0)
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
tlsContext.set_alpn_protocols(["h2"])
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching DOH responder..")
- cls._DOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, cls.callback, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[
+ cls._tlsBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ cls.callback,
+ tlsContext,
+ ],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
"""
Outgoing DOH: X-Forwarded
"""
- name = 'x-forwarded-for.outgoing-doh.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "x-forwarded-for.outgoing-doh.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, expectedResponse)
from dnsdisttests import DNSDistTest, pickAvailablePort
-class OutgoingTLSTests(object):
+class OutgoingTLSTests(object):
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerAPIKey = 'apisecret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
+ _webServerBasicAuthPassword = "secret"
+ _webServerAPIKey = "apisecret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
def checkOnlyTLSResponderHit(self, numberOfTLSQueries=1):
- self.assertNotIn('UDP Responder', self._responsesCounter)
- self.assertNotIn('TCP Responder', self._responsesCounter)
- self.assertEqual(self._responsesCounter['TLS Responder'], numberOfTLSQueries)
+ self.assertNotIn("UDP Responder", self._responsesCounter)
+ self.assertNotIn("TCP Responder", self._responsesCounter)
+ self.assertEqual(self._responsesCounter["TLS Responder"], numberOfTLSQueries)
def getServerStat(self, key):
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertTrue(len(content['servers']), 1)
- server = content['servers'][0]
+ self.assertTrue(len(content["servers"]), 1)
+ server = content["servers"][0]
self.assertIn(key, server)
return server[key]
"""
Outgoing TLS: UDP query is sent via TLS
"""
- name = 'udp.outgoing-tls.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.outgoing-tls.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
numberOfUDPQueries = 10
self.checkOnlyTLSResponderHit(numberOfUDPQueries)
# our TLS responder does only one query per connection, so we need one for the TCP
# query and one for the UDP one (the TCP test is done first)
- self.assertEqual(self.getServerStat('tcpNewConnections'), numberOfQueries)
+ self.assertEqual(self.getServerStat("tcpNewConnections"), numberOfQueries)
# we tried to reuse the connection (and then it failed but hey)
- self.assertEqual(self.getServerStat('tcpReusedConnections'), numberOfQueries - 1)
+ self.assertEqual(self.getServerStat("tcpReusedConnections"), numberOfQueries - 1)
# we resumed the TLS session, though, but since we only learn about that
# when the connection is closed, we might be off by one, except if a health check
# allowed the first TCP connection to be resumed as well
- self.assertGreaterEqual(self.getServerStat('tlsResumptions'), numberOfUDPQueries - 1)
- self.assertLessEqual(self.getServerStat('tlsResumptions'), numberOfUDPQueries)
+ self.assertGreaterEqual(self.getServerStat("tlsResumptions"), numberOfUDPQueries - 1)
+ self.assertLessEqual(self.getServerStat("tlsResumptions"), numberOfUDPQueries)
def testTCP(self):
"""
Outgoing TLS: TCP query is sent via TLS
"""
- name = 'tcp.outgoing-tls.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tcp.outgoing-tls.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, expectedResponse)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, expectedResponse)
self.checkOnlyTLSResponderHit()
- self.assertEqual(self.getServerStat('tcpNewConnections'), 1)
- self.assertEqual(self.getServerStat('tcpReusedConnections'), 0)
- self.assertEqual(self.getServerStat('tlsResumptions'), 0)
+ self.assertEqual(self.getServerStat("tcpNewConnections"), 1)
+ self.assertEqual(self.getServerStat("tcpReusedConnections"), 0)
+ self.assertEqual(self.getServerStat("tlsResumptions"), 0)
-class BrokenOutgoingTLSTests(object):
+class BrokenOutgoingTLSTests(object):
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerAPIKey = 'apisecret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
+ _webServerBasicAuthPassword = "secret"
+ _webServerAPIKey = "apisecret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
def checkNoResponderHit(self):
- self.assertNotIn('UDP Responder', self._responsesCounter)
- self.assertNotIn('TCP Responder', self._responsesCounter)
- self.assertNotIn('TLS Responder', self._responsesCounter)
+ self.assertNotIn("UDP Responder", self._responsesCounter)
+ self.assertNotIn("TCP Responder", self._responsesCounter)
+ self.assertNotIn("TLS Responder", self._responsesCounter)
def testUDP(self):
"""
Outgoing TLS (broken): UDP query is sent via TLS
"""
- name = 'udp.broken-outgoing-tls.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.broken-outgoing-tls.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
"""
Outgoing TLS (broken): TCP query is sent via TLS
"""
- name = 'tcp.broken-outgoing-tls.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tcp.broken-outgoing-tls.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
self.checkNoResponderHit()
+
class TestOutgoingTLSOpenSSL(DNSDistTest, OutgoingTLSTests):
if os.path.exists("/tmp/dotkeys"):
os.remove("/tmp/dotkeys")
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_tlsBackendPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='openssl', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com', keyLogFile="/tmp/dotkeys"}
@staticmethod
def sniCallback(sslSocket, sni, sslContext):
- assert(sni == 'powerdns.com')
+ assert sni == "powerdns.com"
return None
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
# requires Python 3.7+
- if hasattr(tlsContext, 'sni_callback'):
+ if hasattr(tlsContext, "sni_callback"):
tlsContext.sni_callback = cls.sniCallback
print("Launching TLS responder..")
- cls._TLSResponder = threading.Thread(name='TLS Responder', target=cls.TCPResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._TLSResponder = threading.Thread(
+ name="TLS Responder",
+ target=cls.TCPResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._TLSResponder.daemon = True
cls._TLSResponder.start()
+
class TestOutgoingTLSOpenSSLYaml(DNSDistTest, OutgoingTLSTests):
_tlsBackendPort = pickAvailablePort()
_config_params = []
tcp:
worker_threads: 1
"""
- _yaml_config_params = ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _yaml_config_params = [
+ "_tlsBackendPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
@staticmethod
def sniCallback(sslSocket, sni, sslContext):
- assert(sni == 'powerdns.com')
+ assert sni == "powerdns.com"
return None
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
# requires Python 3.7+
- if hasattr(tlsContext, 'sni_callback'):
+ if hasattr(tlsContext, "sni_callback"):
tlsContext.sni_callback = cls.sniCallback
print("Launching TLS responder..")
- cls._TLSResponder = threading.Thread(name='TLS Responder', target=cls.TCPResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._TLSResponder = threading.Thread(
+ name="TLS Responder",
+ target=cls.TCPResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._TLSResponder.daemon = True
cls._TLSResponder.start()
+
class TestOutgoingTLSGnuTLS(DNSDistTest, OutgoingTLSTests):
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_tlsBackendPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='gnutls', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com'}
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
tlsContext.keylog_filename = "/tmp/keys"
print("Launching TLS responder..")
- cls._TLSResponder = threading.Thread(name='TLS Responder', target=cls.TCPResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._TLSResponder = threading.Thread(
+ name="TLS Responder",
+ target=cls.TCPResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._TLSResponder.daemon = True
cls._TLSResponder.start()
+
class TestOutgoingTLSOpenSSLWrongCertName(DNSDistTest, BrokenOutgoingTLSTests):
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_tlsBackendPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='openssl', validateCertificates=true, caStore='ca.pem', subjectName='not-powerdns.com'}
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching TLS responder..")
- cls._TLSResponder = threading.Thread(name='TLS Responder', target=cls.TCPResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._TLSResponder = threading.Thread(
+ name="TLS Responder",
+ target=cls.TCPResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._TLSResponder.daemon = True
cls._TLSResponder.start()
+
class TestOutgoingTLSGnuTLSWrongCertName(DNSDistTest, BrokenOutgoingTLSTests):
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_tlsBackendPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='gnutls', validateCertificates=true, caStore='ca.pem', subjectName='not-powerdns.com'}
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching TLS responder..")
- cls._TLSResponder = threading.Thread(name='TLS Responder', target=cls.TCPResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._TLSResponder = threading.Thread(
+ name="TLS Responder",
+ target=cls.TCPResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._TLSResponder.daemon = True
cls._TLSResponder.start()
+
class TestOutgoingTLSOpenSSLWrongCertNameButNoCheck(DNSDistTest, OutgoingTLSTests):
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_tlsBackendPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='openssl', validateCertificates=false, caStore='ca.pem', subjectName='not-powerdns.com'}
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching TLS responder..")
- cls._TLSResponder = threading.Thread(name='TLS Responder', target=cls.TCPResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._TLSResponder = threading.Thread(
+ name="TLS Responder",
+ target=cls.TCPResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._TLSResponder.daemon = True
cls._TLSResponder.start()
+
class TestOutgoingTLSGnuTLSWrongCertNameButNoCheck(DNSDistTest, OutgoingTLSTests):
_tlsBackendPort = pickAvailablePort()
- _config_params = ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _config_params = [
+ "_tlsBackendPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
setMaxTCPClientThreads(1)
newServer{address="127.0.0.1:%d", tls='gnutls', validateCertificates=false, caStore='ca.pem', subjectName='not-powerdns.com'}
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching TLS responder..")
- cls._TLSResponder = threading.Thread(name='TLS Responder', target=cls.TCPResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext])
+ cls._TLSResponder = threading.Thread(
+ name="TLS Responder",
+ target=cls.TCPResponder,
+ args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext],
+ )
cls._TLSResponder.daemon = True
cls._TLSResponder.start()
import dns
from dnsdisttests import DNSDistTest
+
class TestPoolManagement(DNSDistTest):
_config_template = """
local backendPort = %d
"""
Pool management: A query without EDNS
"""
- name = 'pool-mngmt.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "pool-mngmt.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
@unittest.skipIf("SKIP_PROMETHEUS_TESTS" in os.environ, "Prometheus tests are disabled")
class TestPrometheus(DNSDistTest):
-
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
_webServerBasicAuthPassword = "secret"
- _webServerBasicAuthPasswordHashed = "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
_webServerAPIKey = "apisecret"
- _webServerAPIKeyHashed = "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
_config_params = [
"_testServerPort",
"_webServerPort",
self.assertEqual(len(tokens), 2)
if not line.startswith("dnsdist_") and not line.startswith("custom_"):
raise AssertionError(
- "Expecting prometheus metric to be prefixed by 'dnsdist_', got: \"%s\""
- % (line)
+ "Expecting prometheus metric to be prefixed by 'dnsdist_', got: \"%s\"" % (line)
)
key = tokens[0]
if key in keysSeen:
raise AssertionError(f"Duplicate prometheus key: '{key}'")
keysSeen[key] = True
- def checkMetric(
- self, content, name, expectedType, expectedValue, expectedLabels=""
- ):
+ def checkMetric(self, content, name, expectedType, expectedValue, expectedLabels=""):
typeFound = False
helpFound = False
valueFound = False
)
output = process.communicate(input=content)
except subprocess.CalledProcessError as exc:
- raise AssertionError(
- "%s failed (%d): %s" % (testcmd, process.returncode, process.output)
- )
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, process.output))
# promtool may return 3 because of the "_total" suffix warnings
if not process.returncode in [0, 3]:
- raise AssertionError(
- "%s failed (%d): %s" % (testcmd, process.returncode, output)
- )
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, output))
for line in output[0].splitlines():
if line.endswith(b'should have "_total" suffix'):
self.checkMetric(r.text, "dnsdist_custom_metric1", "counter", 1)
self.checkMetric(r.text, "dnsdist_custom_metric2", "gauge", 0)
self.checkMetric(r.text, "custom_prometheus_name", "counter", 0)
- self.checkMetric(
- r.text, "dnsdist_custom_metric_foo", "counter", 1, '{x="bar",y="xyz"}'
- )
- self.checkMetric(
- r.text, "dnsdist_custom_metric_foo", "counter", 1, '{x="baz",y="abc"}'
- )
+ self.checkMetric(r.text, "dnsdist_custom_metric_foo", "counter", 1, '{x="bar",y="xyz"}')
+ self.checkMetric(r.text, "dnsdist_custom_metric_foo", "counter", 1, '{x="baz",y="abc"}')
class TestPrometheusWithInstance(TestPrometheus):
self.checkPrometheusContentWithInstance(r.text)
self.checkPrometheusContentPromtool(r.content)
- self.checkMetric(
- r.text, "dnsdist_custom_metric1", "counter", 1, '{instance="my-id"}'
- )
- self.checkMetric(
- r.text, "dnsdist_custom_metric2", "gauge", 0, '{instance="my-id"}'
- )
- self.checkMetric(
- r.text, "custom_prometheus_name", "counter", 0, '{instance="my-id"}'
- )
+ self.checkMetric(r.text, "dnsdist_custom_metric1", "counter", 1, '{instance="my-id"}')
+ self.checkMetric(r.text, "dnsdist_custom_metric2", "gauge", 0, '{instance="my-id"}')
+ self.checkMetric(r.text, "custom_prometheus_name", "counter", 0, '{instance="my-id"}')
self.checkMetric(
r.text,
"dnsdist_custom_metric_foo",
import dnsmessage_pb2
import extendederrors
+
class DNSDistProtobufTest(DNSDistTest):
_protobufServerPort = pickAvailablePort()
_protobufQueue = Queue()
- _protobufServerID = 'dnsdist-server-1'
+ _protobufServerID = "dnsdist-server-1"
_protobufCounter = 0
@classmethod
@classmethod
def startResponders(cls):
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._protobufListener = threading.Thread(name='Protobuf Listener', target=cls.ProtobufListener, args=[cls._protobufServerPort])
+ cls._protobufListener = threading.Thread(
+ name="Protobuf Listener", target=cls.ProtobufListener, args=[cls._protobufServerPort]
+ )
cls._protobufListener.daemon = True
cls._protobufListener.start()
def checkProtobufBase(self, msg, protocol, query, initiator, normalQueryResponse=True, v6=False, flags=None):
self.assertTrue(msg)
- self.assertTrue(msg.HasField('timeSec'))
- self.assertTrue(msg.HasField('socketFamily'))
+ self.assertTrue(msg.HasField("timeSec"))
+ self.assertTrue(msg.HasField("socketFamily"))
if v6:
self.assertEqual(msg.socketFamily, dnsmessage_pb2.PBDNSMessage.INET6)
else:
self.assertEqual(msg.socketFamily, dnsmessage_pb2.PBDNSMessage.INET)
- self.assertTrue(msg.HasField('from'))
- fromvalue = getattr(msg, 'from')
+ self.assertTrue(msg.HasField("from"))
+ fromvalue = getattr(msg, "from")
if v6:
self.assertEqual(socket.inet_ntop(socket.AF_INET6, fromvalue), initiator)
else:
self.assertEqual(socket.inet_ntop(socket.AF_INET, fromvalue), initiator)
- self.assertTrue(msg.HasField('socketProtocol'))
+ self.assertTrue(msg.HasField("socketProtocol"))
self.assertEqual(msg.socketProtocol, protocol)
- self.assertTrue(msg.HasField('messageId'))
- self.assertTrue(msg.HasField('id'))
+ self.assertTrue(msg.HasField("messageId"))
+ self.assertTrue(msg.HasField("id"))
self.assertEqual(msg.id, query.id)
- self.assertTrue(msg.HasField('inBytes'))
- self.assertTrue(msg.HasField('headerFlags'))
+ self.assertTrue(msg.HasField("inBytes"))
+ self.assertTrue(msg.HasField("headerFlags"))
queryFlags = flags or int.from_bytes(query.to_wire()[2:4], byteorder=sys.byteorder)
self.assertEqual(msg.headerFlags, queryFlags)
- self.assertTrue(msg.HasField('serverIdentity'))
- self.assertEqual(msg.serverIdentity, self._protobufServerID.encode('utf-8'))
-
- if normalQueryResponse and (protocol == dnsmessage_pb2.PBDNSMessage.UDP or protocol == dnsmessage_pb2.PBDNSMessage.TCP):
- # compare inBytes with length of query/response
- self.assertEqual(msg.inBytes, len(query.to_wire()))
+ self.assertTrue(msg.HasField("serverIdentity"))
+ self.assertEqual(msg.serverIdentity, self._protobufServerID.encode("utf-8"))
+
+ if normalQueryResponse and (
+ protocol == dnsmessage_pb2.PBDNSMessage.UDP or protocol == dnsmessage_pb2.PBDNSMessage.TCP
+ ):
+ # compare inBytes with length of query/response
+ self.assertEqual(msg.inBytes, len(query.to_wire()))
# dnsdist doesn't set the existing EDNS Subnet for now,
# although it might be set from Lua
# self.assertTrue(msg.HasField('originalRequestorSubnet'))
# self.assertEqual(len(msg.originalRequestorSubnet), 4)
# self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.originalRequestorSubnet), '127.0.0.1')
- def checkProtobufQuery(self, msg, protocol, query, qclass, qtype, qname, initiator='127.0.0.1', v6=False):
+ def checkProtobufQuery(self, msg, protocol, query, qclass, qtype, qname, initiator="127.0.0.1", v6=False):
self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSQueryType)
self.checkProtobufBase(msg, protocol, query, initiator, v6=v6)
# dnsdist doesn't fill the responder field for responses
# because it doesn't keep the information around.
- self.assertTrue(msg.HasField('to'))
+ self.assertTrue(msg.HasField("to"))
if not v6:
- self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.to), '127.0.0.1')
- self.assertTrue(msg.HasField('question'))
- self.assertTrue(msg.question.HasField('qClass'))
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.to), "127.0.0.1")
+ self.assertTrue(msg.HasField("question"))
+ self.assertTrue(msg.question.HasField("qClass"))
self.assertEqual(msg.question.qClass, qclass)
- self.assertTrue(msg.question.HasField('qType'))
+ self.assertTrue(msg.question.HasField("qType"))
self.assertEqual(msg.question.qClass, qtype)
- self.assertTrue(msg.question.HasField('qName'))
+ self.assertTrue(msg.question.HasField("qName"))
self.assertEqual(msg.question.qName, qname)
def checkProtobufTags(self, tags, expectedTags):
# exclusive or of lists should be empty
self.assertEqual(len(listx), 0, "Protobuf tags don't match")
- def checkProtobufQueryConvertedToResponse(self, msg, protocol, response, initiator='127.0.0.0', flags=None):
+ def checkProtobufQueryConvertedToResponse(self, msg, protocol, response, initiator="127.0.0.0", flags=None):
self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSResponseType)
# skip comparing inBytes (size of the query) with the length of the generated response
self.checkProtobufBase(msg, protocol, response, initiator, False, flags=flags)
- self.assertTrue(msg.HasField('response'))
- self.assertTrue(msg.response.HasField('queryTimeSec'))
+ self.assertTrue(msg.HasField("response"))
+ self.assertTrue(msg.response.HasField("queryTimeSec"))
- def checkProtobufResponse(self, msg, protocol, response, initiator='127.0.0.1', v6=False):
+ def checkProtobufResponse(self, msg, protocol, response, initiator="127.0.0.1", v6=False):
self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSResponseType)
self.checkProtobufBase(msg, protocol, response, initiator, v6=v6)
- self.assertTrue(msg.HasField('response'))
- self.assertTrue(msg.response.HasField('queryTimeSec'))
+ self.assertTrue(msg.HasField("response"))
+ self.assertTrue(msg.response.HasField("queryTimeSec"))
def checkProtobufResponseRecord(self, record, rclass, rtype, rname, rttl):
- self.assertTrue(record.HasField('class'))
- self.assertEqual(getattr(record, 'class'), rclass)
- self.assertTrue(record.HasField('type'))
+ self.assertTrue(record.HasField("class"))
+ self.assertEqual(getattr(record, "class"), rclass)
+ self.assertTrue(record.HasField("type"))
self.assertEqual(record.type, rtype)
- self.assertTrue(record.HasField('name'))
+ self.assertTrue(record.HasField("name"))
self.assertEqual(record.name, rname)
- self.assertTrue(record.HasField('ttl'))
+ self.assertTrue(record.HasField("ttl"))
self.assertEqual(record.ttl, rttl)
- self.assertTrue(record.HasField('rdata'))
+ self.assertTrue(record.HasField("rdata"))
+
class TestProtobuf(DNSDistProtobufTest):
- _config_params = ['_testServerPort', '_protobufServerPort', '_protobufServerID', '_protobufServerID']
+ _config_params = ["_testServerPort", "_protobufServerPort", "_protobufServerID", "_protobufServerID"]
_config_template = """
luasmn = newSuffixMatchNode()
luasmn:add(newDNSName('lua.protobuf.tests.powerdns.com.'))
"""
Protobuf: Send data to a protobuf server
"""
- name = 'query.protobuf.tests.powerdns.com.'
+ name = "query.protobuf.tests.powerdns.com."
- target = 'target.protobuf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.protobuf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
msg = self.getFirstProtobufMessage()
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
- self.checkProtobufTags(msg.response.tags, [u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Query,123"])
+ self.checkProtobufTags(
+ msg.response.tags, ["TestLabel1,TestData1", "TestLabel2,TestData2", "TestLabel3,TestData3", "Query,123"]
+ )
# check the protobuf message corresponding to the UDP response
msg = self.getFirstProtobufMessage()
self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response)
- self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Response,456"])
+ self.checkProtobufTags(
+ msg.response.tags, ["TestLabel1,TestData1", "TestLabel2,TestData2", "TestLabel3,TestData3", "Response,456"]
+ )
self.assertEqual(len(msg.response.rrs), 2)
rr = msg.response.rrs[0]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600)
- self.assertEqual(rr.rdata.decode('utf-8'), target)
+ self.assertEqual(rr.rdata.decode("utf-8"), target)
rr = msg.response.rrs[1]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "127.0.0.1")
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
msg = self.getFirstProtobufMessage()
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.TCP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
- self.checkProtobufTags(msg.response.tags, [u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Query,123"])
+ self.checkProtobufTags(
+ msg.response.tags, ["TestLabel1,TestData1", "TestLabel2,TestData2", "TestLabel3,TestData3", "Query,123"]
+ )
# check the protobuf message corresponding to the TCP response
msg = self.getFirstProtobufMessage()
self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response)
- self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Response,456"])
+ self.checkProtobufTags(
+ msg.response.tags, ["TestLabel1,TestData1", "TestLabel2,TestData2", "TestLabel3,TestData3", "Response,456"]
+ )
self.assertEqual(len(msg.response.rrs), 2)
rr = msg.response.rrs[0]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600)
- self.assertEqual(rr.rdata.decode('utf-8'), target)
+ self.assertEqual(rr.rdata.decode("utf-8"), target)
rr = msg.response.rrs[1]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "127.0.0.1")
def testLuaProtobuf(self):
-
"""
Protobuf: Check that the Lua callback rewrote the initiator
"""
- name = 'lua.protobuf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "lua.protobuf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
-
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertTrue(receivedQuery)
# check the protobuf message corresponding to the UDP query
msg = self.getFirstProtobufMessage()
flags = int.from_bytes(query.to_wire()[2:4], byteorder=sys.byteorder)
- self.checkProtobufQueryConvertedToResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response, '127.0.0.0', flags=flags)
- self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Query,123"])
+ self.checkProtobufQueryConvertedToResponse(
+ msg, dnsmessage_pb2.PBDNSMessage.UDP, response, "127.0.0.0", flags=flags
+ )
+ self.checkProtobufTags(
+ msg.response.tags, ["TestLabel1,TestData1", "TestLabel2,TestData2", "TestLabel3,TestData3", "Query,123"]
+ )
# check the protobuf message corresponding to the UDP response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response, '127.0.0.0')
- self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Response,456"])
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response, "127.0.0.0")
+ self.checkProtobufTags(
+ msg.response.tags, ["TestLabel1,TestData1", "TestLabel2,TestData2", "TestLabel3,TestData3", "Response,456"]
+ )
self.assertEqual(len(msg.response.rrs), 1)
for rr in msg.response.rrs:
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 3600)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "127.0.0.1")
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
# check the protobuf message corresponding to the TCP query
msg = self.getFirstProtobufMessage()
flags = int.from_bytes(query.to_wire()[2:4], byteorder=sys.byteorder)
- self.checkProtobufQueryConvertedToResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response, '127.0.0.0', flags=flags)
- self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Query,123"])
+ self.checkProtobufQueryConvertedToResponse(
+ msg, dnsmessage_pb2.PBDNSMessage.TCP, response, "127.0.0.0", flags=flags
+ )
+ self.checkProtobufTags(
+ msg.response.tags, ["TestLabel1,TestData1", "TestLabel2,TestData2", "TestLabel3,TestData3", "Query,123"]
+ )
# check the protobuf message corresponding to the TCP response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response, '127.0.0.0')
- self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Response,456"])
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response, "127.0.0.0")
+ self.checkProtobufTags(
+ msg.response.tags, ["TestLabel1,TestData1", "TestLabel2,TestData2", "TestLabel3,TestData3", "Response,456"]
+ )
self.assertEqual(len(msg.response.rrs), 1)
for rr in msg.response.rrs:
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 3600)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "127.0.0.1")
+
class TestProtobufMetaTags(DNSDistProtobufTest):
- _config_params = ['_testServerPort', '_protobufServerPort']
+ _config_params = ["_testServerPort", "_protobufServerPort"]
_config_template = """
newServer{address="127.0.0.1:%d"}
rl = newRemoteLogger('127.0.0.1:%d')
"""
Protobuf: Meta values
"""
- name = 'meta.protobuf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "meta.protobuf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
# regular tags
self.assertEqual(len(msg.response.tags), 2)
- self.assertIn('my-tag-key:my-tag-value', msg.response.tags)
- self.assertIn('my-empty-key', msg.response.tags)
+ self.assertIn("my-tag-key:my-tag-value", msg.response.tags)
+ self.assertIn("my-empty-key", msg.response.tags)
# meta tags
self.assertEqual(len(msg.meta), 4)
tags = {}
for entry in msg.meta:
tags[entry.key] = entry.value.stringVal
- self.assertIn('b64', tags)
- self.assertIn('my-tag-export-name', tags)
+ self.assertIn("b64", tags)
+ self.assertIn("my-tag-export-name", tags)
- self.assertEqual(msg.meta[2].key, 'my-meta-key-1')
+ self.assertEqual(msg.meta[2].key, "my-meta-key-1")
self.assertEqual(len(msg.meta[2].value.stringVal), 2)
- self.assertIn('test', msg.meta[2].value.stringVal)
- self.assertIn('test2', msg.meta[2].value.stringVal)
+ self.assertIn("test", msg.meta[2].value.stringVal)
+ self.assertIn("test2", msg.meta[2].value.stringVal)
self.assertIn(-42, msg.meta[2].value.intVal)
- self.assertEqual(msg.meta[3].key, 'my-meta-key-3')
+ self.assertEqual(msg.meta[3].key, "my-meta-key-3")
self.assertEqual(len(msg.meta[2].value.stringVal), 2)
- self.assertIn('test', msg.meta[2].value.stringVal)
- self.assertIn('test2', msg.meta[2].value.stringVal)
+ self.assertIn("test", msg.meta[2].value.stringVal)
+ self.assertIn("test2", msg.meta[2].value.stringVal)
self.assertIn(-42, msg.meta[2].value.intVal)
- b64EncodedQuery = base64.b64encode(query.to_wire()).decode('ascii')
- self.assertEqual(tags['b64'], [b64EncodedQuery])
- self.assertEqual(tags['my-tag-export-name'], ['my-tag-value'])
+ b64EncodedQuery = base64.b64encode(query.to_wire()).decode("ascii")
+ self.assertEqual(tags["b64"], [b64EncodedQuery])
+ self.assertEqual(tags["my-tag-export-name"], ["my-tag-value"])
# check the protobuf message corresponding to the UDP response
msg = self.getFirstProtobufMessage()
self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response)
# regular tags
self.assertEqual(len(msg.response.tags), 2)
- self.assertIn('my-tag-key2:my-tag-value2', msg.response.tags)
- self.assertIn('my-empty-key', msg.response.tags)
+ self.assertIn("my-tag-key2:my-tag-value2", msg.response.tags)
+ self.assertIn("my-empty-key", msg.response.tags)
# meta tags
self.assertEqual(len(msg.meta), 5)
- self.assertEqual(msg.meta[0].key, 'my-tag-export-name')
+ self.assertEqual(msg.meta[0].key, "my-tag-export-name")
self.assertEqual(len(msg.meta[0].value.stringVal), 3)
- self.assertIn('my-tag-key:my-tag-value', msg.meta[0].value.stringVal)
- self.assertIn('my-tag-key2:my-tag-value2', msg.meta[0].value.stringVal)
+ self.assertIn("my-tag-key:my-tag-value", msg.meta[0].value.stringVal)
+ self.assertIn("my-tag-key2:my-tag-value2", msg.meta[0].value.stringVal)
# no ':' when the value is empty
- self.assertIn('my-empty-key', msg.meta[0].value.stringVal)
+ self.assertIn("my-empty-key", msg.meta[0].value.stringVal)
- self.assertEqual(msg.meta[1].key, 'my-meta-key-1')
+ self.assertEqual(msg.meta[1].key, "my-meta-key-1")
self.assertEqual(len(msg.meta[1].value.stringVal), 2)
- self.assertIn('test', msg.meta[1].value.stringVal)
- self.assertIn('test2', msg.meta[1].value.stringVal)
+ self.assertIn("test", msg.meta[1].value.stringVal)
+ self.assertIn("test2", msg.meta[1].value.stringVal)
self.assertIn(-42, msg.meta[1].value.intVal)
- self.assertEqual(msg.meta[2].key, 'my-meta-key-3')
+ self.assertEqual(msg.meta[2].key, "my-meta-key-3")
self.assertEqual(len(msg.meta[2].value.stringVal), 2)
- self.assertIn('test', msg.meta[2].value.stringVal)
- self.assertIn('test2', msg.meta[2].value.stringVal)
+ self.assertIn("test", msg.meta[2].value.stringVal)
+ self.assertIn("test2", msg.meta[2].value.stringVal)
self.assertIn(-42, msg.meta[2].value.intVal)
- self.assertEqual(msg.meta[3].key, 'my-meta-key-2')
+ self.assertEqual(msg.meta[3].key, "my-meta-key-2")
self.assertEqual(len(msg.meta[3].value.stringVal), 2)
- self.assertIn('foo', msg.meta[3].value.stringVal)
- self.assertIn('bar', msg.meta[3].value.stringVal)
+ self.assertIn("foo", msg.meta[3].value.stringVal)
+ self.assertIn("bar", msg.meta[3].value.stringVal)
self.assertIn(42, msg.meta[3].value.intVal)
- self.assertEqual(msg.meta[4].key, 'my-meta-key-4')
+ self.assertEqual(msg.meta[4].key, "my-meta-key-4")
self.assertEqual(len(msg.meta[4].value.stringVal), 2)
- self.assertIn('foo', msg.meta[4].value.stringVal)
- self.assertIn('bar', msg.meta[4].value.stringVal)
+ self.assertIn("foo", msg.meta[4].value.stringVal)
+ self.assertIn("bar", msg.meta[4].value.stringVal)
self.assertIn(42, msg.meta[4].value.intVal)
+
class TestProtobufExtendedDNSErrorTags(DNSDistProtobufTest):
- _config_params = ['_testServerPort', '_protobufServerPort']
+ _config_params = ["_testServerPort", "_protobufServerPort"]
_config_template = """
newServer{address="127.0.0.1:%d"}
rl = newRemoteLogger('127.0.0.1:%d')
"""
Protobuf: Extended Error
"""
- name = 'extended-error.protobuf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "extended-error.protobuf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- ede = extendederrors.ExtendedErrorOption(15, b'Blocked by RPZ!')
+ ede = extendederrors.ExtendedErrorOption(15, b"Blocked by RPZ!")
response.use_edns(edns=True, payload=4096, options=[ede])
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
# meta tags
self.assertEqual(len(msg.meta), 1)
- self.assertEqual(msg.meta[0].key, 'extended-error')
+ self.assertEqual(msg.meta[0].key, "extended-error")
self.assertEqual(len(msg.meta[0].value.intVal), 1)
self.assertEqual(len(msg.meta[0].value.stringVal), 1)
self.assertIn(15, msg.meta[0].value.intVal)
- self.assertIn('Blocked by RPZ!', msg.meta[0].value.stringVal)
+ self.assertIn("Blocked by RPZ!", msg.meta[0].value.stringVal)
+
class TestProtobufCacheHit(DNSDistProtobufTest):
- _config_params = ['_testServerPort', '_protobufServerPort']
+ _config_params = ["_testServerPort", "_protobufServerPort"]
_config_template = """
newServer{address="127.0.0.1:%d"}
rl = newRemoteLogger('127.0.0.1:%d')
"""
Protobuf: CacheHit field
"""
- name = 'cachehit.protobuf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "cachehit.protobuf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# fill the cache
# check the protobuf message corresponding to the UDP response
msg = self.getFirstProtobufMessage()
self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response)
- self.assertTrue(msg.HasField('packetCacheHit'))
+ self.assertTrue(msg.HasField("packetCacheHit"))
self.assertFalse(msg.packetCacheHit)
- self.assertTrue(msg.HasField('outgoingQueries'))
+ self.assertTrue(msg.HasField("outgoingQueries"))
self.assertEqual(msg.outgoingQueries, 1)
# now should be a cache hit
# check the protobuf message corresponding to the UDP response
msg = self.getFirstProtobufMessage()
self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response)
- self.assertTrue(msg.HasField('packetCacheHit'))
+ self.assertTrue(msg.HasField("packetCacheHit"))
self.assertTrue(msg.packetCacheHit)
- self.assertTrue(msg.HasField('outgoingQueries'))
+ self.assertTrue(msg.HasField("outgoingQueries"))
self.assertEqual(msg.outgoingQueries, 0)
-@unittest.skipIf('SKIP_DOH_TESTS' in os.environ, 'DNS over HTTPS tests are disabled')
-class TestProtobufMetaDOH(DNSDistProtobufTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+@unittest.skipIf("SKIP_DOH_TESTS" in os.environ, "DNS over HTTPS tests are disabled")
+class TestProtobufMetaDOH(DNSDistProtobufTest):
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_dohWithNGHTTP2ServerPort = pickAvailablePort()
- _dohWithNGHTTP2BaseURL = ("https://%s:%d/dns-query" % (_serverName, _dohWithNGHTTP2ServerPort))
+ _dohWithNGHTTP2BaseURL = "https://%s:%d/dns-query" % (_serverName, _dohWithNGHTTP2ServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
rl = newRemoteLogger('127.0.0.1:%d')
addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='dnsdist-server-1'}, mytags))
addResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, false, {serverID='dnsdist-server-1'}, mytags))
"""
- _config_params = ['_testServerPort', '_protobufServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_dohWithNGHTTP2ServerPort', '_serverCert', '_serverKey']
+ _config_params = [
+ "_testServerPort",
+ "_protobufServerPort",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_dohWithNGHTTP2ServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
def testProtobufMetaDoH(self):
"""
Protobuf: Meta values - DoH
"""
- name = 'meta-doh.protobuf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "meta-doh.protobuf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery", "sendDOTQueryWrapper", "sendDOHWithNGHTTP2QueryWrapper"):
self.assertEqual(len(entry.value.stringVal), 1)
tags[entry.key] = entry.value.stringVal[0]
- self.assertIn('agent', tags)
+ self.assertIn("agent", tags)
if method == "sendDOHWithNGHTTP2QueryWrapper":
- self.assertIn('PycURL', tags['agent'])
- self.assertIn('host', tags)
+ self.assertIn("PycURL", tags["agent"])
+ self.assertIn("host", tags)
if method == "sendDOHWithNGHTTP2QueryWrapper":
- self.assertEqual(tags['host'], self._serverName + ':' + str(self._dohWithNGHTTP2ServerPort))
- self.assertIn('path', tags)
- self.assertEqual(tags['path'], '/dns-query')
- self.assertIn('query-string', tags)
- self.assertIn('?dns=', tags['query-string'])
- self.assertIn('scheme', tags)
- self.assertEqual(tags['scheme'], 'https')
+ self.assertEqual(tags["host"], self._serverName + ":" + str(self._dohWithNGHTTP2ServerPort))
+ self.assertIn("path", tags)
+ self.assertEqual(tags["path"], "/dns-query")
+ self.assertIn("query-string", tags)
+ self.assertIn("?dns=", tags["query-string"])
+ self.assertIn("scheme", tags)
+ self.assertEqual(tags["scheme"], "https")
self.assertEqual(msg.httpVersion, dnsmessage_pb2.PBDNSMessage.HTTPVersion.HTTP2)
# check the protobuf message corresponding to the response
self.assertEqual(len(entry.value.stringVal), 1)
tags[entry.key] = entry.value.stringVal[0]
- self.assertIn('agent', tags)
+ self.assertIn("agent", tags)
if method == "sendDOHWithNGHTTP2QueryWrapper":
- self.assertIn('PycURL', tags['agent'])
- self.assertIn('host', tags)
+ self.assertIn("PycURL", tags["agent"])
+ self.assertIn("host", tags)
if method == "sendDOHWithNGHTTP2QueryWrapper":
- self.assertEqual(tags['host'], self._serverName + ':' + str(self._dohWithNGHTTP2ServerPort))
- self.assertIn('path', tags)
- self.assertEqual(tags['path'], '/dns-query')
- self.assertIn('query-string', tags)
- self.assertIn('?dns=', tags['query-string'])
- self.assertIn('scheme', tags)
- self.assertEqual(tags['scheme'], 'https')
+ self.assertEqual(tags["host"], self._serverName + ":" + str(self._dohWithNGHTTP2ServerPort))
+ self.assertIn("path", tags)
+ self.assertEqual(tags["path"], "/dns-query")
+ self.assertIn("query-string", tags)
+ self.assertIn("?dns=", tags["query-string"])
+ self.assertIn("scheme", tags)
+ self.assertEqual(tags["scheme"], "https")
-class TestProtobufMetaProxy(DNSDistProtobufTest):
- _config_params = ['_testServerPort', '_protobufServerPort']
+class TestProtobufMetaProxy(DNSDistProtobufTest):
+ _config_params = ["_testServerPort", "_protobufServerPort"]
_config_template = """
setProxyProtocolACL( { "127.0.0.1/32" } )
"""
Protobuf: Meta values - Proxy
"""
- name = 'meta-proxy.protobuf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "meta-proxy.protobuf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
destAddr = "2001:db8::9"
destPort = 9999
srcAddr = "2001:db8::8"
srcPort = 8888
- udpPayload = ProxyProtocol.getPayload(False, False, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 42, b'proxy'] ])
+ udpPayload = ProxyProtocol.getPayload(
+ False, False, True, srcAddr, destAddr, srcPort, destPort, [[2, b"foo"], [42, b"proxy"]]
+ )
(receivedQuery, receivedResponse) = self.sendUDPQuery(udpPayload + query.to_wire(), response, rawQuery=True)
self.assertTrue(receivedQuery)
# check the protobuf message corresponding to the UDP query
msg = self.getFirstProtobufMessage()
- self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, initiator='2001:db8::8', v6=True)
+ self.checkProtobufQuery(
+ msg,
+ dnsmessage_pb2.PBDNSMessage.UDP,
+ query,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ name,
+ initiator="2001:db8::8",
+ v6=True,
+ )
self.assertEqual(len(msg.meta), 2)
tags = {}
for entry in msg.meta:
tags[entry.key] = entry.value.stringVal
- self.assertIn('pp42', tags)
- self.assertEqual(tags['pp42'], ['proxy'])
- self.assertIn('pp', tags)
- self.assertEqual(len(tags['pp']), 2)
- self.assertIn('2:foo', tags['pp'])
- self.assertIn('42:proxy', tags['pp'])
+ self.assertIn("pp42", tags)
+ self.assertEqual(tags["pp42"], ["proxy"])
+ self.assertIn("pp", tags)
+ self.assertEqual(len(tags["pp"]), 2)
+ self.assertIn("2:foo", tags["pp"])
+ self.assertIn("42:proxy", tags["pp"])
+
class TestProtobufIPCipher(DNSDistProtobufTest):
- _config_params = ['_testServerPort', '_protobufServerPort', '_protobufServerID', '_protobufServerID']
+ _config_params = ["_testServerPort", "_protobufServerPort", "_protobufServerID", "_protobufServerID"]
_config_template = """
newServer{address="127.0.0.1:%d", useClientSubnet=true}
key = makeIPCipherKey("some 16-byte key")
"""
Protobuf: Send data to a protobuf server, with pseudonymization
"""
- name = 'query.protobuf-ipcipher.tests.powerdns.com.'
+ name = "query.protobuf-ipcipher.tests.powerdns.com."
- target = 'target.protobuf-ipcipher.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.protobuf-ipcipher.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
msg = self.getFirstProtobufMessage()
# 108.41.239.98 is 127.0.0.1 pseudonymized with ipcipher and the current key
- self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '108.41.239.98')
+ self.checkProtobufQuery(
+ msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, "108.41.239.98"
+ )
# check the protobuf message corresponding to the UDP response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response, '108.41.239.98')
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response, "108.41.239.98")
self.assertEqual(len(msg.response.rrs), 2)
rr = msg.response.rrs[0]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600)
- self.assertEqual(rr.rdata.decode('ascii'), target)
+ self.assertEqual(rr.rdata.decode("ascii"), target)
rr = msg.response.rrs[1]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "127.0.0.1")
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
# check the protobuf message corresponding to the TCP query
msg = self.getFirstProtobufMessage()
# 108.41.239.98 is 127.0.0.1 pseudonymized with ipcipher and the current key
- self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.TCP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '108.41.239.98')
+ self.checkProtobufQuery(
+ msg, dnsmessage_pb2.PBDNSMessage.TCP, query, dns.rdataclass.IN, dns.rdatatype.A, name, "108.41.239.98"
+ )
# check the protobuf message corresponding to the TCP response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response, '108.41.239.98')
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response, "108.41.239.98")
self.assertEqual(len(msg.response.rrs), 2)
rr = msg.response.rrs[0]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600)
- self.assertEqual(rr.rdata.decode('ascii'), target)
+ self.assertEqual(rr.rdata.decode("ascii"), target)
rr = msg.response.rrs[1]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "127.0.0.1")
+
class TestProtobufIPCrypt2PFX(DNSDistProtobufTest):
- _config_params = ['_testServerPort', '_protobufServerPort', '_protobufServerID', '_protobufServerID']
+ _config_params = ["_testServerPort", "_protobufServerPort", "_protobufServerID", "_protobufServerID"]
_config_template = """
newServer{address="127.0.0.1:%d", useClientSubnet=true}
rl = newRemoteLogger('127.0.0.1:%d')
"""
Protobuf: Send data to a protobuf server, with pseudonymization
"""
- name = 'query.protobuf-ipcipher.tests.powerdns.com.'
+ name = "query.protobuf-ipcipher.tests.powerdns.com."
- target = 'target.protobuf-ipcipher.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ target = "target.protobuf-ipcipher.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- target)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.CNAME, target)
response.answer.append(rrset)
- rrset = dns.rrset.from_text(target,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(target, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
msg = self.getFirstProtobufMessage()
# 108.41.239.98 is 127.0.0.1 pseudonymized with ipcrypt2-pfx and the current key
- self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '109.33.15.148')
+ self.checkProtobufQuery(
+ msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, "109.33.15.148"
+ )
# check the protobuf message corresponding to the UDP response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response, '109.33.15.148')
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response, "109.33.15.148")
self.assertEqual(len(msg.response.rrs), 2)
rr = msg.response.rrs[0]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600)
- self.assertEqual(rr.rdata.decode('ascii'), target)
+ self.assertEqual(rr.rdata.decode("ascii"), target)
rr = msg.response.rrs[1]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "127.0.0.1")
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
# check the protobuf message corresponding to the TCP query
msg = self.getFirstProtobufMessage()
# 108.41.239.98 is 127.0.0.1 pseudonymized with ipcrypt2-pfx and the current key
- self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.TCP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '109.33.15.148')
+ self.checkProtobufQuery(
+ msg, dnsmessage_pb2.PBDNSMessage.TCP, query, dns.rdataclass.IN, dns.rdatatype.A, name, "109.33.15.148"
+ )
# check the protobuf message corresponding to the TCP response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response, '109.33.15.148')
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response, "109.33.15.148")
self.assertEqual(len(msg.response.rrs), 2)
rr = msg.response.rrs[0]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600)
- self.assertEqual(rr.rdata.decode('ascii'), target)
+ self.assertEqual(rr.rdata.decode("ascii"), target)
rr = msg.response.rrs[1]
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "127.0.0.1")
class TestProtobufQUIC(DNSDistProtobufTest):
-
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_doqServerPort = pickAvailablePort()
_doh3ServerPort = pickAvailablePort()
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doh3ServerPort))
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doh3ServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
rl = newRemoteLogger('127.0.0.1:%d')
addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='dnsdist-server-1'}))
"""
- _config_params = ['_testServerPort', '_protobufServerPort', '_doqServerPort', '_serverCert', '_serverKey', '_doh3ServerPort', '_serverCert', '_serverKey']
+ _config_params = [
+ "_testServerPort",
+ "_protobufServerPort",
+ "_doqServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_doh3ServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
def testProtobufMetaDoH(self):
"""
Protobuf: Test logged protocol for QUIC and DOH3
"""
- name = 'quic.protobuf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "quic.protobuf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendDOQQueryWrapper", "sendDOH3QueryWrapper"):
self.checkProtobufQuery(msg, pbMessageType, query, dns.rdataclass.IN, dns.rdatatype.A, name)
+
class TestProtobufAXFR(DNSDistProtobufTest):
# this test suite uses a different responder port
# because, contrary to the other ones, its
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Protobuf AXFR Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Protobuf AXFR Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._TCPResponder = threading.Thread(name='TCP Protobuf AXFR Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, True, None, None, True])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Protobuf AXFR Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, True, None, None, True],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._protobufListener = threading.Thread(name='Protobuf Listener', target=cls.ProtobufListener, args=[cls._protobufServerPort])
+ cls._protobufListener = threading.Thread(
+ name="Protobuf Listener", target=cls.ProtobufListener, args=[cls._protobufServerPort]
+ )
cls._protobufListener.daemon = True
cls._protobufListener.start()
addXFRResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, false, {serverID='dnsdist-server-1'}))
"""
- _config_params = ['_testServerPort', '_protobufServerPort']
+ _config_params = ["_testServerPort", "_protobufServerPort"]
def testProtobufAXFR(self):
"""
Protobuf: Check the logging of multiple messages for AXFR responses
"""
# first query is NOT an AXFR, we should not log anything
- name = 'axfr.protobuf.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "axfr.protobuf.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
ttl = 60
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(self._protobufQueue.empty())
- query = dns.message.make_query(name, 'AXFR', 'IN')
+ query = dns.message.make_query(name, "AXFR", "IN")
responses = []
- soa = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+ soa = dns.rrset.from_text(
+ name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
response = dns.message.make_response(query)
response.answer.append(soa)
responses.append(response)
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1'))
+ response.answer.append(dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"))
responses.append(response)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:db8::1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:db8::1")
response.answer.append(rrset)
responses.append(response)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- 'dummy')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.TXT, "dummy")
response.answer.append(rrset)
responses.append(response)
while not self._protobufQueue.empty():
msg = self.getFirstProtobufMessage()
count = count + 1
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, responses[count-1])
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, responses[count - 1])
- expected = responses[count-1].answer[0]
+ expected = responses[count - 1].answer[0]
if expected.rdtype in [dns.rdatatype.A, dns.rdatatype.AAAA]:
rr = msg.response.rrs[0]
self.checkProtobufResponseRecord(rr, expected.rdclass, expected.rdtype, name, ttl)
if expected.rdtype == dns.rdatatype.A:
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.1")
else:
- self.assertEqual(socket.inet_ntop(socket.AF_INET6, rr.rdata), '2001:db8::1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET6, rr.rdata), "2001:db8::1")
self.assertEqual(count, len(responses))
-class TestYamlProtobuf(DNSDistProtobufTest):
+class TestYamlProtobuf(DNSDistProtobufTest):
_yaml_config_template = """---
binds:
- listen_address: "127.0.0.1:%d"
"""
_dnsDistPort = pickAvailablePort()
_testServerPort = pickAvailablePort()
- _yaml_config_params = ['_dnsDistPort', '_testServerPort', '_protobufServerPort', '_protobufServerID']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort", "_protobufServerPort", "_protobufServerID"]
_config_params = []
def testProtobuf(self):
"""
Yaml: Remote logging via protobuf
"""
- name = 'remote-logging.protobuf.yaml.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "remote-logging.protobuf.yaml.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
-
if self._protobufQueue.empty():
# let the protobuf messages the time to get there
time.sleep(1)
"""
_dnsDistPort = pickAvailablePort()
_testServerPort = pickAvailablePort()
- _yaml_config_params = ['_dnsDistPort', '_testServerPort', '_protobufServerPort', '_protobufServerID']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort", "_protobufServerPort", "_protobufServerID"]
_config_params = []
def testProtobuf(self):
"""
Yaml: Remote logging via protobuf
"""
- name = 'remote-logging.protobuf.yaml.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "remote-logging.protobuf.yaml.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
class TestTimeoutResponseRuleProtobuf(DNSDistProtobufTest):
-
_yaml_config_template = """---
binds:
- listen_address: "127.0.0.1:%d"
_dnsDistPort = pickAvailablePort()
_testServerPortNotListening = pickAvailablePort()
_testServerPort = pickAvailablePort()
- _yaml_config_params = ['_dnsDistPort', '_testServerPortNotListening', '_protobufServerPort', '_protobufServerID']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPortNotListening", "_protobufServerPort", "_protobufServerID"]
_config_params = []
def testProtobuf(self):
"""
Yaml: Remote logging via protobuf of timeouts
"""
- name = 'remote-logging-timeout.protobuf.yaml.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "remote-logging-timeout.protobuf.yaml.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
self.assertEqual(receivedQuery, None)
self.assertEqual(receivedResponse, None)
-
# the UDP timeout usually takes longer to be detected
# than the TCP one
gotUDP = False
protocol = dnsmessage_pb2.PBDNSMessage.TCP
self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSResponseType)
- self.checkProtobufBase(msg, protocol, response, '127.0.0.1', normalQueryResponse=False, v6=False)
- self.assertTrue(msg.HasField('response'))
- self.assertTrue(msg.response.HasField('queryTimeSec'))
- self.assertTrue(msg.HasField('question'))
- self.assertTrue(msg.question.HasField('qClass'))
- self.assertTrue(msg.question.HasField('qType'))
- self.assertTrue(msg.question.HasField('qName'))
+ self.checkProtobufBase(msg, protocol, response, "127.0.0.1", normalQueryResponse=False, v6=False)
+ self.assertTrue(msg.HasField("response"))
+ self.assertTrue(msg.response.HasField("queryTimeSec"))
+ self.assertTrue(msg.HasField("question"))
+ self.assertTrue(msg.question.HasField("qClass"))
+ self.assertTrue(msg.question.HasField("qType"))
+ self.assertTrue(msg.question.HasField("qName"))
self.assertEqual(msg.question.qName, name)
self.assertTrue(gotUDP)
self.assertTrue(gotTCP)
+
class TestProtobufGlobalServerIDYaml(TestYamlProtobuf):
_yaml_config_template = """---
binds:
- "tag-2"
"""
+
class TestProtobufGlobalServerIDLua(DNSDistProtobufTest):
- _config_params = ['_protobufServerID', '_testServerPort', '_protobufServerPort']
+ _config_params = ["_protobufServerID", "_testServerPort", "_protobufServerPort"]
_config_template = """
setServerID("%s")
luasmn = newSuffixMatchNode()
from dnsdistdohtests import DNSDistDOHTest
from dnsdisttests import DNSDistTest, pickAvailablePort
from proxyprotocol import ProxyProtocol
-from proxyprotocolutils import (ProxyProtocolTCPResponder,
- ProxyProtocolUDPResponder)
+from proxyprotocolutils import ProxyProtocolTCPResponder, ProxyProtocolUDPResponder
# Python2/3 compatibility hacks
try:
- from queue import Queue
+ from queue import Queue
except ImportError:
- from Queue import Queue
+ from Queue import Queue
toProxyQueue = Queue()
fromProxyQueue = Queue()
proxyResponderPort = pickAvailablePort()
-udpResponder = threading.Thread(name='UDP Proxy Protocol Responder', target=ProxyProtocolUDPResponder, args=[proxyResponderPort, toProxyQueue, fromProxyQueue])
+udpResponder = threading.Thread(
+ name="UDP Proxy Protocol Responder",
+ target=ProxyProtocolUDPResponder,
+ args=[proxyResponderPort, toProxyQueue, fromProxyQueue],
+)
udpResponder.daemon = True
udpResponder.start()
-tcpResponder = threading.Thread(name='TCP Proxy Protocol Responder', target=ProxyProtocolTCPResponder, args=[proxyResponderPort, toProxyQueue, fromProxyQueue])
+tcpResponder = threading.Thread(
+ name="TCP Proxy Protocol Responder",
+ target=ProxyProtocolTCPResponder,
+ args=[proxyResponderPort, toProxyQueue, fromProxyQueue],
+)
tcpResponder.daemon = True
tcpResponder.start()
backgroundThreads = {}
+
def MockTCPReverseProxyAddingProxyProtocol(listeningPort, forwardingPort, serverCtx=None, ca=None, sni=None):
# this responder accepts TCP connections on the listening port,
# and relay the raw content to a second TCP connection to the
del backgroundThreads[threading.get_native_id()]
break
else:
- continue
+ continue
incoming.settimeout(5.0)
- payload = ProxyProtocol.getPayload(False, True, False, '127.0.0.1', '127.0.0.1', incoming.getpeername()[1], listeningPort, [ [ 2, b'foo'], [ 3, b'proxy'] ])
+ payload = ProxyProtocol.getPayload(
+ False,
+ True,
+ False,
+ "127.0.0.1",
+ "127.0.0.1",
+ incoming.getpeername()[1],
+ listeningPort,
+ [[2, b"foo"], [3, b"proxy"]],
+ )
outgoing = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
outgoing.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
outgoing.settimeout(2.0)
if sni:
- if hasattr(ssl, 'create_default_context'):
+ if hasattr(ssl, "create_default_context"):
sslctx = ssl.create_default_context(cafile=ca)
- if hasattr(sslctx, 'set_alpn_protocols'):
- sslctx.set_alpn_protocols(['h2'])
+ if hasattr(sslctx, "set_alpn_protocols"):
+ sslctx.set_alpn_protocols(["h2"])
outgoing = sslctx.wrap_socket(outgoing, server_hostname=sni)
else:
outgoing = ssl.wrap_socket(outgoing, ca_certs=ca, cert_reqs=ssl.CERT_REQUIRED)
- outgoing.connect(('127.0.0.1', forwardingPort))
+ outgoing.connect(("127.0.0.1", forwardingPort))
outgoing.send(payload)
sel = selectors.DefaultSelector()
+
def readFromClient(conn):
data = conn.recv(512)
if not data or len(data) == 0:
- return False
+ return False
outgoing.send(data)
return True
def readFromBackend(conn):
data = conn.recv(512)
if not data or len(data) == 0:
- return False
+ return False
incoming.send(data)
return True
sel.register(outgoing, selectors.EVENT_READ, readFromBackend)
done = False
while not done:
- try:
- events = sel.select()
- for key, mask in events:
- if not (key.data)(key.fileobj):
- done = True
+ try:
+ events = sel.select()
+ for key, mask in events:
+ if not (key.data)(key.fileobj):
+ done = True
+ break
+ except socket.timeout:
+ break
+ except Exception:
break
- except socket.timeout:
- break
- except Exception:
- break
incoming.close()
outgoing.close()
sock.close()
+
class ProxyProtocolTest(DNSDistTest):
_proxyResponderPort = proxyResponderPort
- _config_params = ['_proxyResponderPort']
+ _config_params = ["_proxyResponderPort"]
+
class TestProxyProtocol(ProxyProtocolTest):
"""
addAction("random-values.proxy.tests.powerdns.com.", LuaAction(addRandomValue))
"""
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKey = 'apisecret'
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
- _config_params = ['_proxyResponderPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _webServerBasicAuthPassword = "secret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKey = "apisecret"
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
+ _config_params = [
+ "_proxyResponderPort",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
def getServerStats(self):
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
- r = requests.get(url, headers=headers, timeout=1)
- self.assertTrue(r)
- self.assertEqual(r.status_code, 200)
- self.assertTrue(r.json())
- content = r.json()
- return content['servers']
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost"
+ r = requests.get(url, headers=headers, timeout=1)
+ self.assertTrue(r)
+ self.assertEqual(r.status_code, 200)
+ self.assertTrue(r.json())
+ content = r.json()
+ return content["servers"]
def testProxyUDP(self):
"""
Proxy Protocol: no value (UDP)
"""
- name = 'simple-udp.proxy.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "simple-udp.proxy.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
toProxyQueue.put(response, True, 2.0)
self._sock.settimeout(2.0)
data = self._sock.recv(4096)
except socket.timeout:
- print('timeout')
+ print("timeout")
data = None
if data:
receivedResponse = dns.message.from_wire(data)
receivedResponse.id = response.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', False)
+ self.checkMessageProxyProtocol(receivedProxyPayload, "127.0.0.1", "127.0.0.1", False)
def testProxyTCP(self):
- """
+ """
Proxy Protocol: no value (TCP)
- """
- name = 'simple-tcp.proxy.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- response = dns.message.make_response(query)
-
- toProxyQueue.put(response, True, 2.0)
-
- conn = self.openTCPConnection(2.0)
- data = query.to_wire()
- self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
- receivedResponse = None
- try:
- receivedResponse = self.recvTCPResponseOverConnection(conn)
- except socket.timeout:
- print('timeout')
+ """
+ name = "simple-tcp.proxy.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ response = dns.message.make_response(query)
- (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
- self.assertTrue(receivedProxyPayload)
- self.assertTrue(receivedDNSData)
- self.assertTrue(receivedResponse)
+ toProxyQueue.put(response, True, 2.0)
- receivedQuery = dns.message.from_wire(receivedDNSData)
- receivedQuery.id = query.id
- receivedResponse.id = response.id
- self.assertEqual(receivedQuery, query)
- self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True)
+ conn = self.openTCPConnection(2.0)
+ data = query.to_wire()
+ self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
+ receivedResponse = None
+ try:
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
+ except socket.timeout:
+ print("timeout")
+
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEqual(receivedQuery, query)
+ self.assertEqual(receivedResponse, response)
+ self.checkMessageProxyProtocol(receivedProxyPayload, "127.0.0.1", "127.0.0.1", True)
def testProxyUDPWithValuesFromLua(self):
"""
Proxy Protocol: values from Lua (UDP)
"""
- name = 'values-lua.proxy.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "values-lua.proxy.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
toProxyQueue.put(response, True, 2.0)
self._sock.settimeout(2.0)
data = self._sock.recv(4096)
except socket.timeout:
- print('timeout')
+ print("timeout")
data = None
if data:
receivedResponse = dns.message.from_wire(data)
receivedResponse.id = response.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', False, [ [0, b'foo'] , [ 42, b'bar'] ])
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload, "127.0.0.1", "127.0.0.1", False, [[0, b"foo"], [42, b"bar"]]
+ )
def testProxyTCPWithValuesFromLua(self):
- """
+ """
Proxy Protocol: values from Lua (TCP)
- """
- name = 'values-lua.proxy.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- response = dns.message.make_response(query)
-
- toProxyQueue.put(response, True, 2.0)
-
- conn = self.openTCPConnection(2.0)
- data = query.to_wire()
- self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
- receivedResponse = None
- try:
- receivedResponse = self.recvTCPResponseOverConnection(conn)
- except socket.timeout:
- print('timeout')
+ """
+ name = "values-lua.proxy.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ response = dns.message.make_response(query)
+
+ toProxyQueue.put(response, True, 2.0)
+
+ conn = self.openTCPConnection(2.0)
+ data = query.to_wire()
+ self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
+ receivedResponse = None
+ try:
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
+ except socket.timeout:
+ print("timeout")
- (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
- self.assertTrue(receivedProxyPayload)
- self.assertTrue(receivedDNSData)
- self.assertTrue(receivedResponse)
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
- receivedQuery = dns.message.from_wire(receivedDNSData)
- receivedQuery.id = query.id
- receivedResponse.id = response.id
- self.assertEqual(receivedQuery, query)
- self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'] , [ 42, b'bar'] ])
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEqual(receivedQuery, query)
+ self.assertEqual(receivedResponse, response)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload, "127.0.0.1", "127.0.0.1", True, [[0, b"foo"], [42, b"bar"]]
+ )
def testProxyUDPWithValuesFromAction(self):
"""
Proxy Protocol: values from Action (UDP)
"""
- name = 'values-action.proxy.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "values-action.proxy.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
toProxyQueue.put(response, True, 2.0)
self._sock.settimeout(2.0)
data = self._sock.recv(4096)
except socket.timeout:
- print('timeout')
+ print("timeout")
data = None
if data:
receivedResponse = dns.message.from_wire(data)
receivedResponse.id = response.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', False, [ [1, b'dnsdist'] , [ 255, b'proxy-protocol'] ])
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload, "127.0.0.1", "127.0.0.1", False, [[1, b"dnsdist"], [255, b"proxy-protocol"]]
+ )
def testProxyTCPWithValuesFromAction(self):
- """
+ """
Proxy Protocol: values from Action (TCP)
- """
- name = 'values-action.proxy.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- response = dns.message.make_response(query)
-
- toProxyQueue.put(response, True, 2.0)
-
- conn = self.openTCPConnection(2.0)
- data = query.to_wire()
- self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
- receivedResponse = None
- try:
- receivedResponse = self.recvTCPResponseOverConnection(conn)
- except socket.timeout:
- print('timeout')
-
- (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
- self.assertTrue(receivedProxyPayload)
- self.assertTrue(receivedDNSData)
- self.assertTrue(receivedResponse)
-
- receivedQuery = dns.message.from_wire(receivedDNSData)
- receivedQuery.id = query.id
- receivedResponse.id = response.id
- self.assertEqual(receivedQuery, query)
- self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [1, b'dnsdist'] , [ 255, b'proxy-protocol'] ])
-
- def testProxyTCPSeveralQueriesOnSameConnection(self):
- """
- Proxy Protocol: Several queries on the same TCP connection
- """
- name = 'several-queries-same-conn.proxy.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- response = dns.message.make_response(query)
-
- new_conn_before = self.getServerStats()[0]['tcpNewConnections']
- reused_conn_before = self.getServerStats()[0]['tcpReusedConnections']
- max_conn_before = self.getServerStats()[0]['tcpMaxConcurrentConnections']
-
- conn = self.openTCPConnection(2.0)
- data = query.to_wire()
+ """
+ name = "values-action.proxy.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ response = dns.message.make_response(query)
- for idx in range(10):
toProxyQueue.put(response, True, 2.0)
+
+ conn = self.openTCPConnection(2.0)
+ data = query.to_wire()
self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
receivedResponse = None
try:
- receivedResponse = self.recvTCPResponseOverConnection(conn)
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
except socket.timeout:
- print('timeout')
+ print("timeout")
(receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
self.assertTrue(receivedProxyPayload)
receivedResponse.id = response.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [])
-
- # check:
- # - only one concurrent connection to the backend
- # - no more than 1 new connection to the backend was opened
- server = self.getServerStats()[0]
- self.assertEqual(server['tcpNewConnections'], new_conn_before + 1)
- self.assertEqual(server['tcpReusedConnections'], reused_conn_before + 9)
- # we can only check that we did not open more than one new connection
- # compared to the connections that existed before, because connections
- # triggered by a different test can still be around
- self.assertLessEqual(server['tcpMaxConcurrentConnections'], max_conn_before + 1)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload, "127.0.0.1", "127.0.0.1", True, [[1, b"dnsdist"], [255, b"proxy-protocol"]]
+ )
+
+ def testProxyTCPSeveralQueriesOnSameConnection(self):
+ """
+ Proxy Protocol: Several queries on the same TCP connection
+ """
+ name = "several-queries-same-conn.proxy.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ response = dns.message.make_response(query)
+
+ new_conn_before = self.getServerStats()[0]["tcpNewConnections"]
+ reused_conn_before = self.getServerStats()[0]["tcpReusedConnections"]
+ max_conn_before = self.getServerStats()[0]["tcpMaxConcurrentConnections"]
+
+ conn = self.openTCPConnection(2.0)
+ data = query.to_wire()
+
+ for idx in range(10):
+ toProxyQueue.put(response, True, 2.0)
+ self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
+ receivedResponse = None
+ try:
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
+ except socket.timeout:
+ print("timeout")
+
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEqual(receivedQuery, query)
+ self.assertEqual(receivedResponse, response)
+ self.checkMessageProxyProtocol(receivedProxyPayload, "127.0.0.1", "127.0.0.1", True, [])
+
+ # check:
+ # - only one concurrent connection to the backend
+ # - no more than 1 new connection to the backend was opened
+ server = self.getServerStats()[0]
+ self.assertEqual(server["tcpNewConnections"], new_conn_before + 1)
+ self.assertEqual(server["tcpReusedConnections"], reused_conn_before + 9)
+ # we can only check that we did not open more than one new connection
+ # compared to the connections that existed before, because connections
+ # triggered by a different test can still be around
+ self.assertLessEqual(server["tcpMaxConcurrentConnections"], max_conn_before + 1)
def testProxyTCPSeveralQueriesWithRandomTLVOnSameConnection(self):
- """
+ """
Proxy Protocol: Several queries with random TLV on the same TCP connection
- """
- name = 'random-values.proxy.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- response = dns.message.make_response(query)
-
- stats = self.getServerStats()[0]
- max_conns_before = stats['tcpMaxConcurrentConnections']
- current_conns_before = stats['tcpCurrentConnections']
- new_conn_before = stats['tcpNewConnections']
- reused_conn_before = stats['tcpReusedConnections']
-
- conn = self.openTCPConnection(2.0)
- data = query.to_wire()
-
- number_of_queries = 10
- for idx in range(number_of_queries):
- toProxyQueue.put(response, True, 2.0)
- self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
- receivedResponse = None
- try:
- receivedResponse = self.recvTCPResponseOverConnection(conn)
- except socket.timeout:
- print('timeout')
+ """
+ name = "random-values.proxy.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ response = dns.message.make_response(query)
- (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
- self.assertTrue(receivedProxyPayload)
- self.assertTrue(receivedDNSData)
- self.assertTrue(receivedResponse)
+ stats = self.getServerStats()[0]
+ max_conns_before = stats["tcpMaxConcurrentConnections"]
+ current_conns_before = stats["tcpCurrentConnections"]
+ new_conn_before = stats["tcpNewConnections"]
+ reused_conn_before = stats["tcpReusedConnections"]
+
+ conn = self.openTCPConnection(2.0)
+ data = query.to_wire()
+
+ number_of_queries = 10
+ for idx in range(number_of_queries):
+ toProxyQueue.put(response, True, 2.0)
+ self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
+ receivedResponse = None
+ try:
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
+ except socket.timeout:
+ print("timeout")
+
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEqual(receivedQuery, query)
+ self.assertEqual(receivedResponse, response)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload, "127.0.0.1", "127.0.0.1", True, [[238, str(idx + 1).encode("UTF-8")]]
+ )
+
+ # check:
+ # - only one concurrent connection to the backend
+ # - no more than number_of_queries connections to the backend were opened
+ server = self.getServerStats()[0]
+ self.assertEqual(server["tcpNewConnections"], new_conn_before + number_of_queries)
+ self.assertEqual(server["tcpReusedConnections"], reused_conn_before)
+ # in some cases existing (established before this test) connections to the backend might still
+ # exist, so we cannot enforce a strict "only 1 connection" check
+ self.assertLessEqual(server["tcpMaxConcurrentConnections"], max_conns_before + 1)
+ # but if we managed to add more than two connections to the existing ones, something is
+ # wrong!
+ # why two and not one? when a query arrives we retrieve the "owned" outgoing connection to the backend,
+ # and we notice that the TLVs are different than the previous ones, so we discard the outgoing connection
+ # and create a new one. This should lead to only one concurrent connection, except that if we read the
+ # new query while being called from the function handling the response from the backend, we might still
+ # hold a shared reference to the previous connection. It will be released when we come back to the calling
+ # function, but for a short time we will have two concurrent connections.
+ self.assertLessEqual(server["tcpMaxConcurrentConnections"], current_conns_before + 2)
- receivedQuery = dns.message.from_wire(receivedDNSData)
- receivedQuery.id = query.id
- receivedResponse.id = response.id
- self.assertEqual(receivedQuery, query)
- self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [[238, str(idx + 1).encode('UTF-8')]])
-
- # check:
- # - only one concurrent connection to the backend
- # - no more than number_of_queries connections to the backend were opened
- server = self.getServerStats()[0]
- self.assertEqual(server['tcpNewConnections'], new_conn_before + number_of_queries)
- self.assertEqual(server['tcpReusedConnections'], reused_conn_before)
- # in some cases existing (established before this test) connections to the backend might still
- # exist, so we cannot enforce a strict "only 1 connection" check
- self.assertLessEqual(server['tcpMaxConcurrentConnections'], max_conns_before + 1)
- # but if we managed to add more than two connections to the existing ones, something is
- # wrong!
- # why two and not one? when a query arrives we retrieve the "owned" outgoing connection to the backend,
- # and we notice that the TLVs are different than the previous ones, so we discard the outgoing connection
- # and create a new one. This should lead to only one concurrent connection, except that if we read the
- # new query while being called from the function handling the response from the backend, we might still
- # hold a shared reference to the previous connection. It will be released when we come back to the calling
- # function, but for a short time we will have two concurrent connections.
- self.assertLessEqual(server['tcpMaxConcurrentConnections'], current_conns_before + 2)
class TestProxyProtocolTraceParent(TestProxyProtocol):
# dnsdist adds a TRACEPARENT EDNS option.
addAction("random-values.proxy.tests.powerdns.com.", LuaAction(addRandomValue))
"""
+
class TestProxyProtocolIncoming(ProxyProtocolTest):
"""
dnsdist is configured to prepend a Proxy Protocol header to the query and expect one on incoming queries
-- override all existing values
addAction("override.proxy-protocol-incoming.tests.powerdns.com.", SetProxyProtocolValuesAction({["50"]="overridden"}))
"""
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohServerPPOutsidePort = pickAvailablePort()
_dohServerPPInsidePort = pickAvailablePort()
_dotServerPPOutsidePort = pickAvailablePort()
_dotServerPPInsidePort = pickAvailablePort()
- _config_params = ['_dohServerPPOutsidePort', '_serverCert', '_serverKey', '_dohServerPPInsidePort', '_serverCert', '_serverKey', '_dotServerPPOutsidePort', '_serverCert', '_serverKey', '_dotServerPPInsidePort', '_serverCert', '_serverKey', '_proxyResponderPort']
+ _config_params = [
+ "_dohServerPPOutsidePort",
+ "_serverCert",
+ "_serverKey",
+ "_dohServerPPInsidePort",
+ "_serverCert",
+ "_serverKey",
+ "_dotServerPPOutsidePort",
+ "_serverCert",
+ "_serverKey",
+ "_dotServerPPInsidePort",
+ "_serverCert",
+ "_serverKey",
+ "_proxyResponderPort",
+ ]
def testNoHeader(self):
"""
Incoming Proxy Protocol: no header
"""
# no proxy protocol header while one is expected, should be dropped
- name = 'no-header.incoming-proxy-protocol.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "no-header.incoming-proxy-protocol.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
for method in ("sendUDPQuery", "sendTCPQuery", "sendDOHQueryWrapper"):
- sender = getattr(self, method)
- try:
- (_, receivedResponse) = sender(query, response=None)
- except Exception:
- receivedResponse = None
- self.assertEqual(receivedResponse, None)
+ sender = getattr(self, method)
+ try:
+ (_, receivedResponse) = sender(query, response=None)
+ except Exception:
+ receivedResponse = None
+ self.assertEqual(receivedResponse, None)
def testIncomingProxyDest(self):
"""
Incoming Proxy Protocol: get forwarded destination
"""
- name = 'get-forwarded-dest.proxy-protocol-incoming.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "get-forwarded-dest.proxy-protocol-incoming.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
srcAddr = "2001:db8::8"
srcPort = 8888
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- "address-was-{}-port-was-{}.proxy-protocol-incoming.tests.powerdns.com.".format(destAddr, destPort))
+ rrset = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.CNAME,
+ "address-was-{}-port-was-{}.proxy-protocol-incoming.tests.powerdns.com.".format(destAddr, destPort),
+ )
response.answer.append(rrset)
- udpPayload = ProxyProtocol.getPayload(False, False, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 3, b'proxy'] ])
- (_, receivedResponse) = self.sendUDPQuery(udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True)
+ udpPayload = ProxyProtocol.getPayload(
+ False, False, True, srcAddr, destAddr, srcPort, destPort, [[2, b"foo"], [3, b"proxy"]]
+ )
+ (_, receivedResponse) = self.sendUDPQuery(
+ udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True
+ )
self.assertEqual(receivedResponse, response)
- tcpPayload = ProxyProtocol.getPayload(False, True, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 3, b'proxy'] ])
+ tcpPayload = ProxyProtocol.getPayload(
+ False, True, True, srcAddr, destAddr, srcPort, destPort, [[2, b"foo"], [3, b"proxy"]]
+ )
wire = query.to_wire()
receivedResponse = None
try:
- conn = self.openTCPConnection(2.0)
- conn.send(tcpPayload)
- conn.send(struct.pack("!H", len(wire)))
- conn.send(wire)
- receivedResponse = self.recvTCPResponseOverConnection(conn)
+ conn = self.openTCPConnection(2.0)
+ conn.send(tcpPayload)
+ conn.send(struct.pack("!H", len(wire)))
+ conn.send(wire)
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
except socket.timeout:
- print('timeout')
+ print("timeout")
self.assertEqual(receivedResponse, response)
def testProxyUDPWithValuesFromLua(self):
"""
Incoming Proxy Protocol: values from Lua (UDP)
"""
- name = 'values-lua.proxy-protocol-incoming.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "values-lua.proxy-protocol-incoming.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
destAddr = "2001:db8::9"
srcAddr = "2001:db8::8"
srcPort = 8888
- udpPayload = ProxyProtocol.getPayload(False, False, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 3, b'proxy'] ])
+ udpPayload = ProxyProtocol.getPayload(
+ False, False, True, srcAddr, destAddr, srcPort, destPort, [[2, b"foo"], [3, b"proxy"]]
+ )
toProxyQueue.put(response, True, 2.0)
- (_, receivedResponse) = self.sendUDPQuery(udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True)
+ (_, receivedResponse) = self.sendUDPQuery(
+ udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True
+ )
(receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
self.assertTrue(receivedProxyPayload)
receivedResponse.id = response.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, srcAddr, destAddr, False, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [ 42, b'bar'], [255, b'proxy-protocol'] ], True, srcPort, destPort)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload,
+ srcAddr,
+ destAddr,
+ False,
+ [[0, b"foo"], [1, b"dnsdist"], [2, b"foo"], [3, b"proxy"], [42, b"bar"], [255, b"proxy-protocol"]],
+ True,
+ srcPort,
+ destPort,
+ )
def testProxyTCPWithValuesFromLua(self):
"""
Incoming Proxy Protocol: values from Lua (TCP)
"""
- name = 'values-lua.proxy-protocol-incoming.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "values-lua.proxy-protocol-incoming.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
destAddr = "2001:db8::9"
srcAddr = "2001:db8::8"
srcPort = 8888
- tcpPayload = ProxyProtocol.getPayload(False, True, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 3, b'proxy'] ])
+ tcpPayload = ProxyProtocol.getPayload(
+ False, True, True, srcAddr, destAddr, srcPort, destPort, [[2, b"foo"], [3, b"proxy"]]
+ )
toProxyQueue.put(response, True, 2.0)
receivedResponse = None
try:
- conn = self.openTCPConnection(2.0)
- conn.send(tcpPayload)
- conn.send(struct.pack("!H", len(wire)))
- conn.send(wire)
- receivedResponse = self.recvTCPResponseOverConnection(conn)
+ conn = self.openTCPConnection(2.0)
+ conn.send(tcpPayload)
+ conn.send(struct.pack("!H", len(wire)))
+ conn.send(wire)
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
except socket.timeout:
- print('timeout')
+ print("timeout")
self.assertEqual(receivedResponse, response)
(receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
receivedQuery.id = query.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, srcAddr, destAddr, True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [ 42, b'bar'], [255, b'proxy-protocol'] ], True, srcPort, destPort)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload,
+ srcAddr,
+ destAddr,
+ True,
+ [[0, b"foo"], [1, b"dnsdist"], [2, b"foo"], [3, b"proxy"], [42, b"bar"], [255, b"proxy-protocol"]],
+ True,
+ srcPort,
+ destPort,
+ )
def testProxyUDPWithValueOverride(self):
"""
Incoming Proxy Protocol: override existing value (UDP)
"""
- name = 'override.proxy-protocol-incoming.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "override.proxy-protocol-incoming.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
destAddr = "2001:db8::9"
srcAddr = "2001:db8::8"
srcPort = 8888
- udpPayload = ProxyProtocol.getPayload(False, False, True, srcAddr, destAddr, srcPort, destPort, [ [2, b'foo'], [3, b'proxy'], [ 50, b'initial-value']])
+ udpPayload = ProxyProtocol.getPayload(
+ False,
+ False,
+ True,
+ srcAddr,
+ destAddr,
+ srcPort,
+ destPort,
+ [[2, b"foo"], [3, b"proxy"], [50, b"initial-value"]],
+ )
toProxyQueue.put(response, True, 2.0)
- (_, receivedResponse) = self.sendUDPQuery(udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True)
+ (_, receivedResponse) = self.sendUDPQuery(
+ udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True
+ )
(receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
self.assertTrue(receivedProxyPayload)
receivedResponse.id = response.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, srcAddr, destAddr, False, [ [50, b'overridden'] ], True, srcPort, destPort)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload, srcAddr, destAddr, False, [[50, b"overridden"]], True, srcPort, destPort
+ )
def testProxyTCPSeveralQueriesOverConnection(self):
"""
Incoming Proxy Protocol: Several queries over the same connection (TCP)
"""
- name = 'several-queries.proxy-protocol-incoming.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "several-queries.proxy-protocol-incoming.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
destAddr = "2001:db8::9"
srcAddr = "2001:db8::8"
srcPort = 8888
- tcpPayload = ProxyProtocol.getPayload(False, True, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 3, b'proxy'] ])
+ tcpPayload = ProxyProtocol.getPayload(
+ False, True, True, srcAddr, destAddr, srcPort, destPort, [[2, b"foo"], [3, b"proxy"]]
+ )
toProxyQueue.put(response, True, 2.0)
receivedResponse = None
conn = self.openTCPConnection(2.0)
try:
- conn.send(tcpPayload)
- conn.send(struct.pack("!H", len(wire)))
- conn.send(wire)
- receivedResponse = self.recvTCPResponseOverConnection(conn)
+ conn.send(tcpPayload)
+ conn.send(struct.pack("!H", len(wire)))
+ conn.send(wire)
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
except socket.timeout:
- print('timeout')
+ print("timeout")
self.assertEqual(receivedResponse, response)
(receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
receivedResponse.id = response.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, srcAddr, destAddr, True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [ 42, b'bar'], [255, b'proxy-protocol'] ], True, srcPort, destPort)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload,
+ srcAddr,
+ destAddr,
+ True,
+ [[0, b"foo"], [1, b"dnsdist"], [2, b"foo"], [3, b"proxy"], [42, b"bar"], [255, b"proxy-protocol"]],
+ True,
+ srcPort,
+ destPort,
+ )
for idx in range(5):
- receivedResponse = None
- toProxyQueue.put(response, True, 2.0)
- try:
- conn.send(struct.pack("!H", len(wire)))
- conn.send(wire)
- receivedResponse = self.recvTCPResponseOverConnection(conn)
- except socket.timeout:
- print('timeout')
-
- self.assertEqual(receivedResponse, response)
-
- (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
- self.assertTrue(receivedProxyPayload)
- self.assertTrue(receivedDNSData)
- self.assertTrue(receivedResponse)
-
- receivedQuery = dns.message.from_wire(receivedDNSData)
- receivedQuery.id = query.id
- self.assertEqual(receivedQuery, query)
- self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, srcAddr, destAddr, True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [ 42, b'bar'], [255, b'proxy-protocol'] ], True, srcPort, destPort)
+ receivedResponse = None
+ toProxyQueue.put(response, True, 2.0)
+ try:
+ conn.send(struct.pack("!H", len(wire)))
+ conn.send(wire)
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
+ except socket.timeout:
+ print("timeout")
+
+ self.assertEqual(receivedResponse, response)
+
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ self.assertEqual(receivedQuery, query)
+ self.assertEqual(receivedResponse, response)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload,
+ srcAddr,
+ destAddr,
+ True,
+ [[0, b"foo"], [1, b"dnsdist"], [2, b"foo"], [3, b"proxy"], [42, b"bar"], [255, b"proxy-protocol"]],
+ True,
+ srcPort,
+ destPort,
+ )
def testProxyDoHSeveralQueriesOverConnectionPPOutside(self):
"""
Incoming Proxy Protocol: Several queries over the same connection (DoH, PP outside TLS)
"""
- name = 'several-queries.doh-outside.proxy-protocol-incoming.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "several-queries.doh-outside.proxy-protocol-incoming.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
toProxyQueue.put(response, True, 2.0)
reverseProxyPort = pickAvailablePort()
- reverseProxy = threading.Thread(name='Mock Proxy Protocol Reverse Proxy', target=MockTCPReverseProxyAddingProxyProtocol, args=[reverseProxyPort, self._dohServerPPOutsidePort])
+ reverseProxy = threading.Thread(
+ name="Mock Proxy Protocol Reverse Proxy",
+ target=MockTCPReverseProxyAddingProxyProtocol,
+ args=[reverseProxyPort, self._dohServerPPOutsidePort],
+ )
reverseProxy.start()
time.sleep(1)
receivedResponse = None
conn = self.openDOHConnection(reverseProxyPort, self._caCert, timeout=2.0)
- reverseProxyBaseURL = ("https://%s:%d/" % (self._serverName, reverseProxyPort))
- (receivedQuery, receivedResponse) = self.sendDOHQuery(reverseProxyPort, self._serverName, reverseProxyBaseURL, query, response=response, caFile=self._caCert, conn=conn)
+ reverseProxyBaseURL = "https://%s:%d/" % (self._serverName, reverseProxyPort)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ reverseProxyPort,
+ self._serverName,
+ reverseProxyBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ conn=conn,
+ )
(receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
self.assertTrue(receivedProxyPayload)
self.assertTrue(receivedDNSData)
receivedResponse.id = response.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [32, ''], [42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload,
+ "127.0.0.1",
+ "127.0.0.1",
+ True,
+ [
+ [0, b"foo"],
+ [1, b"dnsdist"],
+ [2, b"foo"],
+ [3, b"proxy"],
+ [32, ""],
+ [42, b"bar"],
+ [255, b"proxy-protocol"],
+ ],
+ v6=False,
+ sourcePort=None,
+ destinationPort=reverseProxyPort,
+ )
for idx in range(5):
- receivedResponse = None
- toProxyQueue.put(response, True, 2.0)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(reverseProxyPort, self._serverName, reverseProxyBaseURL, query, response=response, caFile=self._caCert, conn=conn)
- (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
- self.assertTrue(receivedProxyPayload)
- self.assertTrue(receivedDNSData)
- self.assertTrue(receivedResponse)
-
- receivedQuery = dns.message.from_wire(receivedDNSData)
- receivedQuery.id = query.id
- receivedResponse.id = response.id
- self.assertEqual(receivedQuery, query)
- self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [32, ''], [42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort)
+ receivedResponse = None
+ toProxyQueue.put(response, True, 2.0)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ reverseProxyPort,
+ self._serverName,
+ reverseProxyBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ conn=conn,
+ )
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEqual(receivedQuery, query)
+ self.assertEqual(receivedResponse, response)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload,
+ "127.0.0.1",
+ "127.0.0.1",
+ True,
+ [
+ [0, b"foo"],
+ [1, b"dnsdist"],
+ [2, b"foo"],
+ [3, b"proxy"],
+ [32, ""],
+ [42, b"bar"],
+ [255, b"proxy-protocol"],
+ ],
+ v6=False,
+ sourcePort=None,
+ destinationPort=reverseProxyPort,
+ )
def testProxyDoHSeveralQueriesOverConnectionPPInside(self):
"""
Incoming Proxy Protocol: Several queries over the same connection (DoH, PP inside TLS)
"""
- name = 'several-queries.doh-inside.proxy-protocol-incoming.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "several-queries.doh-inside.proxy-protocol-incoming.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
toProxyQueue.put(response, True, 2.0)
reverseProxyPort = pickAvailablePort()
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
tlsContext.load_cert_chain(self._serverCert, self._serverKey)
- tlsContext.set_alpn_protocols(['h2'])
- reverseProxy = threading.Thread(name='Mock Proxy Protocol Reverse Proxy', target=MockTCPReverseProxyAddingProxyProtocol, args=[reverseProxyPort, self._dohServerPPInsidePort, tlsContext, self._caCert, self._serverName])
+ tlsContext.set_alpn_protocols(["h2"])
+ reverseProxy = threading.Thread(
+ name="Mock Proxy Protocol Reverse Proxy",
+ target=MockTCPReverseProxyAddingProxyProtocol,
+ args=[reverseProxyPort, self._dohServerPPInsidePort, tlsContext, self._caCert, self._serverName],
+ )
reverseProxy.start()
receivedResponse = None
time.sleep(1)
conn = self.openDOHConnection(reverseProxyPort, self._caCert, timeout=2.0)
- reverseProxyBaseURL = ("https://%s:%d/" % (self._serverName, reverseProxyPort))
- (receivedQuery, receivedResponse) = self.sendDOHQuery(reverseProxyPort, self._serverName, reverseProxyBaseURL, query, response=response, caFile=self._caCert, conn=conn)
+ reverseProxyBaseURL = "https://%s:%d/" % (self._serverName, reverseProxyPort)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ reverseProxyPort,
+ self._serverName,
+ reverseProxyBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ conn=conn,
+ )
(receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
self.assertTrue(receivedProxyPayload)
self.assertTrue(receivedDNSData)
receivedResponse.id = response.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [32, ''], [ 42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload,
+ "127.0.0.1",
+ "127.0.0.1",
+ True,
+ [
+ [0, b"foo"],
+ [1, b"dnsdist"],
+ [2, b"foo"],
+ [3, b"proxy"],
+ [32, ""],
+ [42, b"bar"],
+ [255, b"proxy-protocol"],
+ ],
+ v6=False,
+ sourcePort=None,
+ destinationPort=reverseProxyPort,
+ )
for idx in range(5):
- receivedResponse = None
- toProxyQueue.put(response, True, 2.0)
- (receivedQuery, receivedResponse) = self.sendDOHQuery(reverseProxyPort, self._serverName, reverseProxyBaseURL, query, response=response, caFile=self._caCert, conn=conn)
- (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
- self.assertTrue(receivedProxyPayload)
- self.assertTrue(receivedDNSData)
- self.assertTrue(receivedResponse)
-
- receivedQuery = dns.message.from_wire(receivedDNSData)
- receivedQuery.id = query.id
- receivedResponse.id = response.id
- self.assertEqual(receivedQuery, query)
- self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [32, ''], [ 42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort)
+ receivedResponse = None
+ toProxyQueue.put(response, True, 2.0)
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ reverseProxyPort,
+ self._serverName,
+ reverseProxyBaseURL,
+ query,
+ response=response,
+ caFile=self._caCert,
+ conn=conn,
+ )
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEqual(receivedQuery, query)
+ self.assertEqual(receivedResponse, response)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload,
+ "127.0.0.1",
+ "127.0.0.1",
+ True,
+ [
+ [0, b"foo"],
+ [1, b"dnsdist"],
+ [2, b"foo"],
+ [3, b"proxy"],
+ [32, ""],
+ [42, b"bar"],
+ [255, b"proxy-protocol"],
+ ],
+ v6=False,
+ sourcePort=None,
+ destinationPort=reverseProxyPort,
+ )
def testProxyDoTSeveralQueriesOverConnectionPPOutside(self):
"""
Incoming Proxy Protocol: Several queries over the same connection (DoT, PP outside TLS)
"""
- name = 'several-queries.dot-outside.proxy-protocol-incoming.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "several-queries.dot-outside.proxy-protocol-incoming.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
toProxyQueue.put(response, True, 2.0)
reverseProxyPort = pickAvailablePort()
- reverseProxy = threading.Thread(name='Mock Proxy Protocol Reverse Proxy', target=MockTCPReverseProxyAddingProxyProtocol, args=[reverseProxyPort, self._dotServerPPOutsidePort])
+ reverseProxy = threading.Thread(
+ name="Mock Proxy Protocol Reverse Proxy",
+ target=MockTCPReverseProxyAddingProxyProtocol,
+ args=[reverseProxyPort, self._dotServerPPOutsidePort],
+ )
reverseProxy.start()
time.sleep(1)
receivedResponse.id = response.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [32, ''], [42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload,
+ "127.0.0.1",
+ "127.0.0.1",
+ True,
+ [
+ [0, b"foo"],
+ [1, b"dnsdist"],
+ [2, b"foo"],
+ [3, b"proxy"],
+ [32, ""],
+ [42, b"bar"],
+ [255, b"proxy-protocol"],
+ ],
+ v6=False,
+ sourcePort=None,
+ destinationPort=reverseProxyPort,
+ )
for idx in range(5):
- receivedResponse = None
- toProxyQueue.put(response, True, 2.0)
- self.sendTCPQueryOverConnection(conn, query, response=response)
- receivedResponse = self.recvTCPResponseOverConnection(conn)
- (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
- self.assertTrue(receivedProxyPayload)
- self.assertTrue(receivedDNSData)
- self.assertTrue(receivedResponse)
-
- receivedQuery = dns.message.from_wire(receivedDNSData)
- receivedQuery.id = query.id
- receivedResponse.id = response.id
- self.assertEqual(receivedQuery, query)
- self.assertEqual(receivedResponse, response)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [32, ''], [42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort)
+ receivedResponse = None
+ toProxyQueue.put(response, True, 2.0)
+ self.sendTCPQueryOverConnection(conn, query, response=response)
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEqual(receivedQuery, query)
+ self.assertEqual(receivedResponse, response)
+ self.checkMessageProxyProtocol(
+ receivedProxyPayload,
+ "127.0.0.1",
+ "127.0.0.1",
+ True,
+ [
+ [0, b"foo"],
+ [1, b"dnsdist"],
+ [2, b"foo"],
+ [3, b"proxy"],
+ [32, ""],
+ [42, b"bar"],
+ [255, b"proxy-protocol"],
+ ],
+ v6=False,
+ sourcePort=None,
+ destinationPort=reverseProxyPort,
+ )
def testProxyDoTSeveralQueriesOverConnectionPPInside(self):
"""
Incoming Proxy Protocol: Several queries over the same connection (DoT, PP inside TLS)
"""
- name = 'several-queries.dot-inside.proxy-protocol-incoming.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "several-queries.dot-inside.proxy-protocol-incoming.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
toProxyQueue.put(response, True, 2.0)
reverseProxyPort = pickAvailablePort()
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
tlsContext.load_cert_chain(self._serverCert, self._serverKey)
- tlsContext.set_alpn_protocols(['dot'])
- reverseProxy = threading.Thread(name='Mock Proxy Protocol Reverse Proxy', target=MockTCPReverseProxyAddingProxyProtocol, args=[reverseProxyPort, self._dotServerPPInsidePort, tlsContext, self._caCert, self._serverName])
+ tlsContext.set_alpn_protocols(["dot"])
+ reverseProxy = threading.Thread(
+ name="Mock Proxy Protocol Reverse Proxy",
+ target=MockTCPReverseProxyAddingProxyProtocol,
+ args=[reverseProxyPort, self._dotServerPPInsidePort, tlsContext, self._caCert, self._serverName],
+ )
reverseProxy.start()
receivedResponse = None
self.assertEqual(receivedResponse, response)
for idx in range(5):
- receivedResponse = None
- toProxyQueue.put(response, True, 2.0)
- self.sendTCPQueryOverConnection(conn, query, response=response)
- receivedResponse = self.recvTCPResponseOverConnection(conn)
- (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
- self.assertTrue(receivedProxyPayload)
- self.assertTrue(receivedDNSData)
- self.assertTrue(receivedResponse)
-
- receivedQuery = dns.message.from_wire(receivedDNSData)
- receivedQuery.id = query.id
- receivedResponse.id = response.id
- self.assertEqual(receivedQuery, query)
- self.assertEqual(receivedResponse, response)
+ receivedResponse = None
+ toProxyQueue.put(response, True, 2.0)
+ self.sendTCPQueryOverConnection(conn, query, response=response)
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEqual(receivedQuery, query)
+ self.assertEqual(receivedResponse, response)
@classmethod
def tearDownClass(cls):
backgroundThreads[backgroundThread] = False
cls.killProcess(cls._dnsdist)
+
class TestProxyProtocolIncomingValuesViaLua(DNSDistTest):
"""
Check that dnsdist can retrieve incoming Proxy Protocol TLV values via Lua
af_inet6 = str(socket.AF_INET6)
- _config_template = """
+ _config_template = (
+ """
setProxyProtocolACL( { "127.0.0.1/32" } )
function checkValues(dq)
function checkValuesFFI(dqffi)
C.dnsdist_ffi_dnsquestion_get_localaddr(dqffi, ret_ptr_param, ret_size_param)
- local addr = C.inet_ntop("""+af_inet6+""", ret_ptr[0], inet_buffer, 256)
+ local addr = C.inet_ntop("""
+ + af_inet6
+ + """, ret_ptr[0], inet_buffer, 256)
if addr == nil or ffi.string(addr) ~= '2001:db8::9' then
return sendResult(dqffi, "invalid.local.addr.")
end
C.dnsdist_ffi_dnsquestion_get_remoteaddr(dqffi, ret_ptr_param, ret_size_param)
- local addr = C.inet_ntop("""+af_inet6+""", ret_ptr[0], inet_buffer, 256)
+ local addr = C.inet_ntop("""
+ + af_inet6
+ + """, ret_ptr[0], inet_buffer, 256)
if addr == nil or ffi.string(addr) ~= '2001:db8::8' then
return sendResult(dqffi, "invalid.remote.addr.")
end
newServer{address="127.0.0.1:%d"}
"""
+ )
def testProxyUDPWithValuesFromLua(self):
"""
destPort = 9999
srcAddr = "2001:db8::8"
srcPort = 8888
- names = ['proxy-protocol-incoming-values-via-lua.tests.powerdns.com.',
- 'proxy-protocol-incoming-values-via-lua-ffi.tests.powerdns.com.'
- ]
+ names = [
+ "proxy-protocol-incoming-values-via-lua.tests.powerdns.com.",
+ "proxy-protocol-incoming-values-via-lua-ffi.tests.powerdns.com.",
+ ]
for name in names:
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'ok.')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "ok.")
expectedResponse.answer.append(rrset)
- udpPayload = ProxyProtocol.getPayload(False, False, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 3, b'proxy'] ])
- (_, receivedResponse) = self.sendUDPQuery(udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True)
+ udpPayload = ProxyProtocol.getPayload(
+ False, False, True, srcAddr, destAddr, srcPort, destPort, [[2, b"foo"], [3, b"proxy"]]
+ )
+ (_, receivedResponse) = self.sendUDPQuery(
+ udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True
+ )
self.assertEqual(expectedResponse, receivedResponse)
conn = self.openTCPConnection(2.0)
conn.send(query.to_wire())
receivedResponse = self.recvTCPResponseOverConnection(conn)
except socket.timeout:
- print('timeout')
+ print("timeout")
self.assertEqual(expectedResponse, receivedResponse)
+
class TestProxyProtocolNotExpected(DNSDistTest):
"""
dnsdist is configured to expect a Proxy Protocol header on incoming queries but not from 127.0.0.1
newServer{address="127.0.0.1:%d"}
"""
# NORMAL responder, does not expect a proxy protocol payload!
- _config_params = ['_testServerPort']
+ _config_params = ["_testServerPort"]
def testNoHeader(self):
"""
Unexpected Proxy Protocol: no header
"""
# no proxy protocol header and none is expected from this source, should be passed on
- name = 'no-header.unexpected-proxy-protocol.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "no-header.unexpected-proxy-protocol.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
- sender = getattr(self, method)
- (receivedQuery, receivedResponse) = sender(query, response)
- receivedQuery.id = query.id
- self.assertEqual(query, receivedQuery)
- self.assertEqual(response, receivedResponse)
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response)
+ receivedQuery.id = query.id
+ self.assertEqual(query, receivedQuery)
+ self.assertEqual(response, receivedResponse)
def testIncomingProxyDest(self):
"""
Unexpected Proxy Protocol: should be dropped
"""
- name = 'with-proxy-payload.unexpected-protocol-incoming.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "with-proxy-payload.unexpected-protocol-incoming.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# Make sure that the proxy payload does NOT turn into a legal qname
destAddr = "ff:db8::ffff"
srcAddr = "ff:db8::ffff"
srcPort = 65535
- udpPayload = ProxyProtocol.getPayload(False, False, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 3, b'proxy'] ])
- (_, receivedResponse) = self.sendUDPQuery(udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True)
+ udpPayload = ProxyProtocol.getPayload(
+ False, False, True, srcAddr, destAddr, srcPort, destPort, [[2, b"foo"], [3, b"proxy"]]
+ )
+ (_, receivedResponse) = self.sendUDPQuery(
+ udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True
+ )
self.assertEqual(receivedResponse, None)
- tcpPayload = ProxyProtocol.getPayload(False, True, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 3, b'proxy'] ])
+ tcpPayload = ProxyProtocol.getPayload(
+ False, True, True, srcAddr, destAddr, srcPort, destPort, [[2, b"foo"], [3, b"proxy"]]
+ )
wire = query.to_wire()
receivedResponse = None
try:
- conn = self.openTCPConnection(2.0)
- conn.send(tcpPayload)
- conn.send(struct.pack("!H", len(wire)))
- conn.send(wire)
- receivedResponse = self.recvTCPResponseOverConnection(conn)
+ conn = self.openTCPConnection(2.0)
+ conn.send(tcpPayload)
+ conn.send(struct.pack("!H", len(wire)))
+ conn.send(wire)
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
except socket.timeout:
- print('timeout')
+ print("timeout")
self.assertEqual(receivedResponse, None)
+
class TestProxyProtocolNotAllowedOnBind(DNSDistTest):
"""
dnsdist is configured to expect a Proxy Protocol header on incoming queries but not on the 127.0.0.1 bind
"""
+
_skipListeningOnCL = True
_config_template = """
-- proxy protocol payloads are not allowed on this bind address!
newServer{address="127.0.0.1:%d"}
"""
# NORMAL responder, does not expect a proxy protocol payload!
- _config_params = ['_dnsDistPort', '_testServerPort']
+ _config_params = ["_dnsDistPort", "_testServerPort"]
def testNoHeader(self):
"""
Unexpected Proxy Protocol: no header
"""
# no proxy protocol header and none is expected from this source, should be passed on
- name = 'no-header.unexpected-proxy-protocol.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "no-header.unexpected-proxy-protocol.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
- sender = getattr(self, method)
- (receivedQuery, receivedResponse) = sender(query, response)
- receivedQuery.id = query.id
- self.assertEqual(query, receivedQuery)
- self.assertEqual(response, receivedResponse)
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response)
+ receivedQuery.id = query.id
+ self.assertEqual(query, receivedQuery)
+ self.assertEqual(response, receivedResponse)
def testIncomingProxyDest(self):
"""
Unexpected Proxy Protocol: should be dropped
"""
- name = 'with-proxy-payload.unexpected-protocol-incoming.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "with-proxy-payload.unexpected-protocol-incoming.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# Make sure that the proxy payload does NOT turn into a legal qname
destAddr = "ff:db8::ffff"
srcAddr = "ff:db8::ffff"
srcPort = 65535
- udpPayload = ProxyProtocol.getPayload(False, False, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 3, b'proxy'] ])
- (_, receivedResponse) = self.sendUDPQuery(udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True)
+ udpPayload = ProxyProtocol.getPayload(
+ False, False, True, srcAddr, destAddr, srcPort, destPort, [[2, b"foo"], [3, b"proxy"]]
+ )
+ (_, receivedResponse) = self.sendUDPQuery(
+ udpPayload + query.to_wire(), response=None, useQueue=False, rawQuery=True
+ )
self.assertEqual(receivedResponse, None)
- tcpPayload = ProxyProtocol.getPayload(False, True, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 3, b'proxy'] ])
+ tcpPayload = ProxyProtocol.getPayload(
+ False, True, True, srcAddr, destAddr, srcPort, destPort, [[2, b"foo"], [3, b"proxy"]]
+ )
wire = query.to_wire()
receivedResponse = None
try:
- conn = self.openTCPConnection(2.0)
- conn.send(tcpPayload)
- conn.send(struct.pack("!H", len(wire)))
- conn.send(wire)
- receivedResponse = self.recvTCPResponseOverConnection(conn)
+ conn = self.openTCPConnection(2.0)
+ conn.send(tcpPayload)
+ conn.send(struct.pack("!H", len(wire)))
+ conn.send(wire)
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
except socket.timeout:
- print('timeout')
+ print("timeout")
self.assertEqual(receivedResponse, None)
-class TestDOHWithOutgoingProxyProtocol(DNSDistDOHTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+class TestDOHWithOutgoingProxyProtocol(DNSDistDOHTest):
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_dohWithNGHTTP2ServerPort = pickAvailablePort()
- _dohWithNGHTTP2BaseURL = ("https://%s:%d/dns-query" % (_serverName, _dohWithNGHTTP2ServerPort))
+ _dohWithNGHTTP2BaseURL = "https://%s:%d/dns-query" % (_serverName, _dohWithNGHTTP2ServerPort)
_proxyResponderPort = proxyResponderPort
_config_template = """
newServer{address="127.0.0.1:%d", useProxyProtocol=true}
addDOHLocal("127.0.0.1:%d", "%s", "%s", { '/dns-query' }, { trustForwardedForHeader=true, library='nghttp2' })
setACL( { "::1/128", "127.0.0.0/8" } )
"""
- _config_params = ['_proxyResponderPort', '_dohWithNGHTTP2ServerPort', '_serverCert', '_serverKey']
+ _config_params = ["_proxyResponderPort", "_dohWithNGHTTP2ServerPort", "_serverCert", "_serverKey"]
def testTruncation(self):
"""
"""
# the query is first forwarded over UDP, leading to a TC=1 answer from the
# backend, then over TCP
- name = 'truncated-udp.doh.proxy-protocol.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "truncated-udp.doh.proxy-protocol.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.id = 42
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 42
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- for (port,url) in [(self._dohWithNGHTTP2ServerPort, self._dohWithNGHTTP2BaseURL)]:
- # first response is a TC=1
- tcResponse = dns.message.make_response(query)
- tcResponse.flags |= dns.flags.TC
- toProxyQueue.put(tcResponse, True, 2.0)
-
- ((receivedProxyPayload, receivedDNSData), receivedResponse) = self.sendDOHQuery(port, self._serverName, url, query, caFile=self._caCert, response=response, fromQueue=fromProxyQueue, toQueue=toProxyQueue)
- # first query, received by the responder over UDP
- self.assertTrue(receivedProxyPayload)
- self.assertTrue(receivedDNSData)
- receivedQuery = dns.message.from_wire(receivedDNSData)
- self.assertTrue(receivedQuery)
- receivedQuery.id = expectedQuery.id
- self.assertEqual(expectedQuery, receivedQuery)
- self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, destinationPort=port)
-
- # check the response
- self.assertTrue(receivedResponse)
- self.assertEqual(response, receivedResponse)
-
- # check the second query, received by the responder over TCP
- (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
- self.assertTrue(receivedDNSData)
- receivedQuery = dns.message.from_wire(receivedDNSData)
- self.assertTrue(receivedQuery)
- receivedQuery.id = expectedQuery.id
- self.assertEqual(expectedQuery, receivedQuery)
- self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, destinationPort=port)
-
- # make sure we consumed everything
- self.assertTrue(toProxyQueue.empty())
- self.assertTrue(fromProxyQueue.empty())
+ for port, url in [(self._dohWithNGHTTP2ServerPort, self._dohWithNGHTTP2BaseURL)]:
+ # first response is a TC=1
+ tcResponse = dns.message.make_response(query)
+ tcResponse.flags |= dns.flags.TC
+ toProxyQueue.put(tcResponse, True, 2.0)
+
+ ((receivedProxyPayload, receivedDNSData), receivedResponse) = self.sendDOHQuery(
+ port,
+ self._serverName,
+ url,
+ query,
+ caFile=self._caCert,
+ response=response,
+ fromQueue=fromProxyQueue,
+ toQueue=toProxyQueue,
+ )
+ # first query, received by the responder over UDP
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ self.assertTrue(receivedQuery)
+ receivedQuery.id = expectedQuery.id
+ self.assertEqual(expectedQuery, receivedQuery)
+ self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
+ self.checkMessageProxyProtocol(receivedProxyPayload, "127.0.0.1", "127.0.0.1", True, destinationPort=port)
+
+ # check the response
+ self.assertTrue(receivedResponse)
+ self.assertEqual(response, receivedResponse)
+
+ # check the second query, received by the responder over TCP
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedDNSData)
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ self.assertTrue(receivedQuery)
+ receivedQuery.id = expectedQuery.id
+ self.assertEqual(expectedQuery, receivedQuery)
+ self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
+ self.checkMessageProxyProtocol(receivedProxyPayload, "127.0.0.1", "127.0.0.1", True, destinationPort=port)
+
+ # make sure we consumed everything
+ self.assertTrue(toProxyQueue.empty())
+ self.assertTrue(fromProxyQueue.empty())
def testAddressFamilyMismatch(self):
"""
DOH with IPv6 X-Forwarded-For to an IPv4 endpoint
"""
- name = 'x-forwarded-for-af-mismatch.doh.outgoing-proxy-protocol.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "x-forwarded-for-af-mismatch.doh.outgoing-proxy-protocol.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- for (port,url) in [(self._dohWithNGHTTP2ServerPort, self._dohWithNGHTTP2BaseURL)]:
- # the query should be dropped
- (receivedQuery, receivedResponse) = self.sendDOHQuery(port, self._serverName, url, query, caFile=self._caCert, customHeaders=['x-forwarded-for: [::1]:8080'], useQueue=False)
- self.assertFalse(receivedQuery)
- self.assertFalse(receivedResponse)
-
- # make sure the timeout is detected, if any
- time.sleep(4)
-
- # this one should not
- ((receivedProxyPayload, receivedDNSData), receivedResponse) = self.sendDOHQuery(port, self._serverName, url, query, caFile=self._caCert, customHeaders=['x-forwarded-for: 127.0.0.42:8080'], response=response, fromQueue=fromProxyQueue, toQueue=toProxyQueue)
- self.assertTrue(receivedProxyPayload)
- self.assertTrue(receivedDNSData)
- receivedQuery = dns.message.from_wire(receivedDNSData)
- self.assertTrue(receivedQuery)
- receivedQuery.id = expectedQuery.id
- self.assertEqual(expectedQuery, receivedQuery)
- self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
- self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.42', '127.0.0.1', True, destinationPort=port)
- # check the response
- self.assertTrue(receivedResponse)
- receivedResponse.id = response.id
- self.assertEqual(response, receivedResponse)
+ for port, url in [(self._dohWithNGHTTP2ServerPort, self._dohWithNGHTTP2BaseURL)]:
+ # the query should be dropped
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(
+ port,
+ self._serverName,
+ url,
+ query,
+ caFile=self._caCert,
+ customHeaders=["x-forwarded-for: [::1]:8080"],
+ useQueue=False,
+ )
+ self.assertFalse(receivedQuery)
+ self.assertFalse(receivedResponse)
+
+ # make sure the timeout is detected, if any
+ time.sleep(4)
+
+ # this one should not
+ ((receivedProxyPayload, receivedDNSData), receivedResponse) = self.sendDOHQuery(
+ port,
+ self._serverName,
+ url,
+ query,
+ caFile=self._caCert,
+ customHeaders=["x-forwarded-for: 127.0.0.42:8080"],
+ response=response,
+ fromQueue=fromProxyQueue,
+ toQueue=toProxyQueue,
+ )
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ self.assertTrue(receivedQuery)
+ receivedQuery.id = expectedQuery.id
+ self.assertEqual(expectedQuery, receivedQuery)
+ self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
+ self.checkMessageProxyProtocol(receivedProxyPayload, "127.0.0.42", "127.0.0.1", True, destinationPort=port)
+ # check the response
+ self.assertTrue(receivedResponse)
+ receivedResponse.id = response.id
+ self.assertEqual(response, receivedResponse)
import dns
from dnsdisttests import DNSDistTest
+
class TestRE2(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
"""
RE2: Match
"""
- name = 're2.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "re2.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
"""
RE2: No match
"""
- name = 'sub.re2.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "sub.re2.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
import socket
from dnsdisttests import DNSDistTest
+
class RandomizedIDs:
def testRandomizedIDOverUDPFromLuaConfig(self):
"""
Randomized IDs over UDP: Lua config
"""
- name = 'lua-config.randomizedids.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "lua-config.randomizedids.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
randomized = False
self.assertTrue(randomized)
+
class RandomizedIDsLuaConfig(DNSDistTest, RandomizedIDs):
_config_template = """
setRandomizedIdsOverUDP(true)
newServer{address="127.0.0.1:%d"}
"""
+
class RandomizedIDsYAMLConfig(DNSDistTest, RandomizedIDs):
_yaml_config_template = """
backends:
udp:
randomize_ids_to_backend: true
"""
- _yaml_config_params = ['_testServerPort']
+ _yaml_config_params = ["_testServerPort"]
_config_params = []
import dns
from dnsdisttests import DNSDistTest
-class TestRecordsCountOnlyOneAR(DNSDistTest):
+class TestRecordsCountOnlyOneAR(DNSDistTest):
_config_template = """
addAction(NotRule(RecordsCountRule(DNSSection.Additional, 1, 1)), RCodeAction(DNSRCode.REFUSED))
newServer{address="127.0.0.1:%d"}
Send a query to "refuseemptyar.recordscount.tests.powerdns.com.",
check that we are getting a REFUSED response.
"""
- name = 'refuseemptyar.recordscount.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "refuseemptyar.recordscount.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
Send a query to "allowonear.recordscount.tests.powerdns.com.",
check that we are getting a valid response.
"""
- name = 'allowonear.recordscount.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "allowonear.recordscount.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1'))
+ response.answer.append(dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1"))
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
Send a query to "refusetwoar.recordscount.tests.powerdns.com.",
check that we are getting a REFUSED response.
"""
- name = 'refusetwoar.recordscount.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "refusetwoar.recordscount.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
query.flags &= ~dns.flags.RD
- query.additional.append(dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1'))
+ query.additional.append(dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1"))
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestRecordsCountMoreThanOneLessThanFour(DNSDistTest):
+class TestRecordsCountMoreThanOneLessThanFour(DNSDistTest):
_config_template = """
addAction(RecordsCountRule(DNSSection.Answer, 2, 3), AllowAction())
addAction(AllRule(), RCodeAction(DNSRCode.REFUSED))
Send a query to "refusenoan.recordscount.tests.powerdns.com.",
check that we are getting a REFUSED response.
"""
- name = 'refusenoan.recordscount.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "refusenoan.recordscount.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
Send a query to "allowtwoan.recordscount.tests.powerdns.com.",
check that we are getting a valid response.
"""
- name = 'allowtwoan.recordscount.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
- rrset = dns.rrset.from_text_list(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- ['127.0.0.1', '127.0.0.2'])
+ name = "allowtwoan.recordscount.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
+ rrset = dns.rrset.from_text_list(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, ["127.0.0.1", "127.0.0.2"])
query.answer.append(rrset)
response = dns.message.make_response(query)
response.answer.append(rrset)
Send a query to "refusefouran.recordscount.tests.powerdns.com.",
check that we are getting a REFUSED response.
"""
- name = 'refusefouran.recordscount.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "refusefouran.recordscount.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
query.flags &= ~dns.flags.RD
- rrset = dns.rrset.from_text_list(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- ['127.0.0.1', '127.0.0.2', '127.0.0.3', '127.0.0.4'])
+ rrset = dns.rrset.from_text_list(
+ name, 3600, dns.rdataclass.IN, dns.rdatatype.A, ["127.0.0.1", "127.0.0.2", "127.0.0.3", "127.0.0.4"]
+ )
query.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestRecordsCountNothingInNS(DNSDistTest):
+class TestRecordsCountNothingInNS(DNSDistTest):
_config_template = """
addAction(RecordsCountRule(DNSSection.Authority, 0, 0), AllowAction())
addAction(AllRule(), RCodeAction(DNSRCode.REFUSED))
Send a query to "refusens.recordscount.tests.powerdns.com.",
check that we are getting a REFUSED response.
"""
- name = 'refusens.recordscount.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.NS,
- 'ns.tests.powerdns.com.')
+ name = "refusens.recordscount.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.NS, "ns.tests.powerdns.com.")
query.authority.append(rrset)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-
def testRecordsCountAllowEmptyNS(self):
"""
RecordsCount: Allow nscount == 0
Send a query to "allowns.recordscount.tests.powerdns.com.",
check that we are getting a valid response.
"""
- name = 'allowns.recordscount.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "allowns.recordscount.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1'))
+ response.answer.append(dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1"))
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-class TestRecordsCountNoOPTInAR(DNSDistTest):
+class TestRecordsCountNoOPTInAR(DNSDistTest):
_config_template = """
addAction(NotRule(RecordsTypeCountRule(DNSSection.Additional, DNSQType.OPT, 0, 0)), RCodeAction(DNSRCode.REFUSED))
newServer{address="127.0.0.1:%d"}
Send a query to "refuseoptinar.recordscount.tests.powerdns.com.",
check that we are getting a REFUSED response.
"""
- name = 'refuseoptinar.recordscount.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "refuseoptinar.recordscount.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
Send a query to "allownooptinar.recordscount.tests.powerdns.com.",
check that we are getting a valid response.
"""
- name = 'allowwnooptinar.recordscount.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "allowwnooptinar.recordscount.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1'))
+ response.answer.append(dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1"))
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
Send a query to "allowtwoarnoopt.recordscount.tests.powerdns.com.",
check that we are getting a valid response.
"""
- name = 'allowtwoarnoopt.recordscount.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- query.additional.append(dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1'))
- query.additional.append(dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1'))
+ name = "allowtwoarnoopt.recordscount.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ query.additional.append(dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1"))
+ query.additional.append(dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1"))
response = dns.message.make_response(query)
- response.answer.append(dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1'))
+ response.answer.append(dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1"))
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
import cookiesoption
from dnsdisttests import DNSDistTest
-class TestResponseRuleNXDelayed(DNSDistTest):
+class TestResponseRuleNXDelayed(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
addResponseAction(RCodeRule(DNSRCode.NXDOMAIN), DelayResponseAction(1000))
check that the response delay is longer than 1000 ms
for a NXDomain response over UDP, shorter for a NoError one.
"""
- name = 'delayed.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "delayed.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
# NX over UDP
self.assertEqual(response, receivedResponse)
self.assertLess(end - begin, timedelta(0, 1))
-class TestResponseRuleERCode(DNSDistTest):
+class TestResponseRuleERCode(DNSDistTest):
_extraStartupSleep = 1
_config_template = """
newServer{address="127.0.0.1:%d"}
check that the response delay is longer than 1000 ms
for a BADVERS response over UDP, shorter for BADKEY and NoError.
"""
- name = 'delayed.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "delayed.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.use_edns(edns=True)
self.assertEqual(response, receivedResponse)
self.assertLess(end - begin, timedelta(0, 1))
-class TestResponseRuleQNameDropped(DNSDistTest):
+class TestResponseRuleQNameDropped(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
addResponseAction("drop.responses.tests.powerdns.com.", DropResponseAction())
Send an A query to "drop.responses.tests.powerdns.com.",
check that the response (not the query) is dropped.
"""
- name = 'drop.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "drop.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an A query to "dontdrop.responses.tests.powerdns.com.",
check that the response is not dropped.
"""
- name = 'dontdrop.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "dontdrop.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-class TestResponseRuleQNameAllowed(DNSDistTest):
+class TestResponseRuleQNameAllowed(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
addResponseAction("allow.responses.tests.powerdns.com.", AllowResponseAction())
Send an A query to "allow.responses.tests.powerdns.com.",
check that the response is allowed.
"""
- name = 'allow.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "allow.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an A query to "dontallow.responses.tests.powerdns.com.",
check that the response is dropped.
"""
- name = 'dontallow.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "dontallow.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, None)
-class TestResponseRuleEditTTL(DNSDistTest):
+class TestResponseRuleEditTTL(DNSDistTest):
_ttl = 5
- _config_params = ['_testServerPort', '_ttl']
+ _config_params = ["_testServerPort", "_ttl"]
_config_template = """
newServer{address="127.0.0.1:%d"}
"""
Responses: Alter the TTLs
"""
- name = 'editttl.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "editttl.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertNotEqual(response.answer[0].ttl, receivedResponse.answer[0].ttl)
self.assertEqual(receivedResponse.answer[0].ttl, self._ttl)
-class TestResponseRuleLimitTTL(DNSDistTest):
+class TestResponseRuleLimitTTL(DNSDistTest):
_lowttl = 60
_defaultttl = 3600
_highttl = 18000
- _config_params = ['_lowttl', '_highttl', '_testServerPort']
+ _config_params = ["_lowttl", "_highttl", "_testServerPort"]
_config_template = """
local ffi = require("ffi")
local lowttl = %d
"""
Responses: Alter the TTLs via Limiter
"""
- name = 'min.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "min.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertNotEqual(response.answer[0].ttl, receivedResponse.answer[0].ttl)
self.assertEqual(receivedResponse.answer[0].ttl, self._highttl)
- name = 'max.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "max.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Responses: Alter the TTLs via Limiter
"""
- name = 'ffi.min.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "ffi.min.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertNotEqual(response.answer[0].ttl, receivedResponse.answer[0].ttl)
self.assertEqual(receivedResponse.answer[0].ttl, self._highttl)
- name = 'ffi.max.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "ffi.max.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertNotEqual(response.answer[0].ttl, receivedResponse.answer[0].ttl)
self.assertEqual(receivedResponse.answer[0].ttl, self._lowttl)
-class TestSetReducedTTL(DNSDistTest):
+class TestSetReducedTTL(DNSDistTest):
_percentage = 42
_initialTTL = 100
- _config_params = ['_percentage', '_testServerPort']
+ _config_params = ["_percentage", "_testServerPort"]
_config_template = """
addResponseAction(AllRule(), SetReducedTTLResponseAction(%d))
newServer{address="127.0.0.1:%d"}
"""
Responses: Reduce TTL to 42%
"""
- name = 'reduced-ttl.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "reduced-ttl.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- self._initialTTL,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, self._initialTTL, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertNotEqual(response.answer[0].ttl, receivedResponse.answer[0].ttl)
self.assertEqual(receivedResponse.answer[0].ttl, self._percentage)
-class TestResponseLuaActionReturnSyntax(DNSDistTest):
+class TestResponseLuaActionReturnSyntax(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
function customDelay(dr)
check that the response delay is longer than 1000 ms
for a NXDomain response over UDP, shorter for a NoError one.
"""
- name = 'delayed.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "delayed.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
# NX over UDP
Send an A query to "drop.responses.tests.powerdns.com.",
check that the response (not the query) is dropped.
"""
- name = 'drop.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "drop.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, None)
-class TestResponseClearRecordsType(DNSDistTest):
- _config_params = ['_testServerPort']
+class TestResponseClearRecordsType(DNSDistTest):
+ _config_params = ["_testServerPort"]
_config_template = """
local ffi = require("ffi")
"""
Responses: Removes records of a given type (FFI API)
"""
- name = 'ffi.clear-records-type.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "ffi.clear-records-type.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
- rrset = dns.rrset.from_text(name,
- 3660,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1', '2001:DB8::2')
+ rrset = dns.rrset.from_text(name, 3660, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1", "2001:DB8::2")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
"""
Responses: Removes records of a given type
"""
- name = 'clear-records-type.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "clear-records-type.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
- rrset = dns.rrset.from_text(name,
- 3660,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1', '2001:DB8::2')
+ rrset = dns.rrset.from_text(name, 3660, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1", "2001:DB8::2")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(query, receivedQuery)
self.assertEqual(expectedResponse, receivedResponse)
-class TestResponseRewriteServFail(DNSDistTest):
- _config_params = ['_testServerPort']
+class TestResponseRewriteServFail(DNSDistTest):
+ _config_params = ["_testServerPort"]
_config_template = """
newServer{address="127.0.0.1:%d"}
"""
Responses: Rewrite AAAA ServFails as NoError (don't ask)
"""
- name = 'rewrite-servfail.responses.tests.powerdns.com.'
+ name = "rewrite-servfail.responses.tests.powerdns.com."
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
expectedResponse = dns.message.make_response(query)
response.set_rcode(dns.rcode.SERVFAIL)
expectedResponse.set_rcode(dns.rcode.NOERROR)
- rrset = dns.rrset.from_text(name,
- 3660,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1', '2001:DB8::2')
+ rrset = dns.rrset.from_text(name, 3660, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1", "2001:DB8::2")
response.answer.append(rrset)
expectedResponse.answer.append(rrset)
self.assertEqual(expectedResponse, receivedResponse)
# but ServFail for a different type should stay the same
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.SERVFAIL)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
+
class TestAdvancedSetEDNSOptionResponseAction(DNSDistTest):
_config_template = """
addResponseAction(AllRule(), SetEDNSOptionResponseAction(10, "deadbeefdeadc0de"))
"""
Responses: Set EDNS Option in response
"""
- name = 'setednsoptionresponse.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "setednsoptionresponse.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=512)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadc0de')
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadc0de")
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=512, options=[eco])
expectedResponse.answer.append(rrset)
"""
Responses: Set EDNS Option in response replaces existing option
"""
- name = 'setednsoptionresponse-overwrite.responses.tests.powerdns.com.'
- initialECO = cookiesoption.CookiesOption(b'aaaaaaaa', b'bbbbbbbb')
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "setednsoptionresponse-overwrite.responses.tests.powerdns.com."
+ initialECO = cookiesoption.CookiesOption(b"aaaaaaaa", b"bbbbbbbb")
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=512, options=[initialECO])
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- replacementECO = cookiesoption.CookiesOption(b'deadbeef', b'deadc0de')
+ replacementECO = cookiesoption.CookiesOption(b"deadbeef", b"deadc0de")
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=512, options=[replacementECO])
expectedResponse.answer.append(rrset)
"""
Responses: Set EDNS Option in response (DO bit set)
"""
- name = 'setednsoptionresponse-do.responses.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, want_dnssec=True, payload=4096)
+ name = "setednsoptionresponse-do.responses.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, want_dnssec=True, payload=4096)
response = dns.message.make_response(query)
response.use_edns(edns=True, payload=1024)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadc0de')
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadc0de")
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=1024, options=[eco])
expectedResponse.answer.append(rrset)
from dnsdisttests import DNSDistTest, pickAvailablePort
from proxyprotocolutils import ProxyProtocolUDPResponder, ProxyProtocolTCPResponder
+
def servFailResponseCallback(request):
response = dns.message.make_response(request)
response.set_rcode(dns.rcode.SERVFAIL)
return response.to_wire()
+
def normalResponseCallback(request):
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(request.question[0].name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
return response.to_wire()
-class TestRestartQuery(DNSDistTest):
+class TestRestartQuery(DNSDistTest):
# this test suite uses different responder ports
_testNormalServerPort = pickAvailablePort()
_testServfailServerPort = pickAvailablePort()
addAction(AllRule(), LuaAction(makeQueryRestartable))
addResponseAction(AllRule(), LuaResponseAction(restartOnServFail))
"""
- _config_params = ['_testNormalServerPort', '_testServfailServerPort']
+ _config_params = ["_testNormalServerPort", "_testServfailServerPort"]
_verboseMode = True
@classmethod
print("Launching responders..")
# servfail
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServfailServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, servFailResponseCallback])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[
+ cls._testServfailServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ servFailResponseCallback,
+ ],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServfailServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, servFailResponseCallback])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._testServfailServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ servFailResponseCallback,
+ ],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._UDPResponderNormal = threading.Thread(name='UDP ResponderNormal', target=cls.UDPResponder, args=[cls._testNormalServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, normalResponseCallback])
+ cls._UDPResponderNormal = threading.Thread(
+ name="UDP ResponderNormal",
+ target=cls.UDPResponder,
+ args=[
+ cls._testNormalServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ normalResponseCallback,
+ ],
+ )
cls._UDPResponderNormal.daemon = True
cls._UDPResponderNormal.start()
- cls._TCPResponderNormal = threading.Thread(name='TCP ResponderNormal', target=cls.TCPResponder, args=[cls._testNormalServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, normalResponseCallback])
+ cls._TCPResponderNormal = threading.Thread(
+ name="TCP ResponderNormal",
+ target=cls.TCPResponder,
+ args=[
+ cls._testNormalServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ normalResponseCallback,
+ ],
+ )
cls._TCPResponderNormal.daemon = True
cls._TCPResponderNormal.start()
"""
Restart: ServFail then restarted to a second pool
"""
- name = 'restart.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ name = "restart.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
fromProxyQueue = Queue()
proxyResponderPort = pickAvailablePort()
-udpResponder = threading.Thread(name='UDP Proxy Protocol Responder', target=ProxyProtocolUDPResponder, args=[proxyResponderPort, toProxyQueue, fromProxyQueue])
+udpResponder = threading.Thread(
+ name="UDP Proxy Protocol Responder",
+ target=ProxyProtocolUDPResponder,
+ args=[proxyResponderPort, toProxyQueue, fromProxyQueue],
+)
udpResponder.daemon = True
udpResponder.start()
-tcpResponder = threading.Thread(name='TCP Proxy Protocol Responder', target=ProxyProtocolTCPResponder, args=[proxyResponderPort, toProxyQueue, fromProxyQueue])
+tcpResponder = threading.Thread(
+ name="TCP Proxy Protocol Responder",
+ target=ProxyProtocolTCPResponder,
+ args=[proxyResponderPort, toProxyQueue, fromProxyQueue],
+)
tcpResponder.daemon = True
tcpResponder.start()
+
class TestRestartProxyProtocolThenNot(DNSDistTest):
- _restartPool = 'restart-pool'
+ _restartPool = "restart-pool"
_config_template = """
fallbackPool = '%s'
newServer{address="127.0.0.1:%d", useProxyProtocol=true}
addResponseAction(AllRule(), LuaResponseAction(restart))
"""
_proxyResponderPort = proxyResponderPort
- _config_params = ['_restartPool', '_proxyResponderPort', '_testServerPort']
+ _config_params = ["_restartPool", "_proxyResponderPort", "_testServerPort"]
def testRestart(self):
"""
Restart: queries is first forwarded to proxy-protocol enabled backend, then restarted to a non-PP backend
"""
- name = 'proxy.restart.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "proxy.restart.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(receivedProxyPayload)
self.assertTrue(receivedDNSData)
-class QueryCounter:
+class QueryCounter:
def __init__(self, name):
self.name = name
self.qcnt = 0
response = dns.message.make_response(request)
response.set_rcode(dns.rcode.REFUSED)
return response.to_wire()
+
return callback
-class TestRestartCount(DNSDistTest):
+class TestRestartCount(DNSDistTest):
_queryCounts = {}
_testServer1Port = pickAvailablePort()
_testServer3Port = pickAvailablePort()
_testServer4Port = pickAvailablePort()
_serverPorts = [_testServer1Port, _testServer2Port, _testServer3Port, _testServer4Port]
- _config_params = ['_testServer1Port', '_testServer2Port', '_testServer3Port', '_testServer4Port']
+ _config_params = ["_testServer1Port", "_testServer2Port", "_testServer3Port", "_testServer4Port"]
_config_template = """
MaxRestart = 2
s0 = newServer{name="s0", address="127.0.0.1:%d"}
addResponseAction(RCodeRule(DNSRCode.REFUSED), LuaResponseAction(restartQuery))
addAction(AllRule(), PoolAction("pool0"))
"""
+
@classmethod
def startResponders(cls):
print("Launching responders..")
- for i, name in enumerate(['s0', 's1', 's2', 's3']):
+ for i, name in enumerate(["s0", "s1", "s2", "s3"]):
cls._queryCounts[name] = QueryCounter(name)
cb = cls._queryCounts[name].create_cb()
- responder = threading.Thread(name=name, target=cls.UDPResponder, args=[cls._serverPorts[i], cls._toResponderQueue, cls._fromResponderQueue, False, cb])
+ responder = threading.Thread(
+ name=name,
+ target=cls.UDPResponder,
+ args=[cls._serverPorts[i], cls._toResponderQueue, cls._fromResponderQueue, False, cb],
+ )
responder.daemon = True
responder.start()
def testDefault(self):
numberOfQueries = 100
- name = 'restart.count.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "restart.count.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.SERVFAIL)
self.assertEqual(expectedResponse, receivedResponse)
# if restart count is correct, s0/s1/s2 would get all the queies while s3 would get none
- self.assertEqual(self._queryCounts['s0'](), numberOfQueries)
- self.assertEqual(self._queryCounts['s1'](), numberOfQueries)
- self.assertEqual(self._queryCounts['s2'](), numberOfQueries)
- self.assertEqual(self._queryCounts['s3'](), 0)
+ self.assertEqual(self._queryCounts["s0"](), numberOfQueries)
+ self.assertEqual(self._queryCounts["s1"](), numberOfQueries)
+ self.assertEqual(self._queryCounts["s2"](), numberOfQueries)
+ self.assertEqual(self._queryCounts["s3"](), 0)
import dns
from dnsdisttests import DNSDistTest, pickAvailablePort
-class TestRoutingPoolRouting(DNSDistTest):
+class TestRoutingPoolRouting(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d", pool="real"}
addAction(SuffixMatchNodeRule("poolaction.routing.tests.powerdns.com"), PoolAction("real"))
Send an A query to "poolaction.routing.tests.powerdns.com.",
check that dnsdist routes the query to the "real" pool.
"""
- name = 'poolaction.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "poolaction.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Routing: Set pool by qname via PoolAction (no stop)
"""
- name = 'poolaction-nostop.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "poolaction-nostop.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
check that dnsdist sends no response (no servers
in the default pool).
"""
- name = 'notpool.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "notpool.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
+
class TestRoutingQPSPoolRouting(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d", pool="regular"}
when the max QPS has been reached.
"""
maxQPS = 10
- name = 'qpspoolaction.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "qpspoolaction.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for _ in range(maxQPS):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-
(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
+
class RoundRobinTest(object):
def doTestRR(self, name):
"""
check that dnsdist routes half of it to each backend.
"""
numberOfQueries = 10
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
# the round robin counter is shared for UDP and TCP,
value = self._responsesCounter[key]
self.assertEqual(value, numberOfQueries / 2)
-class TestRoutingRoundRobinLB(RoundRobinTest, DNSDistTest):
+class TestRoutingRoundRobinLB(RoundRobinTest, DNSDistTest):
_testServer2Port = pickAvailablePort()
- _config_params = ['_testServerPort', '_testServer2Port']
+ _config_params = ["_testServerPort", "_testServer2Port"]
_config_template = """
setServerPolicy(roundrobin)
s1 = newServer{address="127.0.0.1:%d"}
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2 = threading.Thread(
+ name="UDP Responder 2",
+ target=cls.UDPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2 = threading.Thread(
+ name="TCP Responder 2",
+ target=cls.TCPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
Send 10 A queries to "rr.routing.tests.powerdns.com.",
check that dnsdist routes half of it to each backend.
"""
- self.doTestRR('rr.routing.tests.powerdns.com.')
+ self.doTestRR("rr.routing.tests.powerdns.com.")
-class TestRoutingRoundRobinLBViaPool(RoundRobinTest, DNSDistTest):
+class TestRoutingRoundRobinLBViaPool(RoundRobinTest, DNSDistTest):
_testServer2Port = pickAvailablePort()
- _config_params = ['_testServerPort', '_testServer2Port']
+ _config_params = ["_testServerPort", "_testServer2Port"]
_config_template = """
s1 = newServer{address="127.0.0.1:%d"}
s1:setUp()
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2 = threading.Thread(
+ name="UDP Responder 2",
+ target=cls.UDPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2 = threading.Thread(
+ name="TCP Responder 2",
+ target=cls.TCPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
Send 10 A queries to "rr-pool.routing.tests.powerdns.com.",
check that dnsdist routes half of it to each backend.
"""
- self.doTestRR('rr-pool.routing.tests.powerdns.com.')
+ self.doTestRR("rr-pool.routing.tests.powerdns.com.")
-class TestRoutingRoundRobinLBOneDown(DNSDistTest):
+class TestRoutingRoundRobinLBOneDown(DNSDistTest):
_testServer2Port = pickAvailablePort()
- _config_params = ['_testServerPort', '_testServer2Port']
+ _config_params = ["_testServerPort", "_testServer2Port"]
_config_template = """
setServerPolicy(roundrobin)
s1 = newServer{address="127.0.0.1:%d"}
check that dnsdist routes all of it to the only backend up.
"""
numberOfQueries = 10
- name = 'rr.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "rr.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
# the round robin counter is shared for UDP and TCP,
self.assertEqual(total, numberOfQueries * 2)
-class TestRoutingRoundRobinLBAllDown(DNSDistTest):
+class TestRoutingRoundRobinLBAllDown(DNSDistTest):
_testServer2Port = pickAvailablePort()
- _config_params = ['_testServerPort', '_testServer2Port']
+ _config_params = ["_testServerPort", "_testServer2Port"]
_config_template = """
setServerPolicy(roundrobin)
setRoundRobinFailOnNoServer(true)
"""
Routing: Round Robin with all servers down
"""
- name = 'alldown.rr.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "alldown.rr.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
-class TestRoutingLuaFFIPerThreadRoundRobinLB(RoundRobinTest, DNSDistTest):
+class TestRoutingLuaFFIPerThreadRoundRobinLB(RoundRobinTest, DNSDistTest):
_testServer2Port = pickAvailablePort()
- _config_params = ['_testServerPort', '_testServer2Port']
+ _config_params = ["_testServerPort", "_testServer2Port"]
_config_template = """
-- otherwise we start too many TCP workers, and as each thread
-- uses it own counter this makes the TCP queries distribution hard to predict
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2 = threading.Thread(
+ name="UDP Responder 2",
+ target=cls.UDPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2 = threading.Thread(
+ name="TCP Responder 2",
+ target=cls.TCPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
"""
Routing: Round Robin (LuaFFI)
"""
- self.doTestRR('rr-luaffi.routing.tests.powerdns.com.')
+ self.doTestRR("rr-luaffi.routing.tests.powerdns.com.")
-class TestRoutingCustomLuaRoundRobinLB(RoundRobinTest, DNSDistTest):
+class TestRoutingCustomLuaRoundRobinLB(RoundRobinTest, DNSDistTest):
_testServer2Port = pickAvailablePort()
- _config_params = ['_testServerPort', '_testServer2Port']
+ _config_params = ["_testServerPort", "_testServer2Port"]
_config_template = """
-- otherwise we start too many TCP workers, and as each thread
-- uses it own counter this makes the TCP queries distribution hard to predict
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2 = threading.Thread(
+ name="UDP Responder 2",
+ target=cls.UDPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2 = threading.Thread(
+ name="TCP Responder 2",
+ target=cls.TCPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
"""
Routing: Round Robin (Lua)
"""
- self.doTestRR('rr-lua.routing.tests.powerdns.com.')
+ self.doTestRR("rr-lua.routing.tests.powerdns.com.")
-class TestRoutingOrder(DNSDistTest):
+class TestRoutingOrder(DNSDistTest):
_testServer2Port = pickAvailablePort()
- _config_params = ['_testServerPort', '_testServer2Port']
+ _config_params = ["_testServerPort", "_testServer2Port"]
_config_template = """
setServerPolicy(firstAvailable)
s1 = newServer{address="127.0.0.1:%d", order=2}
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2 = threading.Thread(
+ name="UDP Responder 2",
+ target=cls.UDPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2 = threading.Thread(
+ name="TCP Responder 2",
+ target=cls.TCPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
because it has the lower order value.
"""
numberOfQueries = 50
- name = 'order.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "order.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for _ in range(numberOfQueries):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
- if 'UDP Responder' in self._responsesCounter:
- self.assertEqual(self._responsesCounter['UDP Responder'], 0)
- self.assertEqual(self._responsesCounter['UDP Responder 2'], numberOfQueries)
- if 'TCP Responder' in self._responsesCounter:
- self.assertEqual(self._responsesCounter['TCP Responder'], 0)
- self.assertEqual(self._responsesCounter['TCP Responder 2'], numberOfQueries)
+ if "UDP Responder" in self._responsesCounter:
+ self.assertEqual(self._responsesCounter["UDP Responder"], 0)
+ self.assertEqual(self._responsesCounter["UDP Responder 2"], numberOfQueries)
+ if "TCP Responder" in self._responsesCounter:
+ self.assertEqual(self._responsesCounter["TCP Responder"], 0)
+ self.assertEqual(self._responsesCounter["TCP Responder 2"], numberOfQueries)
-class TestFirstAvailableQPSPacketCacheHits(DNSDistTest):
+class TestFirstAvailableQPSPacketCacheHits(DNSDistTest):
_verboseMode = True
_testServer2Port = pickAvailablePort()
- _config_params = ['_testServerPort', '_testServer2Port']
+ _config_params = ["_testServerPort", "_testServer2Port"]
_config_template = """
setServerPolicy(firstAvailable)
s1 = newServer{address="127.0.0.1:%d", order=2}
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2 = threading.Thread(
+ name="UDP Responder 2",
+ target=cls.UDPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2 = threading.Thread(
+ name="TCP Responder 2",
+ target=cls.TCPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
and the QPS should only be counted for cache misses.
"""
numberOfQueries = 50
- name = 'order-qps-cache.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "order-qps-cache.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
# first queries to fill the cache
self.assertEqual(receivedResponse, response)
numberOfQueries = 10
- name = 'order-qps-cache-2.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "order-qps-cache-2.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
# first queries to fill the cache
self.assertEqual(receivedResponse, response)
# 4 queries should made it through, 2 UDP and 2 TCP
- #for k,v in self._responsesCounter.items():
+ # for k,v in self._responsesCounter.items():
# print(k)
# print(v)
- if 'UDP Responder' in self._responsesCounter:
- self.assertEqual(self._responsesCounter['UDP Responder'], 0)
- self.assertEqual(self._responsesCounter['UDP Responder 2'], 2)
- if 'TCP Responder' in self._responsesCounter:
- self.assertEqual(self._responsesCounter['TCP Responder'], 0)
- self.assertEqual(self._responsesCounter['TCP Responder 2'], 2)
+ if "UDP Responder" in self._responsesCounter:
+ self.assertEqual(self._responsesCounter["UDP Responder"], 0)
+ self.assertEqual(self._responsesCounter["UDP Responder 2"], 2)
+ if "TCP Responder" in self._responsesCounter:
+ self.assertEqual(self._responsesCounter["TCP Responder"], 0)
+ self.assertEqual(self._responsesCounter["TCP Responder 2"], 2)
-class TestRoutingNoServer(DNSDistTest):
+class TestRoutingNoServer(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d", pool="real"}
setServFailWhenNoServer(true)
Routing: No server should return ServFail
"""
# without EDNS
- name = 'noserver.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "noserver.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.SERVFAIL)
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
# now with EDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, want_dnssec=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096, want_dnssec=False)
expectedResponse = dns.message.make_response(query, our_payload=1232)
expectedResponse.set_rcode(dns.rcode.SERVFAIL)
self.assertFalse(receivedResponse.ednsflags & dns.flags.DO)
self.assertEqual(receivedResponse.payload, 1232)
-class TestRoutingWRandom(DNSDistTest):
+class TestRoutingWRandom(DNSDistTest):
_testServer2Port = pickAvailablePort()
- _config_params = ['_testServerPort', '_testServer2Port']
+ _config_params = ["_testServerPort", "_testServer2Port"]
_config_template = """
setServerPolicy(wrandom)
setWeightedBalancingFactor(1.5)
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2 = threading.Thread(
+ name="UDP Responder 2",
+ target=cls.UDPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2 = threading.Thread(
+ name="TCP Responder 2",
+ target=cls.TCPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
check that dnsdist routes less than half to one, more to the other.
"""
numberOfQueries = 100
- name = 'wrandom.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "wrandom.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
# the counter is shared for UDP and TCP,
self.assertEqual(response, receivedResponse)
# The lower weight downstream should receive less than half the queries
- self.assertLess(self._responsesCounter['UDP Responder'], numberOfQueries * 0.50)
- self.assertLess(self._responsesCounter['TCP Responder'], numberOfQueries * 0.50)
+ self.assertLess(self._responsesCounter["UDP Responder"], numberOfQueries * 0.50)
+ self.assertLess(self._responsesCounter["TCP Responder"], numberOfQueries * 0.50)
# The higher weight downstream should receive more than half the queries
- self.assertGreater(self._responsesCounter['UDP Responder 2'], numberOfQueries * 0.50)
- self.assertGreater(self._responsesCounter['TCP Responder 2'], numberOfQueries * 0.50)
+ self.assertGreater(self._responsesCounter["UDP Responder 2"], numberOfQueries * 0.50)
+ self.assertGreater(self._responsesCounter["TCP Responder 2"], numberOfQueries * 0.50)
class TestRoutingHighValueWRandom(DNSDistTest):
-
_testServer2Port = pickAvailablePort()
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_testServer2Port']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort", "_testServer2Port"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2 = threading.Thread(
+ name="UDP Responder 2",
+ target=cls.UDPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2 = threading.Thread(
+ name="TCP Responder 2",
+ target=cls.TCPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
no-policy.
"""
numberOfQueries = 100
- name = 'wrandom-overflow.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "wrandom-overflow.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
# the counter is shared for UDP and TCP,
# Map to a dict with every other element being the value to the previous one
for i, x in enumerate(stats):
if not i % 2:
- stats_dict[x] = stats[i+1]
+ stats_dict[x] = stats[i + 1]
# There should be no queries getting "no-policy" responses
- self.assertEqual(stats_dict['no-policy'], '0')
+ self.assertEqual(stats_dict["no-policy"], "0")
# Each downstream should receive some queries, but it will be unbalanced
# because the sum of the weights is higher than INT_MAX.
# The first downstream will receive more than half the queries
- self.assertGreater(self._responsesCounter['UDP Responder'], numberOfQueries / 2)
- self.assertGreater(self._responsesCounter['TCP Responder'], numberOfQueries / 2)
+ self.assertGreater(self._responsesCounter["UDP Responder"], numberOfQueries / 2)
+ self.assertGreater(self._responsesCounter["TCP Responder"], numberOfQueries / 2)
# The second downstream will receive the remainder of the queries, but it might very well be 0
- if 'UDP Responder 2' in self._responsesCounter:
- self.assertEqual(self._responsesCounter['UDP Responder 2'], numberOfQueries - self._responsesCounter['UDP Responder'])
- if 'TCP Responder 2' in self._responsesCounter:
- self.assertEqual(self._responsesCounter['TCP Responder 2'], numberOfQueries - self._responsesCounter['TCP Responder'])
+ if "UDP Responder 2" in self._responsesCounter:
+ self.assertEqual(
+ self._responsesCounter["UDP Responder 2"], numberOfQueries - self._responsesCounter["UDP Responder"]
+ )
+ if "TCP Responder 2" in self._responsesCounter:
+ self.assertEqual(
+ self._responsesCounter["TCP Responder 2"], numberOfQueries - self._responsesCounter["TCP Responder"]
+ )
-class TestRoutingWHashed(DNSDistTest):
+class TestRoutingWHashed(DNSDistTest):
_testServer2Port = pickAvailablePort()
- _config_params = ['_testServerPort', '_testServer2Port']
+ _config_params = ["_testServerPort", "_testServer2Port"]
_config_template = """
setServerPolicy(whashed)
setWeightedBalancingFactor(1.5)
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2 = threading.Thread(
+ name="UDP Responder 2",
+ target=cls.UDPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2 = threading.Thread(
+ name="TCP Responder 2",
+ target=cls.TCPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
will not be perfect, especially with so few datapoints, but still).
"""
numberOfQueries = 100
- suffix = 'whashed.routing.tests.powerdns.com.'
+ suffix = "whashed.routing.tests.powerdns.com."
# the counter is shared for UDP and TCP,
# so we need to do UDP then TCP to have a clean count
for idx in range(numberOfQueries):
- name = str(idx) + '.udp.' + suffix
- query = dns.message.make_query(name, 'A', 'IN')
+ name = str(idx) + ".udp." + suffix
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
receivedQuery.id = query.id
self.assertEqual(response, receivedResponse)
for idx in range(numberOfQueries):
- name = str(idx) + '.tcp.' + suffix
- query = dns.message.make_query(name, 'A', 'IN')
+ name = str(idx) + ".tcp." + suffix
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
- self.assertGreater(self._responsesCounter['UDP Responder'], numberOfQueries * 0.25)
- self.assertGreater(self._responsesCounter['TCP Responder'], numberOfQueries * 0.25)
- self.assertGreater(self._responsesCounter['UDP Responder 2'], numberOfQueries * 0.25)
- self.assertGreater(self._responsesCounter['TCP Responder 2'], numberOfQueries * 0.25)
+ self.assertGreater(self._responsesCounter["UDP Responder"], numberOfQueries * 0.25)
+ self.assertGreater(self._responsesCounter["TCP Responder"], numberOfQueries * 0.25)
+ self.assertGreater(self._responsesCounter["UDP Responder 2"], numberOfQueries * 0.25)
+ self.assertGreater(self._responsesCounter["TCP Responder 2"], numberOfQueries * 0.25)
-class TestRoutingCHashed(DNSDistTest):
+class TestRoutingCHashed(DNSDistTest):
_testServer2Port = pickAvailablePort()
- _config_params = ['_testServerPort', '_testServer2Port']
+ _config_params = ["_testServerPort", "_testServer2Port"]
_config_template = """
setServerPolicy(chashed)
setConsistentHashingBalancingFactor(1.5)
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2 = threading.Thread(
+ name="UDP Responder 2",
+ target=cls.UDPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2 = threading.Thread(
+ name="TCP Responder 2",
+ target=cls.TCPResponder,
+ args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
will not be perfect, especially with so few datapoints, but still).
"""
numberOfQueries = 100
- suffix = 'chashed.routing.tests.powerdns.com.'
+ suffix = "chashed.routing.tests.powerdns.com."
# the counter is shared for UDP and TCP,
# so we need to do UDP then TCP to have a clean count
for idx in range(numberOfQueries):
- name = str(idx) + '.udp.' + suffix
- query = dns.message.make_query(name, 'A', 'IN')
+ name = str(idx) + ".udp." + suffix
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
receivedQuery.id = query.id
self.assertEqual(response, receivedResponse)
for idx in range(numberOfQueries):
- name = str(idx) + '.tcp.' + suffix
- query = dns.message.make_query(name, 'A', 'IN')
+ name = str(idx) + ".tcp." + suffix
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
- self.assertGreater(self._responsesCounter['UDP Responder'], numberOfQueries * 0.25)
- self.assertGreater(self._responsesCounter['TCP Responder'], numberOfQueries * 0.25)
- self.assertGreater(self._responsesCounter['UDP Responder 2'], numberOfQueries * 0.25)
- self.assertGreater(self._responsesCounter['TCP Responder 2'], numberOfQueries * 0.25)
+ self.assertGreater(self._responsesCounter["UDP Responder"], numberOfQueries * 0.25)
+ self.assertGreater(self._responsesCounter["TCP Responder"], numberOfQueries * 0.25)
+ self.assertGreater(self._responsesCounter["UDP Responder 2"], numberOfQueries * 0.25)
+ self.assertGreater(self._responsesCounter["TCP Responder 2"], numberOfQueries * 0.25)
-class TestRoutingLuaFFILBNoServer(DNSDistTest):
+class TestRoutingLuaFFILBNoServer(DNSDistTest):
_config_template = """
-- we want a ServFail answer when all servers are down
setServFailWhenNoServer(true)
"""
Routing: LuaFFI policy, all servers are down
"""
- name = 'lua-ffi-no-servers.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "lua-ffi-no-servers.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.SERVFAIL)
class QueryCounter:
-
def __init__(self, name):
self.name = name
self.refuse = False
def callback(request):
self.qcnt += 1
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(request.question[0].name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.set_rcode(dns.rcode.REFUSED) if self.refuse else response.answer.append(rrset)
return response.to_wire()
+
return callback
-class TestRoutingOrderedWRandUntag(DNSDistTest):
+class TestRoutingOrderedWRandUntag(DNSDistTest):
_queryCounts = {}
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_testServer1Port = pickAvailablePort()
_testServer2Port = pickAvailablePort()
_testServer3Port = pickAvailablePort()
_testServer4Port = pickAvailablePort()
_serverPorts = [_testServer1Port, _testServer2Port, _testServer3Port, _testServer4Port]
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServer1Port', '_testServer2Port', '_testServer3Port', '_testServer4Port']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServer1Port",
+ "_testServer2Port",
+ "_testServer3Port",
+ "_testServer4Port",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
def startResponders(cls):
print("Launching responders..")
- for i, name in enumerate(['s11', 's12', 's21', 's22']):
+ for i, name in enumerate(["s11", "s12", "s21", "s22"]):
cls._queryCounts[name] = QueryCounter(name)
cb = cls._queryCounts[name].create_cb()
- responder = threading.Thread(name=name, target=cls.UDPResponder, args=[cls._serverPorts[i], cls._toResponderQueue, cls._fromResponderQueue, False, cb])
+ responder = threading.Thread(
+ name=name,
+ target=cls.UDPResponder,
+ args=[cls._serverPorts[i], cls._toResponderQueue, cls._fromResponderQueue, False, cb],
+ )
responder.daemon = True
responder.start()
check that dnsdist routes based on order first then weighted.
"""
numberOfQueries = 100
- name = 'ordered.wrand.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "ordered.wrand.routing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
### test normal first ordered then random weighted routing ###
self.assertEqual(expectedResponse, receivedResponse)
# Only order 1 servers get queries and weighted
- self.assertGreater(self._queryCounts['s12'](), numberOfQueries * 0.50)
- self.assertLess(self._queryCounts['s11'](), numberOfQueries * 0.50)
- self.assertEqual(self._queryCounts['s21'](), 0)
- self.assertEqual(self._queryCounts['s22'](), 0)
+ self.assertGreater(self._queryCounts["s12"](), numberOfQueries * 0.50)
+ self.assertLess(self._queryCounts["s11"](), numberOfQueries * 0.50)
+ self.assertEqual(self._queryCounts["s21"](), 0)
+ self.assertEqual(self._queryCounts["s22"](), 0)
### test tagged servers for restart
# reset counters
- for name in ['s11', 's12', 's21', 's22']:
+ for name in ["s11", "s12", "s21", "s22"]:
self._queryCounts[name].reset()
- self._queryCounts['s11'].set_refuse(True)
- self.setServerDown('s12')
+ self._queryCounts["s11"].set_refuse(True)
+ self.setServerDown("s12")
# send 100 queries
for _ in range(numberOfQueries):
# s11 receives all 100 initial queries and always refuse to trigger restart
# s12 is not selected for both initial and restarted queries
# s21+s22 shall receive all the 100 restarted queries
- self.assertEqual(self._queryCounts['s11'](), numberOfQueries)
- self.assertEqual(self._queryCounts['s12'](), 0)
- self.assertEqual(self._queryCounts['s21']()+self._queryCounts['s22'](), numberOfQueries)
+ self.assertEqual(self._queryCounts["s11"](), numberOfQueries)
+ self.assertEqual(self._queryCounts["s12"](), 0)
+ self.assertEqual(self._queryCounts["s21"]() + self._queryCounts["s22"](), numberOfQueries)
- self._queryCounts['s11'].set_refuse(False)
- self.setServerUp('s12')
+ self._queryCounts["s11"].set_refuse(False)
+ self.setServerUp("s12")
### further test server down conditions ###
# reset counters
- for name in ['s11', 's12', 's21', 's22']:
+ for name in ["s11", "s12", "s21", "s22"]:
self._queryCounts[name].reset()
- self.setServerDown('s11')
+ self.setServerDown("s11")
# send 100 queries
for _ in range(numberOfQueries):
self.assertEqual(expectedResponse, receivedResponse)
# queries shall arrive 's12' only
- self.assertEqual(self._queryCounts['s11'](), 0)
- self.assertEqual(self._queryCounts['s12'](), numberOfQueries)
- self.assertEqual(self._queryCounts['s21'](), 0)
- self.assertEqual(self._queryCounts['s22'](), 0)
+ self.assertEqual(self._queryCounts["s11"](), 0)
+ self.assertEqual(self._queryCounts["s12"](), numberOfQueries)
+ self.assertEqual(self._queryCounts["s21"](), 0)
+ self.assertEqual(self._queryCounts["s22"](), 0)
# reset counters
- for name in ['s11', 's12', 's21', 's22']:
+ for name in ["s11", "s12", "s21", "s22"]:
self._queryCounts[name].reset()
- self.setServerDown('s12')
+ self.setServerDown("s12")
# send 100 queries
for _ in range(numberOfQueries):
self.assertEqual(expectedResponse, receivedResponse)
# queries now shall be sent to order 2 servers and weighted
- self.assertEqual(self._queryCounts['s11'](), 0)
- self.assertEqual(self._queryCounts['s12'](), 0)
- self.assertLess(self._queryCounts['s21'](), numberOfQueries * 0.50)
- self.assertGreater(self._queryCounts['s22'](), numberOfQueries * 0.50)
+ self.assertEqual(self._queryCounts["s11"](), 0)
+ self.assertEqual(self._queryCounts["s12"](), 0)
+ self.assertLess(self._queryCounts["s21"](), numberOfQueries * 0.50)
+ self.assertGreater(self._queryCounts["s22"](), numberOfQueries * 0.50)
import cookiesoption
from dnsdisttests import DNSDistTest
-class TestAdvancedAllow(DNSDistTest):
+class TestAdvancedAllow(DNSDistTest):
_config_template = """
addAction(AllRule(), NoneAction())
addAction(QNameSuffixRule("allowed.advanced.tests.powerdns.com."), AllowAction())
A query for allowed.advanced.tests.powerdns.com. should be allowed
while others should be dropped.
"""
- name = 'allowed.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "allowed.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
A query for notallowed.advanced.tests.powerdns.com. should be dropped.
"""
- name = 'notallowed.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "notallowed.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
-class TestAdvancedRemoveRD(DNSDistTest):
+class TestAdvancedRemoveRD(DNSDistTest):
_config_template = """
addAction("norecurse.advanced.tests.powerdns.com.", SetNoRecurseAction())
newServer{address="127.0.0.1:%d"}
Send a query with RD,
check that dnsdist clears the RD flag.
"""
- name = 'norecurse.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ name = "norecurse.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN")
expectedQuery.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send a query with RD for a canary domain,
check that dnsdist does not clear the RD flag.
"""
- name = 'keeprecurse.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "keeprecurse.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-class TestAdvancedAddCD(DNSDistTest):
+class TestAdvancedAddCD(DNSDistTest):
_config_template = """
addAction("setcd.advanced.tests.powerdns.com.", SetDisableValidationAction())
newServer{address="127.0.0.1:%d"}
Send a query with CD cleared,
check that dnsdist set the CD flag.
"""
- name = 'setcd.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ name = "setcd.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN")
expectedQuery.flags |= dns.flags.CD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send a query without CD for a canary domain,
check that dnsdist does not set the CD flag.
"""
- name = 'keepnocd.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "keepnocd.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-class TestAdvancedClearRD(DNSDistTest):
+class TestAdvancedClearRD(DNSDistTest):
_config_template = """
addAction("clearrd.advanced.tests.powerdns.com.", SetNoRecurseAction())
newServer{address="127.0.0.1:%d"}
Send a query with RD set,
check that dnsdist clears the RD flag.
"""
- name = 'clearrd.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ name = "clearrd.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN")
expectedQuery.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send a query with RD for a canary domain,
check that dnsdist does not clear the RD flag.
"""
- name = 'keeprd.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "keeprd.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
class TestAdvancedDelay(DNSDistTest):
-
_config_template = """
addAction(AllRule(), DelayAction(1000))
newServer{address="127.0.0.1:%d"}
check that the response delay is longer than 1000 ms
over UDP, less than that over TCP.
"""
- name = 'tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
begin = datetime.now()
self.assertEqual(response, receivedResponse)
self.assertLess(end - begin, timedelta(0, 1))
-class TestAdvancedAndNot(DNSDistTest):
+class TestAdvancedAndNot(DNSDistTest):
_config_template = """
addAction(AndRule({NotRule(QTypeRule("A")), TCPRule(false)}), RCodeAction(DNSRCode.NOTIMP))
newServer{address="127.0.0.1:%d"}
"""
+
def testAOverUDPReturnsNotImplementedCanary(self):
"""
Advanced: !A && UDP canary
We send an A query over UDP and TCP, and check that the
response is OK.
"""
- name = 'andnot.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "andnot.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
We send a TXT query over UDP and TCP, and check that the
response is OK for TCP and 'not implemented' for UDP.
"""
- name = 'andnot.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'TXT', 'IN')
+ name = "andnot.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "TXT", "IN")
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
self.assertEqual(receivedResponse, expectedResponse)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- 'nothing to see here')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, "nothing to see here")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
-class TestAdvancedOr(DNSDistTest):
+class TestAdvancedOr(DNSDistTest):
_config_template = """
addAction(OrRule({QTypeRule("A"), TCPRule(false)}), RCodeAction(DNSRCode.NOTIMP))
newServer{address="127.0.0.1:%d"}
"""
+
def testAAAAOverUDPReturnsNotImplemented(self):
"""
Advanced: A || UDP: AAAA
We send an AAAA query over UDP and TCP, and check that the
response is 'not implemented' for UDP and OK for TCP.
"""
- name = 'aorudp.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "aorudp.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
We send an A query over UDP and TCP, and check that the
response is 'not implemented' for both.
"""
- name = 'aorudp.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "aorudp.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
class TestAdvancedLogAction(DNSDistTest):
-
_config_template = """
newServer{address="127.0.0.1:%d"}
addAction(AllRule(), LogAction("dnsdist.log", false))
"""
+
def testAdvancedLogAction(self):
"""
Advanced: Log all queries
"""
count = 50
- name = 'logaction.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "logaction.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for _ in range(count):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
- self.assertTrue(os.path.isfile('dnsdist.log'))
- self.assertGreater(os.stat('dnsdist.log').st_size, 0)
+ self.assertTrue(os.path.isfile("dnsdist.log"))
+ self.assertGreater(os.stat("dnsdist.log").st_size, 0)
-class TestAdvancedDNSSEC(DNSDistTest):
+class TestAdvancedDNSSEC(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
addAction(DNSSECRule(), DropAction())
"""
+
def testAdvancedDNSSECDrop(self):
"""
Advanced: DNSSEC Rule
"""
- name = 'dnssec.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- doquery = dns.message.make_query(name, 'A', 'IN', want_dnssec=True)
+ name = "dnssec.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ doquery = dns.message.make_query(name, "A", "IN", want_dnssec=True)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
(_, receivedResponse) = sender(doquery, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
-class TestAdvancedQClass(DNSDistTest):
+class TestAdvancedQClass(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
addAction(QClassRule(DNSClass.CHAOS), DropAction())
"""
+
def testAdvancedQClassChaosDrop(self):
"""
Advanced: Drop QClass CHAOS
"""
- name = 'qclasschaos.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'TXT', 'CHAOS')
+ name = "qclasschaos.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "TXT", "CHAOS")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
Advanced: Allow QClass IN
"""
- name = 'qclassin.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "qclassin.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-class TestAdvancedOpcode(DNSDistTest):
+class TestAdvancedOpcode(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
addAction(OpcodeRule(DNSOpcode.Notify), DropAction())
"""
+
def testAdvancedOpcodeNotifyDrop(self):
"""
Advanced: Drop Opcode NOTIFY
"""
- name = 'opcodenotify.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "opcodenotify.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.set_opcode(dns.opcode.NOTIFY)
for method in ("sendUDPQuery", "sendTCPQuery"):
Advanced: Allow Opcode UPDATE
"""
- name = 'opcodeupdate.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'SOA', 'IN')
+ name = "opcodeupdate.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "SOA", "IN")
query.set_opcode(dns.opcode.UPDATE)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-class TestAdvancedNonTerminalRule(DNSDistTest):
+class TestAdvancedNonTerminalRule(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d", pool="real"}
addAction(AllRule(), SetDisableValidationAction())
addAction(AllRule(), PoolAction("real"))
addAction(AllRule(), DropAction())
"""
+
def testAdvancedNonTerminalRules(self):
"""
Advanced: Non terminal rules
but does not stop the processing, then that
PoolAction() is applied _and_ stop the processing.
"""
- name = 'nonterminal.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ name = "nonterminal.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN")
expectedQuery.flags |= dns.flags.CD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(expectedQuery, receivedQuery)
self.assertEqual(response, receivedResponse)
-class TestAdvancedRestoreFlagsOnSelfResponse(DNSDistTest):
+class TestAdvancedRestoreFlagsOnSelfResponse(DNSDistTest):
_config_template = """
addAction(AllRule(), SetDisableValidationAction())
addAction(AllRule(), SpoofAction("192.0.2.1"))
instructed to set it, then to spoof the response,
check that response has the flag cleared.
"""
- name = 'spoofed.restoreflags.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "spoofed.restoreflags.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(receivedResponse)
self.assertEqual(response, receivedResponse)
-class TestAdvancedQPS(DNSDistTest):
+class TestAdvancedQPS(DNSDistTest):
_config_template = """
addAction("qps.advanced.tests.powerdns.com", QPSAction(10))
newServer{address="127.0.0.1:%d"}
check that dnsdist drops queries when the max QPS has been reached.
"""
maxQPS = 10
- name = 'qps.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "qps.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for _ in range(maxQPS):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-
(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
-class TestAdvancedQPSNone(DNSDistTest):
+class TestAdvancedQPSNone(DNSDistTest):
_config_template = """
addAction("qpsnone.advanced.tests.powerdns.com", QPSAction(100))
addAction(AllRule(), RCodeAction(DNSRCode.REFUSED))
check that the rule returns None when the QPS has not been
reached, not Allow.
"""
- name = 'qpsnone.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "qpsnone.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestAdvancedNMGRule(DNSDistTest):
+class TestAdvancedNMGRule(DNSDistTest):
_config_template = """
allowed = newNMG()
allowed:addMask("192.0.2.1/32")
Send queries to "nmgrule.advanced.tests.powerdns.com.",
check that we are getting a REFUSED response.
"""
- name = 'nmgrule.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nmgrule.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
+
class TestAdvancedNMGAddNMG(DNSDistTest):
_config_template = """
oneNMG = newNMG()
"""
Advanced: NMGRule:addNMG()
"""
- name = 'nmgrule-addnmg.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nmgrule-addnmg.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
- (_,receivedResponse) = sender(query, response=expectedResponse, useQueue=False)
+ (_, receivedResponse) = sender(query, response=expectedResponse, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestAdvancedNMGRuleFromString(DNSDistTest):
+class TestAdvancedNMGRuleFromString(DNSDistTest):
_config_template = """
addAction(NotRule(NetmaskGroupRule('192.0.2.1')), RCodeAction(DNSRCode.REFUSED))
newServer{address="127.0.0.1:%d"}
"""
Advanced: NMGRule (from string) should refuse our queries
"""
- name = 'nmgrule-from-string.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nmgrule-from-string.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestAdvancedNMGRuleFromMultipleStrings(DNSDistTest):
+class TestAdvancedNMGRuleFromMultipleStrings(DNSDistTest):
_config_template = """
addAction(NotRule(NetmaskGroupRule({'192.0.2.1', '192.0.2.128/25'})), RCodeAction(DNSRCode.REFUSED))
newServer{address="127.0.0.1:%d"}
"""
Advanced: NMGRule (from multiple strings) should refuse our queries
"""
- name = 'nmgrule-from-multiple-strings.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nmgrule-from-multiple-strings.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestDSTPortRule(DNSDistTest):
- _config_params = ['_dnsDistPort', '_testServerPort']
+class TestDSTPortRule(DNSDistTest):
+ _config_params = ["_dnsDistPort", "_testServerPort"]
_config_template = """
addAction(DSTPortRule(%d), RCodeAction(DNSRCode.REFUSED))
newServer{address="127.0.0.1:%d"}
check that we are getting a REFUSED response.
"""
- name = 'dstportrule.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "dstportrule.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestAdvancedLabelsCountRule(DNSDistTest):
+class TestAdvancedLabelsCountRule(DNSDistTest):
_config_template = """
addAction(QNameLabelsCountRule(5,6), RCodeAction(DNSRCode.REFUSED))
newServer{address="127.0.0.1:%d"}
Advanced: QNameLabelsCountRule(5,6)
"""
# 6 labels, we should be fine
- name = 'ok.labelscount.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "ok.labelscount.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(response, receivedResponse)
# more than 6 labels, the query should be refused
- name = 'not.ok.labelscount.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "not.ok.labelscount.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
self.assertEqual(receivedResponse, expectedResponse)
# less than 5 labels, the query should be refused
- name = 'labelscountadvanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "labelscountadvanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestAdvancedWireLengthRule(DNSDistTest):
+class TestAdvancedWireLengthRule(DNSDistTest):
_config_template = """
addAction(QNameWireLengthRule(54,56), RCodeAction(DNSRCode.REFUSED))
newServer{address="127.0.0.1:%d"}
"""
Advanced: QNameWireLengthRule(54,56)
"""
- name = 'longenough.qnamewirelength.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "longenough.qnamewirelength.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(response, receivedResponse)
# too short, the query should be refused
- name = 'short.qnamewirelength.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "short.qnamewirelength.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
self.assertEqual(receivedResponse, expectedResponse)
# too long, the query should be refused
- name = 'toolongtobevalid.qnamewirelength.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "toolongtobevalid.qnamewirelength.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestAdvancedLuaDO(DNSDistTest):
+class TestAdvancedLuaDO(DNSDistTest):
_config_template = """
function nxDOLua(dq)
if dq:getDO() then
"""
Advanced: Nx DO queries via Lua
"""
- name = 'nxdo.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nxdo.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
- queryWithDO = dns.message.make_query(name, 'A', 'IN', want_dnssec=True)
+ queryWithDO = dns.message.make_query(name, "A", "IN", want_dnssec=True)
doResponse = dns.message.make_response(queryWithDO)
doResponse.set_rcode(dns.rcode.NXDOMAIN)
doResponse.id = receivedResponse.id
self.assertEqual(receivedResponse, doResponse)
-class TestAdvancedLuaRefused(DNSDistTest):
+class TestAdvancedLuaRefused(DNSDistTest):
_config_template = """
function refuse(dq)
return DNSAction.Refused, ""
"""
Advanced: Refused via Lua
"""
- name = 'refused.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "refused.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
refusedResponse = dns.message.make_response(query)
refusedResponse.set_rcode(dns.rcode.REFUSED)
refusedResponse.id = receivedResponse.id
self.assertEqual(receivedResponse, refusedResponse)
-class TestAdvancedLuaActionReturnSyntax(DNSDistTest):
+class TestAdvancedLuaActionReturnSyntax(DNSDistTest):
_config_template = """
function refuse(dq)
return DNSAction.Refused
"""
Advanced: Short syntax for LuaAction return values
"""
- name = 'short.refused.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "short.refused.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
refusedResponse = dns.message.make_response(query)
refusedResponse.set_rcode(dns.rcode.REFUSED)
refusedResponse.id = receivedResponse.id
self.assertEqual(receivedResponse, refusedResponse)
-class TestAdvancedLuaTruncated(DNSDistTest):
+class TestAdvancedLuaTruncated(DNSDistTest):
_config_template = """
function trunc(dq)
if not dq.tcp then
"""
Advanced: TC via Lua
"""
- name = 'tc.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tc.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist sets RA = RD for TC responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
truncatedResponse = dns.message.make_response(query)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
-class TestAdvancedRD(DNSDistTest):
+class TestAdvancedRD(DNSDistTest):
_config_template = """
addAction(RDRule(), RCodeAction(DNSRCode.REFUSED))
newServer{address="127.0.0.1:%d"}
"""
Advanced: RD query is refused
"""
- name = 'rd.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "rd.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
expectedResponse.flags |= dns.flags.RA
"""
Advanced: No-RD query is allowed
"""
- name = 'no-rd.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "no-rd.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
-class TestAdvancedLuaTempFailureTTL(DNSDistTest):
+class TestAdvancedLuaTempFailureTTL(DNSDistTest):
_config_template = """
function testAction(dq)
if dq.tempFailureTTL ~= nil then
"""
Advanced: Exercise dq.tempFailureTTL Lua binding
"""
- name = 'tempfailurettlbinding.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tempfailurettlbinding.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
-class TestAdvancedEDNSOptionRule(DNSDistTest):
+class TestAdvancedEDNSOptionRule(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
addAction(EDNSOptionRule(EDNSOptionCode.ECS), DropAction())
Advanced: A question with ECS is dropped
"""
- name = 'ednsoptionrule.advanced.tests.powerdns.com.'
+ name = "ednsoptionrule.advanced.tests.powerdns.com."
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
Advanced: A question without ECS is answered
"""
- name = 'ednsoptionrule.advanced.tests.powerdns.com.'
+ name = "ednsoptionrule.advanced.tests.powerdns.com."
# both with EDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[], payload=512)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, options=[], payload=512)
response = dns.message.make_response(query)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedResponse, response)
# and with no EDNS at all
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
-class TestAdvancedEDNSVersionRule(DNSDistTest):
+class TestAdvancedEDNSVersionRule(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
addAction(EDNSVersionRule(0), ERCodeAction(DNSRCode.BADVERS))
if sys.version_info >= (3, 11) and sys.version_info < (3, 12):
raise unittest.SkipTest("Test skipped, see https://github.com/PowerDNS/pdns/pull/12912")
- name = 'ednsversionrule.advanced.tests.powerdns.com.'
+ name = "ednsversionrule.advanced.tests.powerdns.com."
- query = dns.message.make_query(name, 'A', 'IN', use_edns=1)
+ query = dns.message.make_query(name, "A", "IN", use_edns=1)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.BADVERS)
Advanced: A question with ECS version 0 goes through
"""
- name = 'ednsversionrule.advanced.tests.powerdns.com.'
+ name = "ednsversionrule.advanced.tests.powerdns.com."
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
response = dns.message.make_response(query)
for method in ("sendUDPQuery", "sendTCPQuery"):
Advanced: A question without ECS goes through
"""
- name = 'ednsoptionrule.advanced.tests.powerdns.com.'
+ name = "ednsoptionrule.advanced.tests.powerdns.com."
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, response)
-class TestSetRules(DNSDistTest):
+class TestSetRules(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
Advanced: Clear rules, set rules
"""
- name = 'clearthensetrules.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "clearthensetrules.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(expectedResponse, receivedResponse)
# insert a new spoofing rule
- self.sendConsoleCommand("setRules({ newRuleAction(AllRule(), SpoofAction(\"192.0.2.2\")) })")
+ self.sendConsoleCommand('setRules({ newRuleAction(AllRule(), SpoofAction("192.0.2.2")) })')
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(receivedResponse)
self.assertEqual(expectedResponse, receivedResponse)
+
class TestRmRules(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
"""
lines = self.sendConsoleCommand("showRules({showUUIDs=true})").splitlines()
self.assertEqual(len(lines), 3)
- self.assertIn('myFirstRule', lines[1])
- self.assertIn('mySecondRule', lines[2])
- self.assertIn('090736ca-2fb6-41e7-a836-58efaca3d71e', lines[1])
+ self.assertIn("myFirstRule", lines[1])
+ self.assertIn("mySecondRule", lines[2])
+ self.assertIn("090736ca-2fb6-41e7-a836-58efaca3d71e", lines[1])
lines = self.sendConsoleCommand("showResponseRules({showUUIDs=true})").splitlines()
self.assertEqual(len(lines), 3)
- self.assertIn('myFirstResponseRule', lines[1])
- self.assertIn('mySecondResponseRule', lines[2])
- self.assertIn('745a03b5-89e0-4eee-a6bf-c9700b0d31f0', lines[1])
+ self.assertIn("myFirstResponseRule", lines[1])
+ self.assertIn("mySecondResponseRule", lines[2])
+ self.assertIn("745a03b5-89e0-4eee-a6bf-c9700b0d31f0", lines[1])
self.sendConsoleCommand("rmRule('090736ca-2fb6-41e7-a836-58efaca3d71e')")
self.sendConsoleCommand("rmRule('mySecondRule')")
lines = self.sendConsoleCommand("showResponseRules({showUUIDs=true})").splitlines()
self.assertEqual(len(lines), 1)
-class TestAdvancedContinueAction(DNSDistTest):
+class TestAdvancedContinueAction(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d", pool="mypool"}
addAction("nocontinue.continue-action.advanced.tests.powerdns.com.", PoolAction("mypool"))
Advanced: Query routed to pool, PoolAction should be terminal
"""
- name = 'nocontinue.continue-action.advanced.tests.powerdns.com.'
+ name = "nocontinue.continue-action.advanced.tests.powerdns.com."
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
expectedResponse = dns.message.make_response(query)
Advanced: Query routed to pool, ContinueAction() should not stop the processing
"""
- name = 'continue.continue-action.advanced.tests.powerdns.com.'
+ name = "continue.continue-action.advanced.tests.powerdns.com."
- query = dns.message.make_query(name, 'A', 'IN')
- expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
+ expectedQuery = dns.message.make_query(name, "A", "IN")
expectedQuery.flags |= dns.flags.CD
response = dns.message.make_response(query)
self.assertEqual(receivedQuery, expectedQuery)
self.assertEqual(receivedResponse, expectedResponse)
-class TestAdvancedNegativeAndSOA(DNSDistTest):
+class TestAdvancedNegativeAndSOA(DNSDistTest):
_selfGeneratedPayloadSize = 1232
_config_template = """
addAction("nxd.negativeandsoa.advanced.tests.powerdns.com.", NegativeAndSOAAction(true, "auth.", 42, "mname", "rname", 5, 4, 3, 2, 1))
setPayloadSizeOnSelfGeneratedAnswers(%d)
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_selfGeneratedPayloadSize', '_testServerPort']
-
+ _config_params = ["_selfGeneratedPayloadSize", "_testServerPort"]
def testAdvancedNegativeAndSOANXD(self):
"""
Advanced: NegativeAndSOAAction NXD
"""
- name = 'nxd.negativeandsoa.advanced.tests.powerdns.com.'
+ name = "nxd.negativeandsoa.advanced.tests.powerdns.com."
# no EDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NXDOMAIN)
- soa = dns.rrset.from_text("auth.",
- 42,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'mname. rname. 5 4 3 2 1')
+ soa = dns.rrset.from_text("auth.", 42, dns.rdataclass.IN, dns.rdatatype.SOA, "mname. rname. 5 4 3 2 1")
expectedResponse.additional.append(soa)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
# withEDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=self._selfGeneratedPayloadSize)
expectedResponse.set_rcode(dns.rcode.NXDOMAIN)
- soa = dns.rrset.from_text("auth.",
- 42,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'mname. rname. 5 4 3 2 1')
+ soa = dns.rrset.from_text("auth.", 42, dns.rdataclass.IN, dns.rdatatype.SOA, "mname. rname. 5 4 3 2 1")
expectedResponse.additional.append(soa)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Advanced: NegativeAndSOAAction NoData
"""
- name = 'nodata.negativeandsoa.advanced.tests.powerdns.com.'
+ name = "nodata.negativeandsoa.advanced.tests.powerdns.com."
# no EDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NOERROR)
- soa = dns.rrset.from_text("another-auth.",
- 42,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'mname. rname. 1 2 3 4 5')
+ soa = dns.rrset.from_text("another-auth.", 42, dns.rdataclass.IN, dns.rdatatype.SOA, "mname. rname. 1 2 3 4 5")
expectedResponse.additional.append(soa)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
# with EDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=self._selfGeneratedPayloadSize)
expectedResponse.set_rcode(dns.rcode.NOERROR)
- soa = dns.rrset.from_text("another-auth.",
- 42,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'mname. rname. 1 2 3 4 5')
+ soa = dns.rrset.from_text("another-auth.", 42, dns.rdataclass.IN, dns.rdatatype.SOA, "mname. rname. 1 2 3 4 5")
expectedResponse.additional.append(soa)
for method in ("sendUDPQuery", "sendTCPQuery"):
class TestAdvancedNegativeAndSOAAuthSection(DNSDistTest):
-
_selfGeneratedPayloadSize = 1232
_config_template = """
addAction("nxd.negativeandsoa.advanced.tests.powerdns.com.", NegativeAndSOAAction(true, "auth.", 42, "mname", "rname", 5, 4, 3, 2, 1, { soaInAuthoritySection=true }))
setPayloadSizeOnSelfGeneratedAnswers(%d)
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_selfGeneratedPayloadSize', '_testServerPort']
-
+ _config_params = ["_selfGeneratedPayloadSize", "_testServerPort"]
def testAdvancedNegativeAndSOANXD(self):
"""
Advanced: NegativeAndSOAAction NXD
"""
- name = 'nxd.negativeandsoa.advanced.tests.powerdns.com.'
+ name = "nxd.negativeandsoa.advanced.tests.powerdns.com."
# no EDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NXDOMAIN)
- soa = dns.rrset.from_text("auth.",
- 42,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'mname. rname. 5 4 3 2 1')
+ soa = dns.rrset.from_text("auth.", 42, dns.rdataclass.IN, dns.rdatatype.SOA, "mname. rname. 5 4 3 2 1")
expectedResponse.authority.append(soa)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
# withEDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=self._selfGeneratedPayloadSize)
expectedResponse.set_rcode(dns.rcode.NXDOMAIN)
- soa = dns.rrset.from_text("auth.",
- 42,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'mname. rname. 5 4 3 2 1')
+ soa = dns.rrset.from_text("auth.", 42, dns.rdataclass.IN, dns.rdatatype.SOA, "mname. rname. 5 4 3 2 1")
expectedResponse.authority.append(soa)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Advanced: NegativeAndSOAAction NoData
"""
- name = 'nodata.negativeandsoa.advanced.tests.powerdns.com.'
+ name = "nodata.negativeandsoa.advanced.tests.powerdns.com."
# no EDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NOERROR)
- soa = dns.rrset.from_text("another-auth.",
- 42,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'mname. rname. 1 2 3 4 5')
+ soa = dns.rrset.from_text("another-auth.", 42, dns.rdataclass.IN, dns.rdatatype.SOA, "mname. rname. 1 2 3 4 5")
expectedResponse.authority.append(soa)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
# with EDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query, our_payload=self._selfGeneratedPayloadSize)
expectedResponse.set_rcode(dns.rcode.NOERROR)
- soa = dns.rrset.from_text("another-auth.",
- 42,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'mname. rname. 1 2 3 4 5')
+ soa = dns.rrset.from_text("another-auth.", 42, dns.rdataclass.IN, dns.rdatatype.SOA, "mname. rname. 1 2 3 4 5")
expectedResponse.authority.append(soa)
for method in ("sendUDPQuery", "sendTCPQuery"):
class TestAdvancedLuaRule(DNSDistTest):
-
_config_template = """
function luarulefunction(dq)
"""
Advanced: Test the LuaRule rule
"""
- name = 'lua-rule.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "lua-rule.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
notimplResponse = dns.message.make_response(query)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, notimplResponse)
- name = 'not-lua-rule.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "not-lua-rule.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
refusedResponse = dns.message.make_response(query)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, refusedResponse)
-class TestAdvancedSetEDNSOptionAction(DNSDistTest):
+class TestAdvancedSetEDNSOptionAction(DNSDistTest):
_config_template = """
addAction(AllRule(), SetEDNSOptionAction(10, "deadbeefdeadc0de"))
newServer{address="127.0.0.1:%d"}
"""
Advanced: Set EDNS Option
"""
- name = 'setednsoption.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "setednsoption.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadc0de')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=512, options=[eco])
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadc0de")
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=512, options=[eco])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Advanced: Set EDNS Option overwrites an existing option
"""
- name = 'setednsoption-overwrite.advanced.tests.powerdns.com.'
- initialECO = cookiesoption.CookiesOption(b'aaaaaaaa', b'bbbbbbbb')
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "setednsoption-overwrite.advanced.tests.powerdns.com."
+ initialECO = cookiesoption.CookiesOption(b"aaaaaaaa", b"bbbbbbbb")
+ query = dns.message.make_query(name, "A", "IN")
- overWrittenECO = cookiesoption.CookiesOption(b'deadbeef', b'deadc0de')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=512, options=[overWrittenECO])
+ overWrittenECO = cookiesoption.CookiesOption(b"deadbeef", b"deadc0de")
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=512, options=[overWrittenECO])
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Advanced: Set EDNS Option (DO bit set)
"""
# check that the DO bit is correctly handled, as we messed that up once
- name = 'setednsoption-do.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True, want_dnssec=True, payload=4096)
+ name = "setednsoption-do.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True, want_dnssec=True, payload=4096)
- eco = cookiesoption.CookiesOption(b'deadbeef', b'deadc0de')
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco], want_dnssec=True)
+ eco = cookiesoption.CookiesOption(b"deadbeef", b"deadc0de")
+ expectedQuery = dns.message.make_query(
+ name, "A", "IN", use_edns=True, payload=4096, options=[eco], want_dnssec=True
+ )
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.checkResponseEDNSWithoutECS(response, receivedResponse)
self.checkQueryEDNS(expectedQuery, receivedQuery)
-class TestAdvancedLuaGetContent(DNSDistTest):
+class TestAdvancedLuaGetContent(DNSDistTest):
_config_template = """
function accessContentLua(dq)
local expectedSize = 57
"""
Advanced: Test getContent() via Lua
"""
- name = 'get-content.advanced.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "get-content.advanced.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
from dnsdisttests import DNSDistTest, pickAvailablePort
+
class TestSNI(DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverKeyEC = 'server-ec.key'
- _serverCertEC = 'server-ec.chain'
- _serverKey2 = 'server2.key'
- _serverCert2 = 'server2.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _serverName2 = 'tls2.tests.dnsdist.org'
- _serverName3 = 'unknown.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverKeyEC = "server-ec.key"
+ _serverCertEC = "server-ec.chain"
+ _serverKey2 = "server2.key"
+ _serverCert2 = "server2.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _serverName2 = "tls2.tests.dnsdist.org"
+ _serverName3 = "unknown.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_dohWithNGHTTP2ServerPort = pickAvailablePort()
_doqServerPort = pickAvailablePort()
_doh3ServerPort = pickAvailablePort()
- _dohWithNGHTTP2BaseURL = ("https://%s:%d/" % (_serverName, _dohWithNGHTTP2ServerPort))
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doh3ServerPort))
+ _dohWithNGHTTP2BaseURL = "https://%s:%d/" % (_serverName, _dohWithNGHTTP2ServerPort)
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doh3ServerPort)
_config_template = """
newServer{address="127.0.0.1:%d"}
end
addAction(AllRule(), LuaAction(checkSNI))
"""
- _config_params = ['_testServerPort', '_serverCert', '_serverCertEC', '_serverCert2', '_serverKey', '_serverKeyEC', '_serverKey2', '_serverCert', '_serverKey', '_tlsServerPort', '_dohWithNGHTTP2ServerPort', '_doqServerPort', '_doh3ServerPort', '_serverName', '_serverName2', '_serverName3', '_serverName', '_serverName']
-
- @unittest.skipUnless('ENABLE_SNI_TESTS_WITH_QUICHE' in os.environ, "SNI tests with Quiche are disabled")
+ _config_params = [
+ "_testServerPort",
+ "_serverCert",
+ "_serverCertEC",
+ "_serverCert2",
+ "_serverKey",
+ "_serverKeyEC",
+ "_serverKey2",
+ "_serverCert",
+ "_serverKey",
+ "_tlsServerPort",
+ "_dohWithNGHTTP2ServerPort",
+ "_doqServerPort",
+ "_doh3ServerPort",
+ "_serverName",
+ "_serverName2",
+ "_serverName3",
+ "_serverName",
+ "_serverName",
+ ]
+
+ @unittest.skipUnless("ENABLE_SNI_TESTS_WITH_QUICHE" in os.environ, "SNI tests with Quiche are disabled")
def testServerNameIndicationWithQuiche(self):
- name = 'simple.sni.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple.sni.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ["sendDOQQueryWrapper", "sendDOH3QueryWrapper"]:
sender = getattr(self, method)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
self.assertTrue(receivedResponse)
- if method == 'sendDOQQueryWrapper':
+ if method == "sendDOQQueryWrapper":
# dnspython sets the ID to 0
receivedResponse.id = response.id
self.assertEqual(response, receivedResponse)
def testServerNameIndication(self):
- name = 'simple.sni.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "simple.sni.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ["sendDOTQueryWrapper", "sendDOHWithNGHTTP2QueryWrapper"]:
sender = getattr(self, method)
self.assertEqual(response, receivedResponse)
# check second certificate
- name = 'name2.sni.tests.powerdns.com.'
- self._dohWithNGHTTP2BaseURL = ("https://%s:%d/" % (self._serverName2, self._dohWithNGHTTP2ServerPort))
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "name2.sni.tests.powerdns.com."
+ self._dohWithNGHTTP2BaseURL = "https://%s:%d/" % (self._serverName2, self._dohWithNGHTTP2ServerPort)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ["sendDOTQueryWrapper", "sendDOHWithNGHTTP2QueryWrapper"]:
sender = getattr(self, method)
self.assertEqual(response, receivedResponse)
# check SNI for an unknown name, we should get the first certificate
- name = 'unknown.sni.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "unknown.sni.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
sslctx = ssl.create_default_context(cafile=self._caCert)
sslctx.check_hostname = False
- if hasattr(sslctx, 'set_alpn_protocols'):
+ if hasattr(sslctx, "set_alpn_protocols"):
sslctx.set_alpn_protocols(self._serverName3)
conn = self.openTLSConnection(self._tlsServerPort, self._serverName3, self._caCert, timeout=1, sslctx=sslctx)
self.assertEqual(receivedResponse, response)
cert = conn.getpeercert()
- subject = cert['subject']
- altNames = cert['subjectAltName']
- self.assertEqual(dict(subject[0])['commonName'], 'tls.tests.dnsdist.org')
- self.assertEqual(dict(subject[1])['organizationalUnitName'], 'PowerDNS.com BV')
+ subject = cert["subject"]
+ altNames = cert["subjectAltName"]
+ self.assertEqual(dict(subject[0])["commonName"], "tls.tests.dnsdist.org")
+ self.assertEqual(dict(subject[1])["organizationalUnitName"], "PowerDNS.com BV")
names = []
for entry in altNames:
names.append(entry[1])
- self.assertEqual(names, ['tls.tests.dnsdist.org', 'powerdns.com', '127.0.0.1'])
+ self.assertEqual(names, ["tls.tests.dnsdist.org", "powerdns.com", "127.0.0.1"])
# check that we provide the correct RSA/ECDSA certificate when requested
- for algo in ['rsa', 'ecdsa']:
- name = algo + '.sni.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ for algo in ["rsa", "ecdsa"]:
+ name = algo + ".sni.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
sslctx = ssl.create_default_context(cafile=self._caCert)
- if hasattr(sslctx, 'set_alpn_protocols'):
+ if hasattr(sslctx, "set_alpn_protocols"):
sslctx.set_alpn_protocols(self._serverName)
# disable TLS 1.3 because configuring the signature algorithm is not supported by Python yet
sslctx.maximum_version = ssl.TLSVersion.TLSv1_2
# explicitly request authentication via RSA or ECDSA
- sslctx.set_ciphers('a' + algo.upper())
+ sslctx.set_ciphers("a" + algo.upper())
conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert, timeout=1, sslctx=sslctx)
self.sendTCPQueryOverConnection(conn, query, response=response, timeout=1)
self.assertEqual(receivedResponse, response)
cert = conn.getpeercert()
- subject = cert['subject']
- altNames = cert['subjectAltName']
- self.assertEqual(dict(subject[0])['commonName'], 'tls.tests.dnsdist.org')
- self.assertEqual(dict(subject[1])['organizationalUnitName'], 'PowerDNS.com BV')
+ subject = cert["subject"]
+ altNames = cert["subjectAltName"]
+ self.assertEqual(dict(subject[0])["commonName"], "tls.tests.dnsdist.org")
+ self.assertEqual(dict(subject[1])["organizationalUnitName"], "PowerDNS.com BV")
names = []
for entry in altNames:
names.append(entry[1])
- self.assertEqual(names, ['tls.tests.dnsdist.org', 'powerdns.com', '127.0.0.1'])
+ self.assertEqual(names, ["tls.tests.dnsdist.org", "powerdns.com", "127.0.0.1"])
from pysnmp.hlapi import *
from dnsdisttests import DNSDistTest
+
class TestSNMP(DNSDistTest):
# wait 1s so that the uptime is > 0
_extraStartupSleep = 1
_snmpTimeout = 2.0
- _snmpServer = '127.0.0.1'
+ _snmpServer = "127.0.0.1"
_snmpPort = 161
- _snmpV2Community = 'secretcommunity'
- _snmpV3User = 'secretuser'
- _snmpV3AuthKey = 'mysecretauthkey'
- _snmpV3EncKey = 'mysecretenckey'
- _snmpOID = '1.3.6.1.4.1.43315.3'
+ _snmpV2Community = "secretcommunity"
+ _snmpV3User = "secretuser"
+ _snmpV3AuthKey = "mysecretauthkey"
+ _snmpV3EncKey = "mysecretenckey"
+ _snmpOID = "1.3.6.1.4.1.43315.3"
_queriesSent = 0
_config_template = """
newServer{address="127.0.0.1:%d", name="servername"}
_verboseMode = True
def _checkStatsValues(self, results, queriesCountersValue):
- for i in list(range(1, 5)) + list(range(6, 20)) + list(range(24, 35)) + [ 35 ] :
- oid = self._snmpOID + '.1.' + str(i) + '.0'
+ for i in list(range(1, 5)) + list(range(6, 20)) + list(range(24, 35)) + [35]:
+ oid = self._snmpOID + ".1." + str(i) + ".0"
self.assertIn(oid, results)
self.assertTrue(isinstance(results[oid], Counter64))
for i in range(20, 23):
- oid = self._snmpOID + '.1.' + str(i) + '.0'
+ oid = self._snmpOID + ".1." + str(i) + ".0"
self.assertTrue(isinstance(results[oid], OctetString))
# check uptime > 0
- self.assertGreater(results['1.3.6.1.4.1.43315.3.1.24.0'], 0)
+ self.assertGreater(results["1.3.6.1.4.1.43315.3.1.24.0"], 0)
# check memory usage > 0
- self.assertGreater(results['1.3.6.1.4.1.43315.3.1.25.0'], 0)
+ self.assertGreater(results["1.3.6.1.4.1.43315.3.1.25.0"], 0)
# check that the queries, responses and rdQueries counters are now at queriesCountersValue
for i in [1, 2, 28]:
- oid = self._snmpOID + '.1.' + str(i) + '.0'
+ oid = self._snmpOID + ".1." + str(i) + ".0"
self.assertEqual(results[oid], queriesCountersValue)
# the others counters (except for latency ones) should still be at 0
for i in [3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 26, 27, 29, 30, 31, 35, 36]:
- oid = self._snmpOID + '.1.' + str(i) + '.0'
+ oid = self._snmpOID + ".1." + str(i) + ".0"
self.assertEqual(results[oid], 0)
# check the backend stats
## types
for i in [3, 4, 5, 6, 7, 11, 12, 13]:
- oid = self._snmpOID + '.2.1.' + str(i) + '.0'
+ oid = self._snmpOID + ".2.1." + str(i) + ".0"
self.assertTrue(isinstance(results[oid], Counter64))
for i in [2, 8, 9, 10]:
- oid = self._snmpOID + '.2.1.' + str(i) + '.0'
+ oid = self._snmpOID + ".2.1." + str(i) + ".0"
self.assertTrue(isinstance(results[oid], OctetString))
## name
- self.assertEqual(str(results['1.3.6.1.4.1.43315.3.2.1.2.0']), "servername")
+ self.assertEqual(str(results["1.3.6.1.4.1.43315.3.2.1.2.0"]), "servername")
## weight
- self.assertEqual(results['1.3.6.1.4.1.43315.3.2.1.4.0'], 1)
+ self.assertEqual(results["1.3.6.1.4.1.43315.3.2.1.4.0"], 1)
## outstanding
- self.assertEqual(results['1.3.6.1.4.1.43315.3.2.1.5.0'], 0)
+ self.assertEqual(results["1.3.6.1.4.1.43315.3.2.1.5.0"], 0)
## qpslimit
- self.assertEqual(results['1.3.6.1.4.1.43315.3.2.1.6.0'], 0)
+ self.assertEqual(results["1.3.6.1.4.1.43315.3.2.1.6.0"], 0)
## reused
- self.assertEqual(results['1.3.6.1.4.1.43315.3.2.1.7.0'], 0)
+ self.assertEqual(results["1.3.6.1.4.1.43315.3.2.1.7.0"], 0)
## state
- self.assertEqual(str(results['1.3.6.1.4.1.43315.3.2.1.8.0']), "up")
+ self.assertEqual(str(results["1.3.6.1.4.1.43315.3.2.1.8.0"]), "up")
## address
- self.assertEqual(str(results['1.3.6.1.4.1.43315.3.2.1.9.0']), ("127.0.0.1:%d" % (self._testServerPort)))
+ self.assertEqual(str(results["1.3.6.1.4.1.43315.3.2.1.9.0"]), ("127.0.0.1:%d" % (self._testServerPort)))
## pools
- self.assertEqual(str(results['1.3.6.1.4.1.43315.3.2.1.10.0']), "")
+ self.assertEqual(str(results["1.3.6.1.4.1.43315.3.2.1.10.0"]), "")
## queries
- self.assertEqual(results['1.3.6.1.4.1.43315.3.2.1.12.0'], queriesCountersValue)
+ self.assertEqual(results["1.3.6.1.4.1.43315.3.2.1.12.0"], queriesCountersValue)
## order
- self.assertEqual(results['1.3.6.1.4.1.43315.3.2.1.13.0'], 1)
+ self.assertEqual(results["1.3.6.1.4.1.43315.3.2.1.13.0"], 1)
def _getSNMPStats(self, auth):
results = {}
- for (errorIndication, errorStatus, errorIndex, varBinds) in nextCmd(SnmpEngine(),
- auth,
- UdpTransportTarget((self._snmpServer, self._snmpPort), timeout=self._snmpTimeout),
- ContextData(),
- ObjectType(ObjectIdentity(self._snmpOID)),
- lookupMib=False):
+ for errorIndication, errorStatus, errorIndex, varBinds in nextCmd(
+ SnmpEngine(),
+ auth,
+ UdpTransportTarget((self._snmpServer, self._snmpPort), timeout=self._snmpTimeout),
+ ContextData(),
+ ObjectType(ObjectIdentity(self._snmpOID)),
+ lookupMib=False,
+ ):
self.assertFalse(errorIndication)
self.assertFalse(errorStatus)
self.assertTrue(varBinds)
results = self._getSNMPStats(auth)
self._checkStatsValues(results, self.__class__._queriesSent)
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
# send a query
"""
auth = CommunityData(self._snmpV2Community, mpModel=1)
- name = 'simplea.snmpv2c.tests.powerdns.com.'
+ name = "simplea.snmpv2c.tests.powerdns.com."
self._checkStats(auth, name)
def testSNMPv3Stats(self):
SNMP: Retrieve statistics via SNMPv3
"""
- auth = UsmUserData(self._snmpV3User,
- authKey=self._snmpV3AuthKey,
- privKey=self._snmpV3EncKey,
- authProtocol=usmHMACSHAAuthProtocol,
- privProtocol=usmAesCfb128Protocol)
- name = 'simplea.snmpv2.tests.powerdns.com.'
+ auth = UsmUserData(
+ self._snmpV3User,
+ authKey=self._snmpV3AuthKey,
+ privKey=self._snmpV3EncKey,
+ authProtocol=usmHMACSHAAuthProtocol,
+ privProtocol=usmAesCfb128Protocol,
+ )
+ name = "simplea.snmpv2.tests.powerdns.com."
self._checkStats(auth, name)
import dns
from dnsdisttests import DNSDistTest
-class TestSVCB(DNSDistTest):
+class TestSVCB(DNSDistTest):
_config_template = """
local basicSVC = { newSVCRecordParameters(1, "dot.powerdns.com.", { mandatory={"port"}, alpn={"dot"}, noDefaultAlpn=true, port=853, ipv4hint={ "192.0.2.1" }, ipv6hint={ "2001:db8::1" } }),
newSVCRecordParameters(2, "doh.powerdns.com.", { mandatory={"port"}, alpn={"h2"}, port=443, ipv4hint={ "192.0.2.2" }, ipv6hint={ "2001:db8::2" }, key7="/dns-query{?dns}" })
"""
SVCB: Basic service binding
"""
- name = 'basic.svcb.tests.powerdns.com.'
- query = dns.message.make_query(name, 64, 'IN')
+ name = "basic.svcb.tests.powerdns.com."
+ query = dns.message.make_query(name, 64, "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
self.assertEqual(len(receivedResponse.answer), 1)
self.assertEqual(receivedResponse.answer[0].rdtype, 64)
self.assertEqual(len(receivedResponse.additional), 4)
- self.assertEqual(receivedResponse.additional[0], dns.rrset.from_text("doh.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.2'))
- self.assertEqual(receivedResponse.additional[1], dns.rrset.from_text("dot.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'))
- self.assertEqual(receivedResponse.additional[2], dns.rrset.from_text("doh.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.AAAA, '2001:db8::2'))
- self.assertEqual(receivedResponse.additional[3], dns.rrset.from_text("dot.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.AAAA, '2001:db8::1'))
+ self.assertEqual(
+ receivedResponse.additional[0],
+ dns.rrset.from_text("doh.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2"),
+ )
+ self.assertEqual(
+ receivedResponse.additional[1],
+ dns.rrset.from_text("dot.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ )
+ self.assertEqual(
+ receivedResponse.additional[2],
+ dns.rrset.from_text("doh.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:db8::2"),
+ )
+ self.assertEqual(
+ receivedResponse.additional[3],
+ dns.rrset.from_text("dot.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:db8::1"),
+ )
def testNoHints(self):
"""
SVCB: No hints
"""
- name = 'no-hints.svcb.tests.powerdns.com.'
- query = dns.message.make_query(name, 64, 'IN')
+ name = "no-hints.svcb.tests.powerdns.com."
+ query = dns.message.make_query(name, 64, "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
"""
SVCB: Effective target
"""
- name = 'effective-target.svcb.tests.powerdns.com.'
- query = dns.message.make_query(name, 64, 'IN')
+ name = "effective-target.svcb.tests.powerdns.com."
+ query = dns.message.make_query(name, 64, "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
self.assertEqual(len(receivedResponse.answer), 1)
self.assertEqual(receivedResponse.answer[0].rdtype, 64)
self.assertEqual(len(receivedResponse.additional), 2)
- self.assertEqual(receivedResponse.additional[0], dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'))
- self.assertEqual(receivedResponse.additional[1], dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, '2001:db8::1'))
+ self.assertEqual(
+ receivedResponse.additional[0],
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ )
+ self.assertEqual(
+ receivedResponse.additional[1],
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:db8::1"),
+ )
def testHTTPS(self):
"""
SVCB: HTTPS
"""
- name = 'https.svcb.tests.powerdns.com.'
- query = dns.message.make_query(name, 65, 'IN')
+ name = "https.svcb.tests.powerdns.com."
+ query = dns.message.make_query(name, 65, "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
self.assertEqual(len(receivedResponse.answer), 1)
self.assertEqual(receivedResponse.answer[0].rdtype, 65)
self.assertEqual(len(receivedResponse.additional), 2)
- self.assertEqual(receivedResponse.additional[0], dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.2'))
- self.assertEqual(receivedResponse.additional[1], dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, '2001:db8::2'))
+ self.assertEqual(
+ receivedResponse.additional[0],
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2"),
+ )
+ self.assertEqual(
+ receivedResponse.additional[1],
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:db8::2"),
+ )
-class TestSVCBViaFFI(DNSDistTest):
+class TestSVCBViaFFI(DNSDistTest):
_config_template = """
local ffi = require("ffi")
"""
SVCB: Basic service binding
"""
- name = 'basic.svcb.tests.powerdns.com.'
- query = dns.message.make_query(name, 64, 'IN')
+ name = "basic.svcb.tests.powerdns.com."
+ query = dns.message.make_query(name, 64, "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
self.assertEqual(len(receivedResponse.answer), 1)
self.assertEqual(receivedResponse.answer[0].rdtype, 64)
self.assertEqual(len(receivedResponse.additional), 4)
- self.assertEqual(receivedResponse.additional[0], dns.rrset.from_text("doh.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.2'))
- self.assertEqual(receivedResponse.additional[1], dns.rrset.from_text("dot.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'))
- self.assertEqual(receivedResponse.additional[2], dns.rrset.from_text("doh.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.AAAA, '2001:db8::2'))
- self.assertEqual(receivedResponse.additional[3], dns.rrset.from_text("dot.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.AAAA, '2001:db8::1'))
+ self.assertEqual(
+ receivedResponse.additional[0],
+ dns.rrset.from_text("doh.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2"),
+ )
+ self.assertEqual(
+ receivedResponse.additional[1],
+ dns.rrset.from_text("dot.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ )
+ self.assertEqual(
+ receivedResponse.additional[2],
+ dns.rrset.from_text("doh.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:db8::2"),
+ )
+ self.assertEqual(
+ receivedResponse.additional[3],
+ dns.rrset.from_text("dot.powerdns.com.", 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:db8::1"),
+ )
def testNoHints(self):
"""
SVCB: No hints
"""
- name = 'no-hints.svcb.tests.powerdns.com.'
- query = dns.message.make_query(name, 64, 'IN')
+ name = "no-hints.svcb.tests.powerdns.com."
+ query = dns.message.make_query(name, 64, "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
"""
SVCB: Effective target
"""
- name = 'effective-target.svcb.tests.powerdns.com.'
- query = dns.message.make_query(name, 64, 'IN')
+ name = "effective-target.svcb.tests.powerdns.com."
+ query = dns.message.make_query(name, 64, "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
self.assertEqual(len(receivedResponse.answer), 1)
self.assertEqual(receivedResponse.answer[0].rdtype, 64)
self.assertEqual(len(receivedResponse.additional), 2)
- self.assertEqual(receivedResponse.additional[0], dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'))
- self.assertEqual(receivedResponse.additional[1], dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, '2001:db8::1'))
+ self.assertEqual(
+ receivedResponse.additional[0],
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ )
+ self.assertEqual(
+ receivedResponse.additional[1],
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:db8::1"),
+ )
def testHTTPS(self):
"""
SVCB: HTTPS
"""
- name = 'https.svcb.tests.powerdns.com.'
- query = dns.message.make_query(name, 65, 'IN')
+ name = "https.svcb.tests.powerdns.com."
+ query = dns.message.make_query(name, 65, "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
self.assertEqual(len(receivedResponse.answer), 1)
self.assertEqual(receivedResponse.answer[0].rdtype, 65)
self.assertEqual(len(receivedResponse.additional), 2)
- self.assertEqual(receivedResponse.additional[0], dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.2'))
- self.assertEqual(receivedResponse.additional[1], dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, '2001:db8::2'))
+ self.assertEqual(
+ receivedResponse.additional[0],
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2"),
+ )
+ self.assertEqual(
+ receivedResponse.additional[1],
+ dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:db8::2"),
+ )
import dns
from dnsdisttests import DNSDistTest
-class TestSelfAnsweredResponses(DNSDistTest):
+class TestSelfAnsweredResponses(DNSDistTest):
_config_template = """
-- this is a silly test config, please do not do this in production.
addAction(SuffixMatchNodeRule("udp.selfanswered.tests.powerdns.com."), SpoofAction("192.0.2.1"))
SelfAnsweredResponses: Drop when served from the cache
"""
ttl = 60
- name = 'udp.selfanswered.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.selfanswered.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
response.flags |= dns.flags.RA
SelfAnsweredResponses: TCP: Drop after exceeding QPS
"""
ttl = 60
- name = 'tcp.selfanswered.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tcp.selfanswered.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- ttl,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, ttl, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
response.flags |= dns.flags.RA
import dns
from dnsdisttests import DNSDistTest
-class TestSize(DNSDistTest):
+class TestSize(DNSDistTest):
_payloadSize = 49
_config_template = """
addAction(PayloadSizeRule("smaller", %d), SpoofAction("192.0.2.1"))
addAction(PayloadSizeRule("equal", %d), SpoofAction("192.0.2.3"))
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_payloadSize', '_payloadSize', '_payloadSize', '_testServerPort']
+ _config_params = ["_payloadSize", "_payloadSize", "_payloadSize", "_testServerPort"]
def testPayloadSize(self):
"""
Size: Check that PayloadSizeRule works
"""
- name = 'payload.size.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "payload.size.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.3')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.3")
expectedResponse.answer.append(rrset)
self.assertEqual(len(query.to_wire()), self._payloadSize)
self.assertTrue(receivedResponse)
self.assertEqual(receivedResponse, expectedResponse)
-class TestTruncateOversizedResponse(DNSDistTest):
+class TestTruncateOversizedResponse(DNSDistTest):
_payloadSize = 512
_config_template = """
addResponseAction(PayloadSizeRule("greater", %d), TCResponseAction())
newServer{address="127.0.0.1:%d"}
"""
- _config_params = ['_payloadSize', '_testServerPort']
+ _config_params = ["_payloadSize", "_testServerPort"]
def testTruncateOversizedResponse(self):
"""
Size: Truncate oversized response
"""
- name = 'truncate-oversized.size.tests.powerdns.com.'
- query = dns.message.make_query(name, 'TXT', 'IN')
+ name = "truncate-oversized.size.tests.powerdns.com."
+ query = dns.message.make_query(name, "TXT", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags |= dns.flags.TC
backendResponse = dns.message.make_response(query)
- content = ''
+ content = ""
for i in range(2):
if len(content) > 0:
- content = content + ' '
- content = content + 'A' * 255
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- content)
+ content = content + " "
+ content = content + "A" * 255
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, content)
backendResponse.answer.append(rrset)
self.assertGreater(len(backendResponse.to_wire()), self._payloadSize)
import dns
from dnsdisttests import DNSDistTest
-class SpoofingTests(object):
+class SpoofingTests(object):
def testSpoofActionA(self):
"""
Spoofing: Spoof A via Action
Send an A query to "spoofaction.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'spoofaction.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "spoofaction.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an A query to "spoofaction.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'spoofaction.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ name = "spoofaction.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=1232)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an AAAA query to "spoofaction.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'spoofaction.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "spoofaction.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an A query for "cnamespoofaction.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'cnamespoofaction.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "cnamespoofaction.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'cnameaction.spoofing.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name, 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "cnameaction.spoofing.tests.powerdns.com."
+ )
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an A query for "multispoof.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'multispoof.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "multispoof.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.2', '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2", "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an AAAA query for "multispoof.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'multispoof.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "multispoof.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1', '2001:DB8::2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1", "2001:DB8::2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an ANY query for "multispoof.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'multispoof.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'ANY', 'IN')
+ name = "multispoof.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "ANY", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.2', '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2", "192.0.2.1")
expectedResponse.answer.append(rrset)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1', '2001:DB8::2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1", "2001:DB8::2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Spoofing: Spoof via Action, setting AA=1
"""
- name = 'spoofaction-aa.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "spoofaction-aa.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags |= dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Spoofing: Spoof via Action, setting AD=1
"""
- name = 'spoofaction-ad.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "spoofaction-ad.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags |= dns.flags.AD
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Spoofing: Spoof via Action, setting RA=1
"""
- name = 'spoofaction-ra.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "spoofaction-ra.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags |= dns.flags.RA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Spoofing: Spoof via Action, setting RA=0
"""
- name = 'spoofaction-nora.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "spoofaction-nora.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.RA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Spoofing: Spoof via Action, setting the TTL to 1500
"""
- name = 'spoofaction-ttl.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "spoofaction-ttl.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.flags |= dns.flags.RA
- rrset = dns.rrset.from_text(name,
- 1500,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 1500, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Spoofing: Spoof a response from raw bytes
"""
- name = 'raw.spoofing.tests.powerdns.com.'
+ name = "raw.spoofing.tests.powerdns.com."
# A
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedResponse.answer[0].ttl, 60)
# A with EDNS
- query = dns.message.make_query(name, 'A', 'IN', use_edns=True)
+ query = dns.message.make_query(name, "A", "IN", use_edns=True)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.use_edns(edns=True, payload=1232)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedResponse.answer[0].ttl, 60)
# TXT
- query = dns.message.make_query(name, 'TXT', 'IN')
+ query = dns.message.make_query(name, "TXT", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- '"aaa" "bbbb" "ccccccccccc"')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.TXT, '"aaa" "bbbb" "ccccccccccc"')
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedResponse.answer[0].ttl, 60)
# SRV
- query = dns.message.make_query(name, 'SRV', 'IN')
+ query = dns.message.make_query(name, "SRV", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
# this one should have the AA flag set
expectedResponse.flags |= dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.SRV,
- '0 0 65535 srv.powerdns.com.')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.SRV, "0 0 65535 srv.powerdns.com.")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Spoofing: Spoof a response from several raw bytes in QCLass CH
"""
- name = 'rawchaos.spoofing.tests.powerdns.com.'
+ name = "rawchaos.spoofing.tests.powerdns.com."
# TXT CH
- query = dns.message.make_query(name, 'TXT', 'CH')
+ query = dns.message.make_query(name, "TXT", "CH")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.CH,
- dns.rdatatype.TXT,
- '"chaos\\\\test"')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.CH, dns.rdatatype.TXT, '"chaos\\\\test"')
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Spoofing: Spoof a HINFO response for ANY queries
"""
- name = 'raw-any.spoofing.tests.powerdns.com.'
+ name = "raw-any.spoofing.tests.powerdns.com."
- query = dns.message.make_query(name, 'ANY', 'IN')
+ query = dns.message.make_query(name, "ANY", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.HINFO,
- '"rfc8482" ""')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.HINFO, '"rfc8482" ""')
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Spoofing: Spoof a response from several raw bytes
"""
- name = 'multiraw.spoofing.tests.powerdns.com.'
+ name = "multiraw.spoofing.tests.powerdns.com."
# A
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1', '192.0.2.2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedResponse.answer[0].ttl, 60)
# TXT
- query = dns.message.make_query(name, 'TXT', 'IN')
+ query = dns.message.make_query(name, "TXT", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- '"aaa" "bbbb"', '"ccccccccccc"')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.TXT, '"aaa" "bbbb"', '"ccccccccccc"')
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
self.assertEqual(receivedResponse.answer[0].ttl, 60)
-class TestSpoofingViaLuaConfig(DNSDistTest, SpoofingTests):
+class TestSpoofingViaLuaConfig(DNSDistTest, SpoofingTests):
_config_template = """
addAction(SuffixMatchNodeRule("spoofaction.spoofing.tests.powerdns.com."), SpoofAction({"192.0.2.1", "2001:DB8::1"}))
addAction(SuffixMatchNodeRule("spoofaction-aa.spoofing.tests.powerdns.com."), SpoofAction({"192.0.2.1", "2001:DB8::1"}, {aa=true}))
newServer{address="127.0.0.1:%d"}
"""
-class TestSpoofingViaYamlConfig(DNSDistTest, SpoofingTests):
+class TestSpoofingViaYamlConfig(DNSDistTest, SpoofingTests):
_yaml_config_template = """
backends:
- address: "127.0.0.1:%d"
vars:
ttl: 60
"""
- _yaml_config_params = ['_testServerPort']
+ _yaml_config_params = ["_testServerPort"]
_config_params = []
-class TestSpoofingLuaSpoof(DNSDistTest):
+class TestSpoofingLuaSpoof(DNSDistTest):
_config_template = """
function spoof1rule(dq)
if(dq.qtype==1) -- A
Send an A query to "luaspoof1.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'luaspoof1.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "luaspoof1.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1', '192.0.2.2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an AAAA query to "luaspoof1.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'luaspoof1.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "luaspoof1.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an A query to "luaspoof2.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'luaspoof2.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "luaspoof2.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'spoofedcname.spoofing.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name, 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "spoofedcname.spoofing.tests.powerdns.com."
+ )
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an AAAA query to "luaspoof2.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'luaspoof2.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "luaspoof2.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'spoofedcname.spoofing.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name, 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "spoofedcname.spoofing.tests.powerdns.com."
+ )
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Spoofing: Spoof a response from raw bytes via Lua
"""
- name = 'lua-raw.spoofing.tests.powerdns.com.'
+ name = "lua-raw.spoofing.tests.powerdns.com."
# A
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedResponse.answer[0].ttl, 60)
# TXT
- query = dns.message.make_query(name, 'TXT', 'IN')
+ query = dns.message.make_query(name, "TXT", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- '"aaa" "bbbb" "ccccccccccc"')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.TXT, '"aaa" "bbbb" "ccccccccccc"')
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedResponse.answer[0].ttl, 60)
# SRV
- query = dns.message.make_query(name, 'SRV', 'IN')
+ query = dns.message.make_query(name, "SRV", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
# this one should have the AA flag set
expectedResponse.flags |= dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.SRV,
- '0 0 65535 srv.powerdns.com.')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.SRV, "0 0 65535 srv.powerdns.com.")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(receivedResponse)
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
# sorry, we can't set the TTL from the Lua API right now
- #self.assertEqual(receivedResponse.answer[0].ttl, 3600)
+ # self.assertEqual(receivedResponse.answer[0].ttl, 3600)
-class TestSpoofingLuaSpoofMulti(DNSDistTest):
+class TestSpoofingLuaSpoofMulti(DNSDistTest):
_config_template = """
function spoof1multirule(dq)
if(dq.qtype==1) -- A
Send an A query to "luaspoof1multi.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'luaspoof1multi.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "luaspoof1multi.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1', '192.0.2.2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
Send an AAAA query to "luaspoof1.spoofing.tests.powerdns.com.",
check that dnsdist sends a spoofed result.
"""
- name = 'luaspoof1multi.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "luaspoof1multi.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1', '2001:DB8::2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1", "2001:DB8::2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Spoofing: Spoof responses from raw bytes via Lua dq:spoof
"""
- name = 'lua-raw-multi.spoofing.tests.powerdns.com.'
+ name = "lua-raw-multi.spoofing.tests.powerdns.com."
# A
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1', '192.0.2.2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.2")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedResponse.answer[0].ttl, 60)
# TXT
- query = dns.message.make_query(name, 'TXT', 'IN')
+ query = dns.message.make_query(name, "TXT", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- '"aaa" "bbbb"', '"ccccccccccc"')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.TXT, '"aaa" "bbbb"', '"ccccccccccc"')
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedResponse.answer[0].ttl, 60)
# SRV
- query = dns.message.make_query(name, 'SRV', 'IN')
+ query = dns.message.make_query(name, "SRV", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
# this one should have the AA flag set
expectedResponse.flags |= dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.SRV,
- '0 0 65535 srv1.powerdns.com.', '0 0 65535 srv2.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.SRV,
+ "0 0 65535 srv1.powerdns.com.",
+ "0 0 65535 srv2.powerdns.com.",
+ )
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(receivedResponse)
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
# sorry, we can't set the TTL from the Lua API right now
- #self.assertEqual(receivedResponse.answer[0].ttl, 3600)
+ # self.assertEqual(receivedResponse.answer[0].ttl, 3600)
-class TestSpoofingLuaFFISpoofMulti(DNSDistTest):
+class TestSpoofingLuaFFISpoofMulti(DNSDistTest):
_config_template = """
local ffi = require("ffi")
"""
Spoofing via Lua FFI: Spoof responses from raw bytes via Lua FFI
"""
- name = 'lua-raw-multi.ffi-spoofing.tests.powerdns.com.'
+ name = "lua-raw-multi.ffi-spoofing.tests.powerdns.com."
# A
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1', '192.0.2.255')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.255")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(receivedResponse.answer[0].ttl, 60)
# TXT
- query = dns.message.make_query(name, 'TXT', 'IN')
+ query = dns.message.make_query(name, "TXT", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.flags &= ~dns.flags.AA
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- '"this text has a comma at the end,"', '"aaa" "bbbb"')
+ rrset = dns.rrset.from_text(
+ name, 60, dns.rdataclass.IN, dns.rdatatype.TXT, '"this text has a comma at the end,"', '"aaa" "bbbb"'
+ )
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.checkMessageNoEDNS(expectedResponse, receivedResponse)
self.assertEqual(receivedResponse.answer[0].ttl, 60)
-class TestSpoofingLuaWithStatistics(DNSDistTest):
+class TestSpoofingLuaWithStatistics(DNSDistTest):
_config_template = """
function spoof1rule(dq)
queriesCount = getStatisticsCounters()['queries']
Spoofing: Spoofing an A via Lua based on statistics counters
"""
- name = 'luaspoofwithstats.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "luaspoofwithstats.spoofing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse1 = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse1.answer.append(rrset)
expectedResponse2 = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.2')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2")
expectedResponse2.answer.append(rrset)
expectedResponseAfterwards = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.0')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.0")
expectedResponseAfterwards.answer.append(rrset)
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertTrue(receivedResponse)
self.assertEqual(expectedResponseAfterwards, receivedResponse)
-class TestSpoofingLuaSpoofPacket(DNSDistTest):
+class TestSpoofingLuaSpoofPacket(DNSDistTest):
_config_template = """
function spoofpacket(dq)
"""
Spoofing via Lua FFI: Spoof raw response via Lua
"""
- for name in ('lua-raw-packet.spoofing.tests.powerdns.com.', 'rule-lua-raw-packet.spoofing.tests.powerdns.com.'):
-
- query = dns.message.make_query(name, 'A', 'IN')
+ for name in ("lua-raw-packet.spoofing.tests.powerdns.com.", "rule-lua-raw-packet.spoofing.tests.powerdns.com."):
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.flags |= dns.flags.RA
expectedResponse.set_rcode(dns.rcode.REFUSED)
- if name == 'rule-lua-raw-packet.spoofing.tests.powerdns.com.':
- nsid_opt = dns.edns.GenericOption(dns.edns.NSID, 'dnsdist-1'.encode())
+ if name == "rule-lua-raw-packet.spoofing.tests.powerdns.com.":
+ nsid_opt = dns.edns.GenericOption(dns.edns.NSID, "dnsdist-1".encode())
expectedResponse.use_edns(options=[nsid_opt])
for method in ("sendUDPQuery", "sendTCPQuery"):
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertTrue(receivedResponse)
self.assertEqual(expectedResponse, receivedResponse)
- if name == 'rule-lua-raw-packet.spoofing.tests.powerdns.com.':
+ if name == "rule-lua-raw-packet.spoofing.tests.powerdns.com.":
self.checkMessageEDNS(expectedResponse, receivedResponse)
def testLuaFFISpoofPacket(self):
"""
Spoofing via Lua FFI: Spoof raw response via Lua FFI
"""
- name = 'lua-raw-packet.ffi-spoofing.tests.powerdns.com.'
+ name = "lua-raw-packet.ffi-spoofing.tests.powerdns.com."
#
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.flags |= dns.flags.RA
expectedResponse.set_rcode(dns.rcode.REFUSED)
#!/usr/bin/env python
from dnsdisttests import DNSDistTest, pickAvailablePort
-class TestStructuredLoggingDefaultBackendFromYaml(DNSDistTest):
+class TestStructuredLoggingDefaultBackendFromYaml(DNSDistTest):
_yaml_config_template = """---
binds:
- listen_address: "127.0.0.1:%d"
"""
_dnsDistPort = pickAvailablePort()
_testServerPort = pickAvailablePort()
- _yaml_config_params = ['_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort"]
_config_params = []
_checkConfigExpectedOutput = None
_checkConfigExpectedOutputPrefix = b'msg="Configuration OK" subsystem="setup"'
def testOK(self):
pass
-class TestStructuredLoggingJSONBackendFromYaml(DNSDistTest):
+class TestStructuredLoggingJSONBackendFromYaml(DNSDistTest):
_yaml_config_template = """---
binds:
- listen_address: "127.0.0.1:%d"
"""
_dnsDistPort = pickAvailablePort()
_testServerPort = pickAvailablePort()
- _yaml_config_params = ['_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort"]
_config_params = []
_checkConfigExpectedOutput = None
_checkConfigExpectedOutputPrefix = b'{"level": "0", "msg": "Configuration OK", "path":'
def testOK(self):
pass
-class TestStructuredLoggingDefaultBackendFromLua(DNSDistTest):
+class TestStructuredLoggingDefaultBackendFromLua(DNSDistTest):
_config_template = """
setStructuredLogging(true)
def testOK(self):
pass
-class TestStructuredLoggingJSONBackendFromLua(DNSDistTest):
+class TestStructuredLoggingJSONBackendFromLua(DNSDistTest):
_config_template = """
setStructuredLogging(true, {backend="json"})
pass
-class TestStructuredLoggingDefaultBackendWithInstanceFromYaml(
- TestStructuredLoggingDefaultBackendFromYaml
-):
+class TestStructuredLoggingDefaultBackendWithInstanceFromYaml(TestStructuredLoggingDefaultBackendFromYaml):
_yaml_config_template = """---
general:
server_id: "foobar"
_checkConfigExpectedOutputPrefix = b'msg="Configuration OK" subsystem="setup"'
-class TestStructuredLoggingJSONBackendWithInstanceFromYaml(
- TestStructuredLoggingJSONBackendFromYaml
-):
+class TestStructuredLoggingJSONBackendWithInstanceFromYaml(TestStructuredLoggingJSONBackendFromYaml):
_yaml_config_template = """---
general:
server_id: "foobar"
backend: "json"
set_instance_from_server_id: true
"""
- _checkConfigExpectedOutputPrefix = (
- b'{"instance": "foobar", "level": "0", "msg": "Configuration OK", "path":'
- )
+ _checkConfigExpectedOutputPrefix = b'{"instance": "foobar", "level": "0", "msg": "Configuration OK", "path":'
-class TestStructuredLoggingDefaultBackendWithInstanceFromLua(
- TestStructuredLoggingDefaultBackendFromLua
-):
+class TestStructuredLoggingDefaultBackendWithInstanceFromLua(TestStructuredLoggingDefaultBackendFromLua):
_config_template = """
setServerID("foobar")
setStructuredLogging(true, {setInstanceFromServerID=true})
newServer{address="127.0.0.1:%d"}
"""
- _checkConfigExpectedOutputPrefix = b'msg="Configuration OK" subsystem="setup" level="0" prio="Info" instance="foobar" ts='
-
+ _checkConfigExpectedOutputPrefix = (
+ b'msg="Configuration OK" subsystem="setup" level="0" prio="Info" instance="foobar" ts='
+ )
-class TestStructuredLoggingJSONBackendWithInstanceFromLua(
- TestStructuredLoggingJSONBackendFromLua
-):
+class TestStructuredLoggingJSONBackendWithInstanceFromLua(TestStructuredLoggingJSONBackendFromLua):
_config_template = """
setServerID("foobar")
setStructuredLogging(true, {backend="json", setInstanceFromServerID=true})
newServer{address="127.0.0.1:%d"}
"""
- _checkConfigExpectedOutputPrefix = (
- b'{"instance": "foobar", "level": "0", "msg": "Configuration OK", "path":'
- )
+ _checkConfigExpectedOutputPrefix = b'{"instance": "foobar", "level": "0", "msg": "Configuration OK", "path":'
import struct
from dnsdisttests import DNSDistTest, pickAvailablePort
-class TestBrokenTCPFastOpen(DNSDistTest):
+class TestBrokenTCPFastOpen(DNSDistTest):
# this test suite uses a different responder port
# because, contrary to the other ones, its
# TCP responder will accept a connection, read the
_testServerRetries = 5
_webTimeout = 2.0
_webServerPort = pickAvailablePort()
- _webServerBasicAuthPassword = 'secret'
- _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
- _webServerAPIKey = 'apisecret'
- _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
- _config_params = ['_testServerPort', '_testServerRetries', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
+ _webServerBasicAuthPassword = "secret"
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKey = "apisecret"
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
+ _config_params = [
+ "_testServerPort",
+ "_testServerRetries",
+ "_webServerPort",
+ "_webServerBasicAuthPasswordHashed",
+ "_webServerAPIKeyHashed",
+ ]
_config_template = """
newServer{address="127.0.0.1:%d", useClientSubnet=true, tcpFastOpen=true, retries=%d }
webserver("127.0.0.1:%d")
print("Launching responders..")
# Normal responder
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
# Close the connection right after reading the query
- cls._TCPResponder = threading.Thread(name='Broken TCP Responder', target=cls.BrokenTCPResponder, args=[cls._testServerPort])
+ cls._TCPResponder = threading.Thread(
+ name="Broken TCP Responder", target=cls.BrokenTCPResponder, args=[cls._testServerPort]
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
"""
TCP Fast Open: Close after read
"""
- name = 'close-after-read.tfo.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "close-after-read.tfo.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertFalse(receivedQuery)
self.assertFalse(receivedResponse)
- headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
+ headers = {"x-api-key": self._webServerAPIKey}
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertTrue(len(content['servers']), 1)
- server = content['servers'][0]
- self.assertIn('tcpDiedReadingResponse', server)
- self.assertEqual(server['tcpDiedReadingResponse'], self._testServerRetries)
+ self.assertTrue(len(content["servers"]), 1)
+ server = content["servers"][0]
+ self.assertIn("tcpDiedReadingResponse", server)
+ self.assertEqual(server["tcpDiedReadingResponse"], self._testServerRetries)
except NameError:
pass
+
class TestTCPKeepAlive(DNSDistTest):
"""
These tests make sure that dnsdist keeps the TCP connection alive
addAction("nodownstream-servfail.tcpka.tests.powerdns.com.", PoolAction("nosuchpool"))
setServFailWhenNoServer(true)
"""
- _config_params = ['_testServerPort', '_tcpIdleTimeout', '_maxTCPQueriesPerConn', '_maxTCPConnsPerClient', '_maxTCPConnDuration']
+ _config_params = [
+ "_testServerPort",
+ "_tcpIdleTimeout",
+ "_maxTCPQueriesPerConn",
+ "_maxTCPConnsPerClient",
+ "_maxTCPConnDuration",
+ ]
def testTCPKaSelfGenerated(self):
"""
TCP KeepAlive: Self-generated answer
"""
- name = 'refused.tcpka.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "refused.tcpka.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
"""
TCP KeepAlive: Cache Hit
"""
- name = 'cachehit.tcpka.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "cachehit.tcpka.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
# first query to fill the cache
and dnsdist is configured to send a ServFail when
that happens. We should keep the TCP connection open.
"""
- name = 'nodownstream-servfail.tcpka.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nodownstream-servfail.tcpka.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.SERVFAIL)
"""
TCP KeepAlive: QR bit set in question
"""
- name = 'qrset.tcpka.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "qrset.tcpka.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags |= dns.flags.QR
conn = self.openTCPConnection()
"""
TCP KeepAlive: Drop
"""
- name = 'dropped.tcpka.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "dropped.tcpka.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags |= dns.flags.QR
conn = self.openTCPConnection()
"""
TCP KeepAlive: Drop Response
"""
- name = 'dropped-response.tcpka.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "dropped-response.tcpka.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
conn = self.openTCPConnection()
"""
TCP KeepAlive: Large number of connections
"""
- name = 'largernumberofconnections.tcpka.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "largernumberofconnections.tcpka.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- #expectedResponse.set_rcode(dns.rcode.SERVFAIL)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ # expectedResponse.set_rcode(dns.rcode.SERVFAIL)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
# number of connections
pass
for conn in conns:
- conn.close()
+ conn.close()
self.assertEqual(count, numConns * numQueriesPerConn)
+
class TestTCPKeepAliveNoDownstreamDrop(DNSDistTest):
"""
This test makes sure that dnsdist drops the TCP connection
getPool("nosuchpool")
addAction("nodownstream-drop.tcpka.tests.powerdns.com.", PoolAction("nosuchpool"))
"""
- _config_params = ['_testServerPort', '_tcpIdleTimeout', '_maxTCPQueriesPerConn', '_maxTCPConnsPerClient', '_maxTCPConnDuration']
+ _config_params = [
+ "_testServerPort",
+ "_tcpIdleTimeout",
+ "_maxTCPQueriesPerConn",
+ "_maxTCPConnsPerClient",
+ "_maxTCPConnDuration",
+ ]
def testTCPKaNoDownstreamDrop(self):
"""
and dnsdist is configured to drop the query when
that happens. We should close the TCP connection right away.
"""
- name = 'nodownstream-drop.tcpka.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nodownstream-drop.tcpka.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
conn = self.openTCPConnection()
from dnsdisttests import DNSDistTest, pickAvailablePort
try:
- range = xrange
+ range = xrange
except NameError:
- pass
+ pass
-class TestTCPLimits(DNSDistTest):
+class TestTCPLimits(DNSDistTest):
# this test suite uses a different responder port
# because it uses a different health check configuration
_testServerPort = pickAvailablePort()
-- test gets us banned very quickly
setMaxTCPReadIOsPerQuery(0)
"""
- _config_params = ['_testServerPort', '_tcpIdleTimeout', '_maxTCPQueriesPerConn', '_maxTCPConnsPerClient', '_maxTCPConnDuration']
+ _config_params = [
+ "_testServerPort",
+ "_tcpIdleTimeout",
+ "_maxTCPQueriesPerConn",
+ "_maxTCPConnsPerClient",
+ "_maxTCPConnDuration",
+ ]
def testTCPQueriesPerConn(self):
"""
TCP Limits: Maximum number of queries
"""
- name = 'maxqueriesperconn.tcp.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "maxqueriesperconn.tcp.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
conn = self.openTCPConnection()
count = 0
"""
TCP Limits: Maximum number of conns per client
"""
- name = 'maxconnsperclient.tcp.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "maxconnsperclient.tcp.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
conns = []
for idx in range(self._maxTCPConnsPerClient + 1):
# sleeping for only one second keeps us below the
# idle timeout (setTCPRecvTimeout())
time.sleep(0.1)
- conn.send(b'A')
+ conn.send(b"A")
count = count + 1
except Exception as e:
print("Exception: %s!" % (e))
conn.close()
-class TestTCPLimitsReadIO(DNSDistTest):
+class TestTCPLimitsReadIO(DNSDistTest):
# separate test suite because we get banned for a few seconds
_testServerPort = pickAvailablePort()
_answerUnexpected = True
-- disable "near limits" otherwise our tests are broken because connections are forcibly closed
setTCPConnectionsOverloadThreshold(0)
"""
- _config_params = ['_testServerPort', '_tcpIdleTimeout', '_maxTCPReadIOsPerQuery', '_banDuration']
+ _config_params = ["_testServerPort", "_tcpIdleTimeout", "_maxTCPReadIOsPerQuery", "_banDuration"]
def testTCPMaxReadIOsPerQuery(self):
"""
TCP Limits: Maximum number of IO read events per query
"""
- name = 'maxreadios.tcp.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "maxreadios.tcp.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
payload = query.to_wire()
self.assertGreater(len(payload), self._maxTCPReadIOsPerQuery)
try:
response = self.recvTCPResponseOverConnection(conn)
if not response:
- failed = True
+ failed = True
except Exception:
failed = True
conn = self.openTCPConnection()
response = self.recvTCPResponseOverConnection(conn)
if response is None:
- failed = True
+ failed = True
except Exception:
failed = True
finally:
self.assertTrue(failed)
-class TestTCPLimitsConnectionRate(DNSDistTest):
+class TestTCPLimitsConnectionRate(DNSDistTest):
# separate test suite because we get banned for a few seconds
_testServerPort = pickAvailablePort()
_answerUnexpected = True
-- disable "near limits" otherwise our tests are broken because connections are forcibly closed
setTCPConnectionsOverloadThreshold(0)
"""
- _config_params = ['_testServerPort', '_tcpIdleTimeout', '_maxConnectionRate', '_banDuration']
+ _config_params = ["_testServerPort", "_tcpIdleTimeout", "_maxConnectionRate", "_banDuration"]
_verboseMode = True
def testTCPConnectionRate(self):
"""
TCP Limits: Maximum connection rate
"""
- name = 'maxconnectionrate.tcp.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "maxconnectionrate.tcp.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
# _maxConnectionRate connections in a row
self.assertEqual(receivedQuery, None)
self.assertEqual(receivedResponse, None)
+
class TestTCPLimitsTLSNewSessionRate(DNSDistTest):
# separate test suite because we get banned for a few seconds
_testServerPort = pickAvailablePort()
_maxNewTLSSessionRate = 10
_tcpIdleTimeout = 2
_banDuration = 2
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_config_template = """
newServer{address="127.0.0.1:%d"}
setTCPRecvTimeout(%d)
-- disable "near limits" otherwise our tests are broken because connections are forcibly closed
setTCPConnectionsOverloadThreshold(0)
"""
- _config_params = ['_testServerPort', '_tcpIdleTimeout', '_maxNewTLSSessionRate', '_banDuration', '_tlsServerPort', '_serverCert', '_serverKey']
+ _config_params = [
+ "_testServerPort",
+ "_tcpIdleTimeout",
+ "_maxNewTLSSessionRate",
+ "_banDuration",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
_verboseMode = True
def testTLSNewSessionRate(self):
"""
TCP Limits: Maximum TLS new session rate
"""
- name = 'maxtlsnewsessionrate.tcp.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "maxtlsnewsessionrate.tcp.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
# _maxNewTLSSessionRate connections in a row, plus one because
self.sendDOTQueryWrapper(query, response=None, useQueue=False)
self.fail()
except ConnectionResetError:
- pass
+ pass
+
class TestTCPLimitsTLSResumedSessionRate(DNSDistTest):
# separate test suite because we get banned for a few seconds
_maxResumedTLSSessionRate = 10
_tcpIdleTimeout = 2
_banDuration = 2
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_config_template = """
newServer{address="127.0.0.1:%d"}
setTCPRecvTimeout(%d)
-- disable "near limits" otherwise our tests are broken because connections are forcibly closed
setTCPConnectionsOverloadThreshold(0)
"""
- _config_params = ['_testServerPort', '_tcpIdleTimeout', '_maxNewTLSSessionRate', '_maxResumedTLSSessionRate', '_banDuration', '_tlsServerPort', '_serverCert', '_serverKey']
+ _config_params = [
+ "_testServerPort",
+ "_tcpIdleTimeout",
+ "_maxNewTLSSessionRate",
+ "_maxResumedTLSSessionRate",
+ "_banDuration",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
_verboseMode = True
def testTLSResumedSessionRate(self):
"""
TCP Limits: Maximum TLS resumed session rate
"""
- name = 'maxtlsresumedsessionrate.tcp.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "maxtlsresumedsessionrate.tcp.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
session = None
# - the first one is a new TLS session
# - the session is only accounted for once the handshake has been completed
for idx in range(self._maxResumedTLSSessionRate + 2):
- conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert, timeout=1, sslctx=sslctx, session=session)
+ conn = self.openTLSConnection(
+ self._tlsServerPort, self._serverName, self._caCert, timeout=1, sslctx=sslctx, session=session
+ )
self.sendTCPQueryOverConnection(conn, query, response=response, timeout=1)
(receivedQuery, receivedResponse) = self.recvTCPResponseOverConnection(conn, useQueue=True, timeout=1)
receivedQuery.id = query.id
try:
# the next one should be past the max rate
- conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert, timeout=1, sslctx=sslctx, session=session)
+ conn = self.openTLSConnection(
+ self._tlsServerPort, self._serverName, self._caCert, timeout=1, sslctx=sslctx, session=session
+ )
self.sendTCPQueryOverConnection(conn, query, response=response, timeout=1)
self.recvTCPResponseOverConnection(conn, useQueue=True, timeout=1)
self.fail()
except ConnectionResetError:
- pass
+ pass
-class TestTCPFrontendLimits(DNSDistTest):
+class TestTCPFrontendLimits(DNSDistTest):
# this test suite uses a different responder port
# because it uses a different health check configuration
_testServerPort = pickAvailablePort()
-- disable "near limits" otherwise our tests are broken because connections are forcibly closed
setTCPConnectionsOverloadThreshold(0)
"""
- _config_params = ['_testServerPort', '_dnsDistListeningAddr', '_dnsDistPort', '_maxTCPConnsPerFrontend']
+ _config_params = ["_testServerPort", "_dnsDistListeningAddr", "_dnsDistPort", "_maxTCPConnsPerFrontend"]
def testTCPConnsPerFrontend(self):
"""
TCP Frontend Limits: Maximum number of conns per frontend
"""
- name = 'maxconnsperfrontend.tcp.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "maxconnsperfrontend.tcp.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
conns = []
for idx in range(self._maxTCPConnsPerFrontend + 1):
from dnsdisttests import DNSDistTest
-class TestTCPOnly(DNSDistTest):
+class TestTCPOnly(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d", tcpOnly=true}
"""
"""
TCP Only: UDP query is sent via TCP
"""
- name = 'udp.tcp-only.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.tcp-only.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, expectedResponse)
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, expectedResponse)
- if 'UDP Responder' in self._responsesCounter:
- self.assertEqual(self._responsesCounter['UDP Responder'], 0)
- if 'TCP Responder' in self._responsesCounter:
- self.assertEqual(self._responsesCounter['TCP Responder'], 1)
+ if "UDP Responder" in self._responsesCounter:
+ self.assertEqual(self._responsesCounter["UDP Responder"], 0)
+ if "TCP Responder" in self._responsesCounter:
+ self.assertEqual(self._responsesCounter["TCP Responder"], 1)
def testTCP(self):
"""
TCP Only: TCP query is sent via TCP
"""
- name = 'tcp.tcp-only.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tcp.tcp-only.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, expectedResponse)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
self.assertEqual(receivedResponse, expectedResponse)
- if 'UDP Responder' in self._responsesCounter:
- self.assertEqual(self._responsesCounter['UDP Responder'], 0)
- if 'TCP Responder' in self._responsesCounter:
- self.assertEqual(self._responsesCounter['TCP Responder'], 1)
+ if "UDP Responder" in self._responsesCounter:
+ self.assertEqual(self._responsesCounter["UDP Responder"], 0)
+ if "TCP Responder" in self._responsesCounter:
+ self.assertEqual(self._responsesCounter["TCP Responder"], 1)
from dnsdisttests import DNSDistTest, pickAvailablePort
try:
- range = xrange
+ range = xrange
except NameError:
- pass
+ pass
+
class TestTCPShort(DNSDistTest):
# this test suite uses a different responder port
# responders allow trailing data and multiple responses,
# and we don't want to mix things up.
_testServerPort = pickAvailablePort()
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_tcpSendTimeout = 60
_config_template = """
addTLSLocal("127.0.0.1:%d", "%s", "%s")
setTCPSendTimeout(%d)
"""
- _config_params = ['_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_tcpSendTimeout']
+ _config_params = ["_testServerPort", "_tlsServerPort", "_serverCert", "_serverKey", "_tcpSendTimeout"]
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, True])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, True],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, True, True])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, True, True],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
"""
TCP: Short read from client
"""
- name = 'short-read.tcp-short.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "short-read.tcp-short.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
conn = self.openTCPConnection()
# announce 7680 bytes (more than 4096, less than 8192 - the 512 bytes dnsdist is going to add)
announcedSize = 7680
paddingSize = announcedSize - len(wire)
- wire = wire + (b'A' * (paddingSize - 1))
+ wire = wire + (b"A" * (paddingSize - 1))
self._toResponderQueue.put(expectedResponse, True, 2.0)
sizeBytes = struct.pack("!H", announcedSize)
conn.send(wire)
time.sleep(1)
# send the remaining byte
- conn.send(b'A')
+ conn.send(b"A")
(receivedQuery, receivedResponse) = self.recvTCPResponseOverConnection(conn, True)
conn.close()
"""
TCP/TLS: Short read from client
"""
- name = 'short-read-tls.tcp-short.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "short-read-tls.tcp-short.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
expectedResponse.answer.append(rrset)
conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert)
# announce 7680 bytes (more than 4096, less than 8192 - the 512 bytes dnsdist is going to add)
announcedSize = 7680
paddingSize = announcedSize - len(wire)
- wire = wire + (b'A' * (paddingSize - 1))
+ wire = wire + (b"A" * (paddingSize - 1))
self._toResponderQueue.put(expectedResponse, True, 2.0)
sizeBytes = struct.pack("!H", announcedSize)
conn.send(wire)
time.sleep(1)
# send the remaining byte
- conn.send(b'A')
+ conn.send(b"A")
(receivedQuery, receivedResponse) = self.recvTCPResponseOverConnection(conn, True)
conn.close()
"""
TCP: Short write to client
"""
- name = 'short-write.tcp-short.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AXFR', 'IN')
+ name = "short-write.tcp-short.tests.powerdns.com."
+ query = dns.message.make_query(name, "AXFR", "IN")
# we prepare a large AXFR answer
# SOA + 200 dns messages of one huge TXT RRset each + SOA
responses = []
- soa = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+ soa = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
soaResponse = dns.message.make_response(query)
soaResponse.use_edns(edns=False)
content = ""
for i in range(200):
if len(content) > 0:
- content = content + ', '
- content = content + (str(i)*50)
-
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- content)
+ content = content + ", "
+ content = content + (str(i) * 50)
+
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, content)
response.answer.append(rrset)
for _ in range(200):
break
(datalen,) = struct.unpack("!H", datalen)
- data = b''
+ data = b""
remaining = datalen
got = conn.recv(remaining)
while got:
TCP/TLS: Short write to client
"""
# same as testTCPShortWrite but over TLS this time
- name = 'short-write-tls.tcp-short.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AXFR', 'IN')
+ name = "short-write-tls.tcp-short.tests.powerdns.com."
+ query = dns.message.make_query(name, "AXFR", "IN")
responses = []
- soa = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.SOA,
- 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+ soa = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns." + name + " hostmaster." + name + " 1 3600 3600 3600 60",
+ )
soaResponse = dns.message.make_response(query)
soaResponse.use_edns(edns=False)
content = ""
for i in range(200):
if len(content) > 0:
- content = content + ', '
- content = content + (str(i)*50)
-
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- content)
+ content = content + ", "
+ content = content + (str(i) * 50)
+
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, content)
response.answer.append(rrset)
for _ in range(200):
break
(datalen,) = struct.unpack("!H", datalen)
- data = b''
+ data = b""
remaining = datalen
got = conn.recv(remaining)
while got:
from dnsdisttests import DNSDistTest, pickAvailablePort
-class TLSTests(object):
+class TLSTests(object):
def getServerCertificate(self):
conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert)
cert = conn.getpeercert()
"""
TLS: Single query
"""
- name = 'single.tls.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "single.tls.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert)
# check the certificate
cert = self.getServerCertificate()
- self.assertIn('subject', cert)
- self.assertIn('serialNumber', cert)
- self.assertIn('subjectAltName', cert)
- subject = cert['subject']
- altNames = cert['subjectAltName']
- self.assertEqual(dict(subject[0])['commonName'], 'tls.tests.dnsdist.org')
- self.assertEqual(dict(subject[1])['organizationalUnitName'], 'PowerDNS.com BV')
+ self.assertIn("subject", cert)
+ self.assertIn("serialNumber", cert)
+ self.assertIn("subjectAltName", cert)
+ subject = cert["subject"]
+ altNames = cert["subjectAltName"]
+ self.assertEqual(dict(subject[0])["commonName"], "tls.tests.dnsdist.org")
+ self.assertEqual(dict(subject[1])["organizationalUnitName"], "PowerDNS.com BV")
names = []
for entry in altNames:
names.append(entry[1])
- self.assertEqual(names, ['tls.tests.dnsdist.org', 'powerdns.com', '127.0.0.1'])
- serialNumber = cert['serialNumber']
+ self.assertEqual(names, ["tls.tests.dnsdist.org", "powerdns.com", "127.0.0.1"])
+ serialNumber = cert["serialNumber"]
- self.generateNewCertificateAndKey('server-tls')
+ self.generateNewCertificateAndKey("server-tls")
self.sendConsoleCommand("reloadAllCertificates()")
conn.close()
# check that the certificate is OK
cert = self.getServerCertificate()
- self.assertIn('subject', cert)
- self.assertIn('serialNumber', cert)
- self.assertIn('subjectAltName', cert)
- subject = cert['subject']
- altNames = cert['subjectAltName']
- self.assertEqual(dict(subject[0])['commonName'], 'tls.tests.dnsdist.org')
- self.assertEqual(dict(subject[1])['organizationalUnitName'], 'PowerDNS.com BV')
+ self.assertIn("subject", cert)
+ self.assertIn("serialNumber", cert)
+ self.assertIn("subjectAltName", cert)
+ subject = cert["subject"]
+ altNames = cert["subjectAltName"]
+ self.assertEqual(dict(subject[0])["commonName"], "tls.tests.dnsdist.org")
+ self.assertEqual(dict(subject[1])["organizationalUnitName"], "PowerDNS.com BV")
names = []
for entry in altNames:
names.append(entry[1])
- self.assertEqual(names, ['tls.tests.dnsdist.org', 'powerdns.com', '127.0.0.1'])
+ self.assertEqual(names, ["tls.tests.dnsdist.org", "powerdns.com", "127.0.0.1"])
# and that the serial is different
- self.assertNotEqual(serialNumber, cert['serialNumber'])
+ self.assertNotEqual(serialNumber, cert["serialNumber"])
conn.close()
def testTLKA(self):
"""
TLS: Several queries over the same connection
"""
- name = 'ka.tls.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "ka.tls.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert)
"""
TLS: Several queries over the same connection without waiting for the responses
"""
- name = 'pipelining.tls.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "pipelining.tls.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert)
"""
TLS: SNI Routing
"""
- name = 'sni.tls.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "sni.tls.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.4')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
expectedResponse.answer.append(rrset)
# this SNI should match so we should get a spoofed answer
- conn = self.openTLSConnection(self._tlsServerPort, 'powerdns.com', self._caCert)
+ conn = self.openTLSConnection(self._tlsServerPort, "powerdns.com", self._caCert)
self.sendTCPQueryOverConnection(conn, query, response=None)
receivedResponse = self.recvTCPResponseOverConnection(conn, useQueue=False)
"""
TLS: SNI Routing after resumption
"""
- name = 'sni-resumed.tls.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "sni-resumed.tls.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.4')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
expectedResponse.answer.append(rrset)
# this SNI should match so we should get a spoofed answer
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
sock.settimeout(2.0)
- sslsock = sslctx.wrap_socket(sock, server_hostname='powerdns.com')
+ sslsock = sslctx.wrap_socket(sock, server_hostname="powerdns.com")
sslsock.connect(("127.0.0.1", self._tlsServerPort))
self.sendTCPQueryOverConnection(sslsock, query, response=None)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
sock.settimeout(2.0)
- sslsock = sslctx.wrap_socket(sock, server_hostname='powerdns.com')
+ sslsock = sslctx.wrap_socket(sock, server_hostname="powerdns.com")
sslsock.session = session
sslsock.connect(("127.0.0.1", self._tlsServerPort))
self.assertEqual(expectedResponse, receivedResponse)
self.assertTrue(sslsock.session_reused)
-class TestOpenSSL(DNSDistTest, TLSTests):
+class TestOpenSSL(DNSDistTest, TLSTests):
_extraStartupSleep = 1
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _serverKey = 'server-tls.key'
- _serverCert = 'server-tls.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _serverIPOnlyKey = 'server-ip-only.key'
- _serverIPOnlyCert = 'server-ip-only.chain'
- _caCert = 'ca.pem'
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _serverKey = "server-tls.key"
+ _serverCert = "server-tls.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _serverIPOnlyKey = "server-ip-only.key"
+ _serverIPOnlyCert = "server-ip-only.chain"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_tlsServerPort2 = pickAvailablePort()
_config_template = """
addTLSLocal("127.0.0.1:%d", "%s", "%s", { provider="openssl" })
addAction(SNIRule("powerdns.com"), SpoofAction("1.2.3.4"))
"""
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_tlsServerPort2', '_serverIPOnlyCert', '_serverIPOnlyKey']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_tlsServerPort2",
+ "_serverIPOnlyCert",
+ "_serverIPOnlyKey",
+ ]
@classmethod
def setUpClass(cls):
- cls.generateNewCertificateAndKey('server-tls')
+ cls.generateNewCertificateAndKey("server-tls")
cls.startResponders()
cls.startDNSDist()
cls.setUpSockets()
def testProvider(self):
self.assertEqual(self.getTLSProvider(), "openssl")
-class TestGnuTLS(DNSDistTest, TLSTests):
+class TestGnuTLS(DNSDistTest, TLSTests):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _serverKey = 'server-tls.key'
- _serverCert = 'server-tls.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _serverIPOnlyKey = 'server-ip-only.key'
- _serverIPOnlyCert = 'server-ip-only.chain'
- _caCert = 'ca.pem'
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _serverKey = "server-tls.key"
+ _serverCert = "server-tls.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _serverIPOnlyKey = "server-ip-only.key"
+ _serverIPOnlyCert = "server-ip-only.chain"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_tlsServerPort2 = pickAvailablePort()
_config_template = """
addTLSLocal("127.0.0.1:%d", "%s", "%s", { provider="gnutls" })
addAction(SNIRule("powerdns.com"), SpoofAction("1.2.3.4"))
"""
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_tlsServerPort2', '_serverIPOnlyCert', '_serverIPOnlyKey']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ "_tlsServerPort2",
+ "_serverIPOnlyCert",
+ "_serverIPOnlyKey",
+ ]
@classmethod
def setUpClass(cls):
- cls.generateNewCertificateAndKey('server-tls')
+ cls.generateNewCertificateAndKey("server-tls")
cls.startResponders()
cls.startDNSDist()
cls.setUpSockets()
def testProvider(self):
self.assertEqual(self.getTLSProvider(), "gnutls")
-class TestOpenSSLYaml(DNSDistTest, TLSTests):
+class TestOpenSSLYaml(DNSDistTest, TLSTests):
_extraStartupSleep = 1
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _serverKey = 'server-tls.key'
- _serverCert = 'server-tls.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _serverKey = "server-tls.key"
+ _serverCert = "server-tls.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_config_template = ""
_config_params = []
ips:
- "1.2.3.4"
"""
- _yaml_config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey']
+ _yaml_config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
@classmethod
def setUpClass(cls):
- cls.generateNewCertificateAndKey('server-tls')
+ cls.generateNewCertificateAndKey("server-tls")
cls.startResponders()
cls.startDNSDist()
cls.setUpSockets()
def testProvider(self):
self.assertEqual(self.getTLSProvider(), "openssl")
+
class TestDOTWithCache(DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_config_template = """
newServer{address="127.0.0.1:%d"}
pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
getPool(""):setCache(pc)
"""
- _config_params = ['_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_tlsServerPort", "_serverCert", "_serverKey"]
def testDOTCacheLargeAnswer(self):
"""
DOT with cache: Check that we can cache (and retrieve) large answers
"""
numberOfQueries = 10
- name = 'large.dot-with-cache.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ name = "large.dot-with-cache.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN", use_edns=False)
query.id = 0
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, payload=4096)
expectedQuery.id = 0
response = dns.message.make_response(query)
# we prepare a large answer
content = ""
for i in range(44):
if len(content) > 0:
- content = content + ', '
- content = content + (str(i)*50)
+ content = content + ", "
+ content = content + (str(i) * 50)
# pad up to 4096
- content = content + 'A'*40
+ content = content + "A" * 40
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.TXT,
- content)
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.TXT, content)
response.answer.append(rrset)
self.assertEqual(len(response.to_wire()), 4096)
self.assertEqual(receivedResponse, response)
conn.close()
-class TestTLSFrontendLimits(DNSDistTest):
+class TestTLSFrontendLimits(DNSDistTest):
# this test suite uses a different responder port
# because it uses a different health check configuration
_testServerPort = pickAvailablePort()
_answerUnexpected = True
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_skipListeningOnCL = True
newServer{address="127.0.0.1:%d"}
addTLSLocal("127.0.0.1:%d", "%s", "%s", { provider="openssl", maxConcurrentTCPConnections=%d })
"""
- _config_params = ['_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_maxTCPConnsPerTLSFrontend']
- _alternateListeningAddr = '127.0.0.1'
+ _config_params = ["_testServerPort", "_tlsServerPort", "_serverCert", "_serverKey", "_maxTCPConnsPerTLSFrontend"]
+ _alternateListeningAddr = "127.0.0.1"
_alternateListeningPort = _tlsServerPort
def testTCPConnsPerTLSFrontend(self):
"""
TLS Frontend Limits: Maximum number of conns per TLS frontend
"""
- name = 'maxconnspertlsfrontend.tls.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "maxconnspertlsfrontend.tls.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
conns = []
for idx in range(self._maxTCPConnsPerTLSFrontend + 1):
self.assertEqual(count, self._maxTCPConnsPerTLSFrontend)
self.assertEqual(failed, 1)
+
class TestProtocols(DNSDistTest):
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_config_template = """
newServer{address="127.0.0.1:%d"}
addTLSLocal("127.0.0.1:%d", "%s", "%s", { provider="openssl" })
"""
- _config_params = ['_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey']
+ _config_params = ["_testServerPort", "_tlsServerPort", "_serverCert", "_serverKey"]
def testProtocolDOT(self):
"""
DoT: Test DNSQuestion.Protocol
"""
- name = 'protocols.tls.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "protocols.tls.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert)
self.assertEqual(response, receivedResponse)
conn.close()
+
class TestPKCSTLSCertificate(DNSDistTest, TLSTests):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _serverCert = 'server-tls.p12'
- _pkcsPassphrase = 'passw0rd'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
+ _serverCert = "server-tls.p12"
+ _pkcsPassphrase = "passw0rd"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_config_template = """
setKey("%s")
addTLSLocal("127.0.0.1:%d", cert, "", { provider="openssl" })
addAction(SNIRule("powerdns.com"), SpoofAction("1.2.3.4"))
"""
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_serverCert', '_pkcsPassphrase', '_tlsServerPort']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_serverCert",
+ "_pkcsPassphrase",
+ "_tlsServerPort",
+ ]
@classmethod
def setUpClass(cls):
- cls.generateNewCertificateAndKey('server-tls')
+ cls.generateNewCertificateAndKey("server-tls")
cls.startResponders()
cls.startDNSDist()
cls.setUpSockets()
+
class TestOpenSSLTLSTicketsKeyCallback(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_numberOfKeys = 5
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
TLSTicketsKey: test setting new key and the key added hook
"""
- newKey = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(80))
- self.sendConsoleCommand("getTLSFrontend(0):loadTicketsKey(\"{}\")".format(newKey))
- keyLen = self.sendConsoleCommand('lastKeyLen')
+ newKey = "".join(random.choice(string.ascii_uppercase + string.digits) for _ in range(80))
+ self.sendConsoleCommand('getTLSFrontend(0):loadTicketsKey("{}")'.format(newKey))
+ keyLen = self.sendConsoleCommand("lastKeyLen")
self.assertEqual(int(keyLen), 80)
- lastKey = self.sendConsoleCommand('lastKey')
+ lastKey = self.sendConsoleCommand("lastKey")
self.assertEqual(newKey, lastKey.strip())
+
class TestGnuTLSTLSTicketsKeyCallback(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
- _serverKey = 'server.key'
- _serverCert = 'server.chain'
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
+ _serverKey = "server.key"
+ _serverCert = "server.chain"
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
_tlsServerPort = pickAvailablePort()
_numberOfKeys = 5
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_tlsServerPort",
+ "_serverCert",
+ "_serverKey",
+ ]
_config_template = """
setKey("%s")
controlSocket("127.0.0.1:%d")
TLSTicketsKey: test setting new key and the key added hook
"""
- newKey = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(64))
- self.sendConsoleCommand("getTLSFrontend(0):loadTicketsKey(\"{}\")".format(newKey))
- keyLen = self.sendConsoleCommand('lastKeyLen')
+ newKey = "".join(random.choice(string.ascii_uppercase + string.digits) for _ in range(64))
+ self.sendConsoleCommand('getTLSFrontend(0):loadTicketsKey("{}")'.format(newKey))
+ keyLen = self.sendConsoleCommand("lastKeyLen")
self.assertEqual(int(keyLen), 64)
- lastKey = self.sendConsoleCommand('lastKey')
+ lastKey = self.sendConsoleCommand("lastKey")
self.assertEqual(newKey, lastKey.strip())
class DNSDistTLSSessionResumptionTest(DNSDistTest):
-
_consoleKey = DNSDistTest.generateConsoleKey()
_consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
time.sleep(0.5)
output = process.communicate(input=b"")
except subprocess.CalledProcessError as exc:
- raise AssertionError(
- "%s failed (%d): %s" % (testcmd, process.returncode, process.output)
- )
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, process.output))
if process.returncode != 0:
- raise AssertionError(
- "%s failed (%d): %s" % (testcmd, process.returncode, output)
- )
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, output))
if os.stat(outFile.name).st_size == 0:
# if tickets have been disabled, or if the session ticket encryption key is exactly the same, we might not get a new ticket
if not allowNoTicket:
raise AssertionError(
- "%s failed (%d) to write a session to the output file: %s"
- % (testcmd, process.returncode, output)
+ "%s failed (%d) to write a session to the output file: %s" % (testcmd, process.returncode, output)
)
else:
shutil.copyfile(outFile.name, ticketFileOut)
@unittest.skipIf("SKIP_DOH_TESTS" in os.environ, "DNS over HTTPS tests are disabled")
class TestNoTLSSessionResumptionDOH(DNSDistTLSSessionResumptionTest):
-
_serverKey = "server.key"
_serverCert = "server.chain"
_serverName = "tls.tests.dnsdist.org"
@unittest.skipIf("SKIP_DOH_TESTS" in os.environ, "DNS over HTTPS tests are disabled")
class TestTLSSessionResumptionDOH(DNSDistTLSSessionResumptionTest):
-
_serverKey = "server.key"
_serverCert = "server.chain"
_serverName = "tls.tests.dnsdist.org"
self.generateTicketKeysFile(self._numberOfKeys, "/tmp/ticketKeys.1")
self.generateTicketKeysFile(self._numberOfKeys - 1, "/tmp/ticketKeys.2")
# load all ticket keys from the file
- self.sendConsoleCommand(
- f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.1')"
- )
+ self.sendConsoleCommand(f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.1')")
# create a new session, resume it
self.assertFalse(
)
# reload the same keys
- self.sendConsoleCommand(
- f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.1')"
- )
+ self.sendConsoleCommand(f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.1')")
# should still be able to resume
self.assertTrue(
)
# reload the same keys
- self.sendConsoleCommand(
- f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.1')"
- )
+ self.sendConsoleCommand(f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.1')")
# since the last key was only present in memory, we should not be able to resume
self.assertFalse(
self.checkSessionResumed(
# generate a file with only _numberOfKeys - 1 keys, so the last active one should still be around after loading that one
self.generateTicketKeysFile(self._numberOfKeys - 1, "/tmp/ticketKeys.2")
- self.sendConsoleCommand(
- f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.2')"
- )
+ self.sendConsoleCommand(f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.2')")
# we should be able to resume, and the ticket should be re-encrypted with the new key (NOTE THAT we store into a new file!!)
self.assertTrue(
self.checkSessionResumed(
)
# reload from file 1, the old session should resume
- self.sendConsoleCommand(
- f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.1')"
- )
+ self.sendConsoleCommand(f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.1')")
self.assertTrue(
self.checkSessionResumed(
"127.0.0.1",
)
# reload from file 2, the latest session should resume
- self.sendConsoleCommand(
- f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.2')"
- )
+ self.sendConsoleCommand(f"getDOHFrontend({bindIdx}):loadTicketsKeys('/tmp/ticketKeys.2')")
self.assertTrue(
self.checkSessionResumed(
"127.0.0.1",
class TestNoTLSSessionResumptionDOT(DNSDistTLSSessionResumptionTest):
-
_serverKey = "server.key"
_serverCert = "server.chain"
_serverName = "tls.tests.dnsdist.org"
class TestTLSSessionResumptionDOT(DNSDistTLSSessionResumptionTest):
-
_serverKey = "server.key"
_serverCert = "server.chain"
_serverName = "tls.tests.dnsdist.org"
self.generateTicketKeysFile(self._numberOfKeys, "/tmp/ticketKeys.1")
self.generateTicketKeysFile(self._numberOfKeys - 1, "/tmp/ticketKeys.2")
# load all ticket keys from the file
- self.sendConsoleCommand(
- "getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')"
- )
+ self.sendConsoleCommand("getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
# create a new session, resume it
self.assertFalse(
)
# reload the same keys
- self.sendConsoleCommand(
- "getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')"
- )
+ self.sendConsoleCommand("getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
# should still be able to resume
self.assertTrue(
)
# reload the same keys
- self.sendConsoleCommand(
- "getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')"
- )
+ self.sendConsoleCommand("getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
# since the last key was only present in memory, we should not be able to resume
self.assertFalse(
self.checkSessionResumed(
# generate a file with only _numberOfKeys - 1 keys, so the last active one should still be around after loading that one
self.generateTicketKeysFile(self._numberOfKeys - 1, "/tmp/ticketKeys.2")
- self.sendConsoleCommand(
- "getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.2')"
- )
+ self.sendConsoleCommand("getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.2')")
# we should be able to resume, and the ticket should be re-encrypted with the new key (NOTE THAT we store into a new file!!)
self.assertTrue(
self.checkSessionResumed(
)
# reload from file 1, the old session should resume
- self.sendConsoleCommand(
- "getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')"
- )
+ self.sendConsoleCommand("getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
self.assertTrue(
self.checkSessionResumed(
"127.0.0.1",
)
# reload from file 2, the latest session should resume
- self.sendConsoleCommand(
- "getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.2')"
- )
+ self.sendConsoleCommand("getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.2')")
self.assertTrue(
self.checkSessionResumed(
"127.0.0.1",
_webServerPort = pickAvailablePort()
_webServerBasicAuthPassword = "secret"
_webServerAPIKey = "apisecret"
- _webServerBasicAuthPasswordHashed = "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
- _webServerAPIKeyHashed = "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ _webServerBasicAuthPasswordHashed = (
+ "$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM="
+ )
+ _webServerAPIKeyHashed = (
+ "$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso="
+ )
_serverKey = "server.key"
_serverCert = "server.chain"
_serverName = "tls.tests.dnsdist.org"
session=session,
)
self.sendTCPQueryOverConnection(conn, query, response=response, timeout=1)
- (receivedQuery, receivedResponse) = self.recvTCPResponseOverConnection(
- conn, useQueue=True, timeout=1
- )
+ (receivedQuery, receivedResponse) = self.recvTCPResponseOverConnection(conn, useQueue=True, timeout=1)
receivedQuery.id = query.id
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
self.assertTrue(conn.session_reused)
headers = {"x-api-key": self._webServerAPIKey}
- url = (
- "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost"
- )
+ url = "http://127.0.0.1:" + str(self._webServerPort) + "/api/v1/servers/localhost"
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
continue
new_sessions = new_sessions + int(frontend["tlsNewSessions"])
resumed_sessions = resumed_sessions + int(frontend["tlsResumptions"])
- if (
- int(frontend["tlsNewSessions"]) > 0
- or int(frontend["tlsResumptions"]) > 0
- ):
+ if int(frontend["tlsNewSessions"]) > 0 or int(frontend["tlsResumptions"]) > 0:
tls_frontends_seen[frontend["id"]] = True
self.assertEqual(new_sessions, 1)
import dns
from dnsdisttests import DNSDistTest
-class TestTags(DNSDistTest):
+class TestTags(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
"""
Tag: No match
"""
- name = 'no-match.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "no-match.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Tag: Name and value match
"""
- name = 'tag-me-dns-1.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tag-me-dns-1.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.50')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.50")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Tag: Name matches
"""
- name = 'tag-me-dns-2.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tag-me-dns-2.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.100')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.100")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Tag: Name matches, and value is exactly empty
"""
- name = 'tag-me-dns-3.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tag-me-dns-3.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.75')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.75")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Tag: Tag set on query does not match anything
"""
- name = 'tag-me-response-2.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tag-me-response-2.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
"""
Tag: Tag and value set on query matches on response
"""
- name = 'tag-me-response-1.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tag-me-response-1.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.100')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.100")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
"""
Tag: Tag set on response matches
"""
- name = 'tag-me-response-3.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tag-me-response-3.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.100')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.100")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
self.assertEqual(query, receivedQuery)
self.assertEqual(expectedResponse, receivedResponse)
-class TestSetTagAction(DNSDistTest):
+class TestSetTagAction(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
"""
def testSetTagDefault(self):
-
"""
Tag: Test setTag overwrites existing value
"""
- name = 'tag-me-dns-1.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tag-me-dns-1.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.50')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.50")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(expectedResponse, receivedResponse)
def testSetTagOverwritten(self):
-
"""
Tag: Test setTag overwrites existing value
"""
- name = 'tag-me-dns-2.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tag-me-dns-2.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.4')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(receivedResponse)
self.assertEqual(expectedResponse, receivedResponse)
-class TestSetTag(DNSDistTest):
+class TestSetTag(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
"""
def testSetTagDefault(self):
-
"""
Tag: Test setTag overwrites existing value
"""
- name = 'tag-me-dns-1.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tag-me-dns-1.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.50')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.50")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(expectedResponse, receivedResponse)
def testSetTagOverwritten(self):
-
"""
Tag: Test setTag overwrites existing value
"""
- name = 'tag-me-dns-2.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "tag-me-dns-2.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.4')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertTrue(receivedResponse)
self.assertEqual(expectedResponse, receivedResponse)
-class TestUnsetTag(DNSDistTest):
+class TestUnsetTag(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
"""
def testUnsetTag(self):
-
"""
Tag: Test UnsetTagAction
"""
- name = 'unset.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "unset.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.50')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.50")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(expectedResponse, receivedResponse)
-class TestUnsetTagViaLua(DNSDistTest):
+class TestUnsetTagViaLua(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%d"}
"""
def testUnsetTag(self):
-
"""
Tag: Test UnsetTag via Lua
"""
- name = 'unset-lua.tags.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "unset-lua.tags.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '1.2.3.50')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.50")
expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
from dnsdisttests import DNSDistTest, Queue, pickAvailablePort
from proxyprotocolutils import ProxyProtocolUDPResponder
-class TestTeeAction(DNSDistTest):
+class TestTeeAction(DNSDistTest):
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_teeServerPort = pickAvailablePort()
_teeProxyServerPort = pickAvailablePort()
_toTeeQueue = Queue()
addAction(QTypeRule(DNSQType.AAAA), TeeAction("127.0.0.1:%d", false))
addAction(QTypeRule(DNSQType.ANY), TeeAction("127.0.0.1:%d", false, '127.0.0.1', true))
"""
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_teeServerPort', '_teeServerPort', '_teeProxyServerPort']
+ _config_params = [
+ "_consoleKeyB64",
+ "_consolePort",
+ "_testServerPort",
+ "_teeServerPort",
+ "_teeServerPort",
+ "_teeProxyServerPort",
+ ]
+
@classmethod
def startResponders(cls):
print("Launching responders..")
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, True])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, True],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._TeeResponder = threading.Thread(name='Tee Responder', target=cls.UDPResponder, args=[cls._teeServerPort, cls._toTeeQueue, cls._fromTeeQueue])
+ cls._TeeResponder = threading.Thread(
+ name="Tee Responder", target=cls.UDPResponder, args=[cls._teeServerPort, cls._toTeeQueue, cls._fromTeeQueue]
+ )
cls._TeeResponder.daemon = True
cls._TeeResponder.start()
- cls._TeeProxyResponder = threading.Thread(name='Proxy Protocol Tee Responder', target=ProxyProtocolUDPResponder, args=[cls._teeProxyServerPort, cls._toTeeProxyQueue, cls._fromTeeProxyQueue])
+ cls._TeeProxyResponder = threading.Thread(
+ name="Proxy Protocol Tee Responder",
+ target=ProxyProtocolUDPResponder,
+ args=[cls._teeProxyServerPort, cls._toTeeProxyQueue, cls._fromTeeProxyQueue],
+ )
cls._TeeProxyResponder.daemon = True
cls._TeeProxyResponder.start()
"""
TeeAction: ECS
"""
- name = 'ecs.tee.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "ecs.tee.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
numberOfQueries = 10
# retrieve the query from the Tee server
teedQuery = self._fromTeeQueue.get(True, 2.0)
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ expectedQuery = dns.message.make_query(name, "A", "IN", use_edns=True, options=[ecso], payload=512)
expectedQuery.id = query.id
self.checkQueryEDNSWithECS(expectedQuery, teedQuery)
# check the TeeAction stats
stats = self.sendConsoleCommand("getAction(0):printStats()")
- self.assertEqual(stats, """noerrors\t%d
+ self.assertEqual(
+ stats,
+ """noerrors\t%d
nxdomains\t0
other-rcode\t0
queries\t%d
send-errors\t0
servfails\t0
tcp-drops\t0
-""" % (numberOfQueries, numberOfQueries, numberOfQueries))
+"""
+ % (numberOfQueries, numberOfQueries, numberOfQueries),
+ )
def testTeeWithoutECS(self):
"""
TeeAction: No ECS
"""
- name = 'noecs.tee.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
+ name = "noecs.tee.tests.powerdns.com."
+ query = dns.message.make_query(name, "AAAA", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.AAAA, "2001:DB8::1")
response.answer.append(rrset)
numberOfQueries = 10
# retrieve the query from the Tee server
teedQuery = self._fromTeeQueue.get(True, 2.0)
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
- expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 24)
+ expectedQuery = dns.message.make_query(name, "AAAA", "IN", use_edns=True, options=[ecso], payload=512)
expectedQuery.id = query.id
self.checkMessageNoEDNS(expectedQuery, teedQuery)
# check the TeeAction stats
stats = self.sendConsoleCommand("getAction(0):printStats()")
- self.assertEqual(stats, """noerrors\t%d
+ self.assertEqual(
+ stats,
+ """noerrors\t%d
nxdomains\t0
other-rcode\t0
queries\t%d
send-errors\t0
servfails\t0
tcp-drops\t0
-""" % (numberOfQueries, numberOfQueries, numberOfQueries))
+"""
+ % (numberOfQueries, numberOfQueries, numberOfQueries),
+ )
def testTeeWithProxy(self):
"""
TeeAction: Proxy
"""
- name = 'proxy.tee.tests.powerdns.com.'
- query = dns.message.make_query(name, 'ANY', 'IN')
+ name = "proxy.tee.tests.powerdns.com."
+ query = dns.message.make_query(name, "ANY", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1")
response.answer.append(rrset)
numberOfQueries = 10
# retrieve the query from the Tee Proxy server
[payload, teedQuery] = self._fromTeeProxyQueue.get(True, 2.0)
self.checkMessageNoEDNS(query, dns.message.from_wire(teedQuery))
- self.checkMessageProxyProtocol(payload, '127.0.0.1', '127.0.0.1', False)
+ self.checkMessageProxyProtocol(payload, "127.0.0.1", "127.0.0.1", False)
# check the TeeAction stats
stats = self.sendConsoleCommand("getAction(0):printStats()")
- self.assertEqual(stats, """noerrors\t%d
+ self.assertEqual(
+ stats,
+ """noerrors\t%d
nxdomains\t0
other-rcode\t0
queries\t%d
send-errors\t0
servfails\t0
tcp-drops\t0
-""" % (numberOfQueries, numberOfQueries, numberOfQueries))
+"""
+ % (numberOfQueries, numberOfQueries, numberOfQueries),
+ )
import dns
from dnsdisttests import DNSDistTest, pickAvailablePort
-class TestTimeIPSetYaml(DNSDistTest):
+class TestTimeIPSetYaml(DNSDistTest):
_yaml_config_template = """---
console:
listen_address: "127.0.0.1:%d"
rcode: "Refused"
"""
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_consolePort = pickAvailablePort()
_testServerPort = pickAvailablePort()
- _yaml_config_params = ['_consolePort', '_consoleKeyB64', '_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_consolePort", "_consoleKeyB64", "_dnsDistPort", "_testServerPort"]
_config_params = []
def testTimedIPSet(self):
"""
TimedIPSet from YAML configuration
"""
- name = 'timedipset-yaml.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "timedipset-yaml.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
refusedResponse = dns.message.make_response(query)
refusedResponse.set_rcode(dns.rcode.REFUSED)
self.assertEqual(receivedResponse, response)
# now we block it for two seconds
- self.sendConsoleCommand('getObjectFromYAMLConfiguration(\'my-set\'):add(newCA(\'127.0.0.1\'), 2)')
+ self.sendConsoleCommand("getObjectFromYAMLConfiguration('my-set'):add(newCA('127.0.0.1'), 2)")
for method in ["sendUDPQuery", "sendTCPQuery"]:
sender = getattr(self, method)
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
+
class TestTimeIPSetLua(DNSDistTest):
_config_template = """---
setKey("%s")
addAction(mySet:slice(), RCodeAction(DNSRCode.REFUSED))
"""
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_consolePort = pickAvailablePort()
_testServerPort = pickAvailablePort()
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
+ _config_params = ["_consoleKeyB64", "_consolePort", "_testServerPort"]
def testTimedIPSet(self):
"""
TimedIPSet from Lua configuration
"""
- name = 'timedipset-lua.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "timedipset-lua.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
refusedResponse = dns.message.make_response(query)
refusedResponse.set_rcode(dns.rcode.REFUSED)
self.assertEqual(receivedResponse, response)
# now we block it for two seconds
- self.sendConsoleCommand('mySet:add(newCA(\'127.0.0.1\'), 2)')
+ self.sendConsoleCommand("mySet:add(newCA('127.0.0.1'), 2)")
for method in ["sendUDPQuery", "sendTCPQuery"]:
sender = getattr(self, method)
addTimeoutResponseAction(AllRule(), LuaResponseAction(restartQuery))
"""
+
def timeoutResponseCallback(request):
return ResponderDropAction()
+
def normalResponseCallback(request):
response = dns.message.make_response(request)
- rrset = dns.rrset.from_text(request.question[0].name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(request.question[0].name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
return response.to_wire()
+
def dohTimeoutResponseCallback(request, headers, fromQueue, toQueue):
return 200, timeoutResponseCallback(request)
+
def dohNormalResponseCallback(request, headers, fromQueue, toQueue):
return 200, normalResponseCallback(request)
-class TestTimeoutBackendUdpTcp(DNSDistTest):
+class TestTimeoutBackendUdpTcp(DNSDistTest):
# this test suite uses different responder ports
_testNormalServerPort = pickAvailablePort()
_testTimeoutServerPort = pickAvailablePort()
_doh3ServerPort = pickAvailablePort()
_tlsServerPort = pickAvailablePort()
- _serverName = 'tls.tests.dnsdist.org'
- _caCert = 'ca.pem'
- _dohWithNGHTTP2BaseURL = ("https://%s:%d/dns-query" % ("127.0.0.1", _dohWithNGHTTP2ServerPort))
- _dohBaseURL = ("https://%s:%d/" % (_serverName, _doh3ServerPort))
+ _serverName = "tls.tests.dnsdist.org"
+ _caCert = "ca.pem"
+ _dohWithNGHTTP2BaseURL = "https://%s:%d/dns-query" % ("127.0.0.1", _dohWithNGHTTP2ServerPort)
+ _dohBaseURL = "https://%s:%d/" % (_serverName, _doh3ServerPort)
- _config_template = """
+ _config_template = (
+ """
newServer{address="127.0.0.1:%d",pool='restarted',udpTimeout=1,tcpRecvTimeout=1}:setUp()
newServer{address="127.0.0.1:%d",pool='',udpTimeout=1,tcpRecvTimeout=1}:setUp()
- """ + _common_config
- _config_params = ['_testNormalServerPort', '_testTimeoutServerPort', '_dohWithNGHTTP2ServerPort', '_doqServerPort', '_doh3ServerPort', '_tlsServerPort']
+ """
+ + _common_config
+ )
+ _config_params = [
+ "_testNormalServerPort",
+ "_testTimeoutServerPort",
+ "_dohWithNGHTTP2ServerPort",
+ "_doqServerPort",
+ "_doh3ServerPort",
+ "_tlsServerPort",
+ ]
_verboseMode = True
@classmethod
print("Launching responders..")
# timeout
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testTimeoutServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, timeoutResponseCallback])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[
+ cls._testTimeoutServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ timeoutResponseCallback,
+ ],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testTimeoutServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, timeoutResponseCallback])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._testTimeoutServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ timeoutResponseCallback,
+ ],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
- cls._UDPResponderNormal = threading.Thread(name='UDP ResponderNormal', target=cls.UDPResponder, args=[cls._testNormalServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, normalResponseCallback])
+ cls._UDPResponderNormal = threading.Thread(
+ name="UDP ResponderNormal",
+ target=cls.UDPResponder,
+ args=[
+ cls._testNormalServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ normalResponseCallback,
+ ],
+ )
cls._UDPResponderNormal.daemon = True
cls._UDPResponderNormal.start()
- cls._TCPResponderNormal = threading.Thread(name='TCP ResponderNormal', target=cls.TCPResponder, args=[cls._testNormalServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, normalResponseCallback])
+ cls._TCPResponderNormal = threading.Thread(
+ name="TCP ResponderNormal",
+ target=cls.TCPResponder,
+ args=[
+ cls._testNormalServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ normalResponseCallback,
+ ],
+ )
cls._TCPResponderNormal.daemon = True
cls._TCPResponderNormal.start()
"""
Restart: Timeout then restarted to a second pool
"""
- name = 'timeout.restart.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ name = "timeout.restart.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse = dns.message.make_response(query)
expectedResponse.answer.append(rrset)
- for method in ("sendUDPQuery", "sendTCPQuery", "sendDOQQueryWrapper", "sendDOH3QueryWrapper", "sendDOTQueryWrapper", "sendDOHWithNGHTTP2QueryWrapper"):
+ for method in (
+ "sendUDPQuery",
+ "sendTCPQuery",
+ "sendDOQQueryWrapper",
+ "sendDOH3QueryWrapper",
+ "sendDOTQueryWrapper",
+ "sendDOHWithNGHTTP2QueryWrapper",
+ ):
sender = getattr(self, method)
(_, receivedResponse) = sender(query, response=None, useQueue=False, timeout=4)
self.assertTrue(receivedResponse)
self.assertEqual(receivedResponse, expectedResponse)
-class TestTimeoutBackendDOH(TestTimeoutBackendUdpTcp):
- _config_template = """
+class TestTimeoutBackendDOH(TestTimeoutBackendUdpTcp):
+ _config_template = (
+ """
newServer{address="127.0.0.1:%d",pool='restarted',udpTimeout=1,tcpRecvTimeout=1,tls='openssl',validateCertificates=true,caStore='ca.pem',subjectName='powerdns.com',dohPath='/dns-query'}:setUp()
newServer{address="127.0.0.1:%d",pool='',udpTimeout=1,tcpRecvTimeout=1,tls='openssl',validateCertificates=true,caStore='ca.pem',subjectName='powerdns.com',dohPath='/dns-query'}:setUp()
- """ + _common_config
+ """
+ + _common_config
+ )
@classmethod
def startResponders(cls):
# timeout
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching DOH responder..")
- cls._DOHResponder = threading.Thread(name='DOH Responder', target=cls.DOHResponder, args=[cls._testTimeoutServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, dohTimeoutResponseCallback, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH Responder",
+ target=cls.DOHResponder,
+ args=[
+ cls._testTimeoutServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ dohTimeoutResponseCallback,
+ tlsContext,
+ ],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
- cls._DOHResponder = threading.Thread(name='DOH ResponderNormal', target=cls.DOHResponder, args=[cls._testNormalServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, dohNormalResponseCallback, tlsContext])
+ cls._DOHResponder = threading.Thread(
+ name="DOH ResponderNormal",
+ target=cls.DOHResponder,
+ args=[
+ cls._testNormalServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ dohNormalResponseCallback,
+ tlsContext,
+ ],
+ )
cls._DOHResponder.daemon = True
cls._DOHResponder.start()
-class TestTimeoutBackendDOT(TestTimeoutBackendUdpTcp):
- _config_template = """
+class TestTimeoutBackendDOT(TestTimeoutBackendUdpTcp):
+ _config_template = (
+ """
newServer{address="127.0.0.1:%d",pool='restarted',udpTimeout=1,tcpRecvTimeout=1,tls='openssl',validateCertificates=true,caStore='ca.pem',subjectName='powerdns.com'}:setUp()
newServer{address="127.0.0.1:%d",pool='',udpTimeout=1,tcpRecvTimeout=1,tls='openssl',validateCertificates=true,caStore='ca.pem',subjectName='powerdns.com'}:setUp()
- """ + _common_config
+ """
+ + _common_config
+ )
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
print("Launching TLS responder..")
- cls._TLSResponder = threading.Thread(name='TLS Responder', target=cls.TCPResponder, args=[cls._testTimeoutServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, timeoutResponseCallback, tlsContext])
+ cls._TLSResponder = threading.Thread(
+ name="TLS Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._testTimeoutServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ timeoutResponseCallback,
+ tlsContext,
+ ],
+ )
cls._TLSResponder.daemon = True
cls._TLSResponder.start()
- cls._TLSResponder = threading.Thread(name='TLS ResponderNormal', target=cls.TCPResponder, args=[cls._testNormalServerPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, normalResponseCallback, tlsContext])
+ cls._TLSResponder = threading.Thread(
+ name="TLS ResponderNormal",
+ target=cls.TCPResponder,
+ args=[
+ cls._testNormalServerPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ normalResponseCallback,
+ tlsContext,
+ ],
+ )
cls._TLSResponder.daemon = True
cls._TLSResponder.start()
import dns
from dnsdisttests import DNSDistTest, pickAvailablePort
-class TestTrailingDataToBackend(DNSDistTest):
+class TestTrailingDataToBackend(DNSDistTest):
# this test suite uses a different responder port
# because, contrary to the other ones, its
# responders allow trailing data and we don't want
end
addAction("limited.trailing.tests.powerdns.com.", LuaAction(exceedBuffer))
"""
+
@classmethod
def startResponders(cls):
print("Launching responders..")
# Respond REFUSED to queries with trailing data.
- cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, dns.rcode.REFUSED])
+ cls._UDPResponder = threading.Thread(
+ name="UDP Responder",
+ target=cls.UDPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, dns.rcode.REFUSED],
+ )
cls._UDPResponder.daemon = True
cls._UDPResponder.start()
# Respond REFUSED to queries with trailing data.
- cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, dns.rcode.REFUSED])
+ cls._TCPResponder = threading.Thread(
+ name="TCP Responder",
+ target=cls.TCPResponder,
+ args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue, dns.rcode.REFUSED],
+ )
cls._TCPResponder.daemon = True
cls._TCPResponder.start()
Trailing data: Pass through
"""
- name = 'passthrough.trailing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "passthrough.trailing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
raw = query.to_wire()
- raw = raw + b'A'* 20
+ raw = raw + b"A" * 20
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
Trailing data: Fill buffer
"""
- name = 'max.trailing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "max.trailing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
Trailing data: Reject buffer overflows
"""
- name = 'limited.trailing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "limited.trailing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.SERVFAIL)
Trailing data: Add
"""
- name = 'added.trailing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "added.trailing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, expectedResponse)
+
class TestTrailingDataToDnsdist(DNSDistTest):
_verboseMode = True
_config_template = """
Trailing data: Drop query
"""
- name = 'dropped.trailing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "dropped.trailing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
raw = query.to_wire()
- raw = raw + b'A'* 20
+ raw = raw + b"A" * 20
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
Trailing data: Remove
"""
- name = 'removed.trailing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "removed.trailing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
raw = query.to_wire()
- raw = raw + b'A'* 20
+ raw = raw + b"A" * 20
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
Trailing data: Echo
"""
- name = 'echoed.trailing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "echoed.trailing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.SERVFAIL)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- '-TrailingData.echoed.trailing.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name, 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "-TrailingData.echoed.trailing.tests.powerdns.com."
+ )
expectedResponse.answer.append(rrset)
raw = query.to_wire()
- raw = raw + b'TrailingData'
+ raw = raw + b"TrailingData"
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
Trailing data: Replace
"""
- name = 'replaced.trailing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "replaced.trailing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.SERVFAIL)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- '-ABC.echoed.trailing.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name, 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "-ABC.echoed.trailing.tests.powerdns.com."
+ )
expectedResponse.answer.append(rrset)
raw = query.to_wire()
- raw = raw + b'TrailingData'
+ raw = raw + b"TrailingData"
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
Trailing data: Echo as hex
"""
- name = 'echoed-hex.trailing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "echoed-hex.trailing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.SERVFAIL)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- '-0x0000DEAD.echoed-hex.trailing.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name, 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "-0x0000DEAD.echoed-hex.trailing.tests.powerdns.com."
+ )
expectedResponse.answer.append(rrset)
raw = query.to_wire()
- raw = raw + b'\x00\x00\xDE\xAD'
+ raw = raw + b"\x00\x00\xde\xad"
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
Trailing data: Replace with null and/or non-ASCII bytes
"""
- name = 'replaced-unsafe.trailing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "replaced-unsafe.trailing.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
response = dns.message.make_response(query)
response.set_rcode(dns.rcode.SERVFAIL)
expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- '-0xB000DEAD42F09F91BBC3BE.echoed-hex.trailing.tests.powerdns.com.')
+ rrset = dns.rrset.from_text(
+ name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.CNAME,
+ "-0xB000DEAD42F09F91BBC3BE.echoed-hex.trailing.tests.powerdns.com.",
+ )
expectedResponse.answer.append(rrset)
raw = query.to_wire()
- raw = raw + b'TrailingData'
+ raw = raw + b"TrailingData"
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
import dns
from dnsdisttests import DNSDistTest, pickAvailablePort
-class TestYaml(DNSDistTest):
+class TestYaml(DNSDistTest):
_yaml_config_template = """---
webserver:
listen_addresses:
_webServerPort2 = pickAvailablePort()
_dnsDistPort = pickAvailablePort()
_consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode("ascii")
_consolePort = pickAvailablePort()
_testServerPort = pickAvailablePort()
- _yaml_config_params = ['_webServerPort', '_webServerPort2', '_consolePort', '_consoleKeyB64', '_dnsDistPort', '_testServerPort']
+ _yaml_config_params = [
+ "_webServerPort",
+ "_webServerPort2",
+ "_consolePort",
+ "_consoleKeyB64",
+ "_dnsDistPort",
+ "_testServerPort",
+ ]
_config_params = []
def testForwarded(self):
"""
Yaml: Forwarded query
"""
- name = 'forwarded.yaml.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "forwarded.yaml.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
# UDP query should be dropped
(_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
# TCP query should be forwarded
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response=response)
"""
Yaml: Inline Lua
"""
- name = 'inline-lua.yaml.test.powerdns.com.'
+ name = "inline-lua.yaml.test.powerdns.com."
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
truncatedResponse = dns.message.make_response(query)
self.assertEqual(receivedResponse, clearedResponse)
# response with RD should be forwarded
- query = dns.message.make_query(name, 'A', 'IN')
+ query = dns.message.make_query(name, "A", "IN")
query.flags |= dns.flags.RD
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ["sendUDPQuery", "sendTCPQuery"]:
self.assertEqual(receivedQuery, query)
self.assertEqual(receivedResponse, response)
-class TestMixingYamlWithLua(DNSDistTest):
+class TestMixingYamlWithLua(DNSDistTest):
_yaml_config_template = """---
binds:
- listen_address: "127.0.0.1:%d"
"""
_dnsDistPort = pickAvailablePort()
_testServerPort = pickAvailablePort()
- _yaml_config_params = ['_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort"]
_config_params = []
_config_template = """
enableLuaConfiguration()
"""
Yaml / Lua mix: Refused from YAML
"""
- name = 'refused.yaml-lua-mix.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "refused.yaml-lua-mix.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
"""
Yaml / Lua mix: Not imp from Lua
"""
- name = 'notimp-lua.yaml-lua-mix.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "notimp-lua.yaml-lua-mix.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.NOTIMP)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestYamlNMGRule(DNSDistTest):
+class TestYamlNMGRule(DNSDistTest):
_yaml_config_template = """---
binds:
- listen_address: "127.0.0.1:%d"
type: "RCode"
rcode: "5"
"""
- _yaml_config_params = ['_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort"]
_config_params = []
def testYamlNMGRule(self):
"""
YAML: NMGRule should refuse our queries
"""
- name = 'nmgrule.yaml.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nmgrule.yaml.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestYamlNMGRuleObject(DNSDistTest):
+class TestYamlNMGRuleObject(DNSDistTest):
_yaml_config_template = """---
binds:
- listen_address: "127.0.0.1:%d"
type: "RCode"
rcode: "5"
"""
- _yaml_config_params = ['_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort"]
_config_params = []
def testYamlNMGRule(self):
"""
YAML: NMGRule (via a NMG object) should refuse our queries
"""
- name = 'nmgrule-object.yaml.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nmgrule-object.yaml.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestYamlNMGRuleObjectExcludeMasks(DNSDistTest):
+class TestYamlNMGRuleObjectExcludeMasks(DNSDistTest):
_yaml_config_template = """---
binds:
- listen_address: "127.0.0.1:%d"
type: "RCode"
rcode: "5"
"""
- _yaml_config_params = ['_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort"]
_config_params = []
def testYamlNMGRule(self):
"""
YAML: NMGRule (via a NMG object with exclusion) should refuse our queries
"""
- name = 'nmgrule-object-exclusion.yaml.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "nmgrule-object-exclusion.yaml.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, expectedResponse)
-class TestYamlOpcode(DNSDistTest):
+class TestYamlOpcode(DNSDistTest):
_yaml_config_template = """---
binds:
- listen_address: "127.0.0.1:%d"
type: "RCode"
rcode: "Refused"
"""
- _yaml_config_params = ['_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort"]
_config_params = []
def testRefuseOpcodeNotify(self):
"""
YAML: Refuse Opcode NOTIFY
"""
- name = 'opcodenotify.yaml.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "opcodenotify.yaml.tests.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
query.set_opcode(dns.opcode.NOTIFY)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
"""
YAML: Allow Opcode UPDATE
"""
- name = 'opcodeupdate.yaml.tests.powerdns.com.'
- query = dns.message.make_query(name, 'SOA', 'IN')
+ name = "opcodeupdate.yaml.tests.powerdns.com."
+ query = dns.message.make_query(name, "SOA", "IN")
query.set_opcode(dns.opcode.UPDATE)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-class TestYamlPoolECSZeroScope(DNSDistTest):
+class TestYamlPoolECSZeroScope(DNSDistTest):
_yaml_config_template = """---
binds:
- listen_address: "127.0.0.1:%d"
type: "RCode"
rcode: "Refused"
"""
- _yaml_config_params = ['_dnsDistPort', '_testServerPort', '_testServerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort", "_testServerPort"]
_config_params = []
def testPoolECSZeroScopeConfig(self):
"""
YAML: Test pool ECS and zero scope
"""
- name = 'pool-ecs-zero-scope.yaml.tests.powerdns.com.'
- query = dns.message.make_query(name, 'SOA', 'IN')
+ name = "pool-ecs-zero-scope.yaml.tests.powerdns.com."
+ query = dns.message.make_query(name, "SOA", "IN")
query.set_opcode(dns.opcode.UPDATE)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
-class TestYamlUnknownSelectorName(DNSDistTest):
+class TestYamlUnknownSelectorName(DNSDistTest):
_yaml_config_template = """---
logging:
structured:
type: "Pool"
pool_name: "tcp-pool"
"""
- _yaml_config_params = ['_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort"]
_config_params = []
# we need this because the error is triggered during the parsing of the YAML configuration,
# too early for the logging for the logging configuration to have been applied
if cls._dnsdist:
cls.killProcess(cls._dnsdist)
-class TestYamlUnknownPolicyName(DNSDistTest):
+class TestYamlUnknownPolicyName(DNSDistTest):
_yaml_config_template = """---
logging:
structured:
- name: ""
policy: "this-policy-does-not-exist"
"""
- _yaml_config_params = ['_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort"]
_config_params = []
def testFailToStart(self):
if cls._dnsdist:
cls.killProcess(cls._dnsdist)
-class TestYamlLuaCodeUsingObjects(DNSDistTest):
+class TestYamlLuaCodeUsingObjects(DNSDistTest):
_yaml_config_template = """---
binds:
- listen_address: "127.0.0.1:%d"
type: "RCode"
rcode: "Refused"
"""
- _yaml_config_params = ['_dnsDistPort', '_testServerPort']
+ _yaml_config_params = ["_dnsDistPort", "_testServerPort"]
_config_params = []
def testLuaObjects(self):
"""
YAML: Test Lua objects
"""
- name = 'lua-objects.yaml.tests.powerdns.com.'
- query = dns.message.make_query(name, 'SOA', 'IN')
+ name = "lua-objects.yaml.tests.powerdns.com."
+ query = dns.message.make_query(name, "SOA", "IN")
query.set_opcode(dns.opcode.UPDATE)
response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
response.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
from eqdnsmessage import AssertEqualDNSMessageMixin
-class IXFRDistTest(AssertEqualDNSMessageMixin, unittest.TestCase):
+class IXFRDistTest(AssertEqualDNSMessageMixin, unittest.TestCase):
_ixfrDistStartupDelay = 2.0
_ixfrDistPort = 5342
failed-soa-retry: 3
"""
_config_domains = None
- _config_params = ['_ixfrDistPort']
+ _config_params = ["_ixfrDistPort"]
@classmethod
def startIXFRDist(cls):
print("Launching ixfrdist..")
- conffile = 'ixfrdist.yml'
+ conffile = "ixfrdist.yml"
params = tuple([getattr(cls, param) for param in cls._config_params])
print(params)
- with open(conffile, 'w') as conf:
+ with open(conffile, "w") as conf:
conf.write("# Autogenerated by ixfrdisttests.py\n")
conf.write(cls._config_template % params)
conf.write("domains:\n")
for item in cls._config_domains:
- conf.write(" - domain: %s\n" % (item['domain']))
- conf.write(" master: %s\n" % (item['master']))
- if ('notify' in item) :
- conf.write(" notify: %s\n" % (item['notify']))
+ conf.write(" - domain: %s\n" % (item["domain"]))
+ conf.write(" master: %s\n" % (item["master"]))
+ if "notify" in item:
+ conf.write(" notify: %s\n" % (item["notify"]))
- ixfrdistcmd = [os.environ['IXFRDISTBIN'], '--config', conffile, '--debug']
+ ixfrdistcmd = [os.environ["IXFRDISTBIN"], "--config", conffile, "--debug"]
- logFile = 'ixfrdist.log'
- with open(logFile, 'w') as fdLog:
- cls._ixfrdist = subprocess.Popen(ixfrdistcmd, close_fds=True,
- stdout=fdLog, stderr=fdLog)
+ logFile = "ixfrdist.log"
+ with open(logFile, "w") as fdLog:
+ cls._ixfrdist = subprocess.Popen(ixfrdistcmd, close_fds=True, stdout=fdLog, stderr=fdLog)
- if 'IXFRDIST_FAST_TESTS' in os.environ:
+ if "IXFRDIST_FAST_TESTS" in os.environ:
delay = 0.5
else:
delay = cls._ixfrDistStartupDelay
@classmethod
def tearDownIXFRDist(cls):
- if 'IXFRDIST_FAST_TESTS' in os.environ:
+ if "IXFRDIST_FAST_TESTS" in os.environ:
delay = 0.1
else:
delay = 1.0
def setUp(self):
# This function is called before every tests
super(IXFRDistTest, self).setUp()
-
test.ixfr.case. 1234 TXT "Hello World"
case2.ixfr.case. 1234 TXT "Mixed Case"
case3.ixfr.case. 1234 TXT "MIXED CASE"
-"""
+""",
}
xfrServerPort = 4246
xfrServer = AXFRServer(xfrServerPort, zones)
+
class IXFRDistCaseSensitiveTXTTest(IXFRDistTest):
"""
This test verifies that TXT record comparisons are case-sensitive
global xfrServerPort
_xfrDone = 0
_config_domains = [
- {"domain" : "ixfr.case", "master" : "127.0.0.1:" + str(xfrServerPort)},
+ {"domain": "ixfr.case", "master": "127.0.0.1:" + str(xfrServerPort)},
]
_loaded_serials = []
xfrServer.moveToSerial(serial)
if notify:
- notif = dns.message.make_query('ixfr.case.', 'SOA')
+ notif = dns.message.make_query("ixfr.case.", "SOA")
notif.set_opcode(dns.opcode.NOTIFY)
notify_response = self.sendUDPQuery(notif)
assert notify_response.rcode() == dns.rcode.NOERROR
def get_current_serial():
- query = dns.message.make_query('ixfr.case.', 'SOA')
+ query = dns.message.make_query("ixfr.case.", "SOA")
response_message = self.sendUDPQuery(query)
if response_message.rcode() == dns.rcode.REFUSED:
return 0
- soa_rrset = response_message.find_rrset(dns.message.ANSWER, dns.name.from_text("ixfr.case."), dns.rdataclass.IN, dns.rdatatype.SOA)
+ soa_rrset = response_message.find_rrset(
+ dns.message.ANSWER, dns.name.from_text("ixfr.case."), dns.rdataclass.IN, dns.rdatatype.SOA
+ )
return soa_rrset[0].serial
attempts = 0
while attempts < timeout:
- print('attempts=%s timeout=%s' % (attempts, timeout))
+ print("attempts=%s timeout=%s" % (attempts, timeout))
servedSerial = get_current_serial()
- print('servedSerial=%s' % servedSerial)
+ print("servedSerial=%s" % servedSerial)
if servedSerial > serial:
raise AssertionError("Expected serial %d, got %d" % (serial, servedSerial))
if servedSerial == serial:
attempts = attempts + 1
time.sleep(1)
- raise AssertionError("Waited %d seconds for the serial to be updated to %d but the last served serial is still %d" % (timeout, serial, servedSerial))
+ raise AssertionError(
+ "Waited %d seconds for the serial to be updated to %d but the last served serial is still %d"
+ % (timeout, serial, servedSerial)
+ )
def checkTXTRecord(self, expected_txt_records):
"""
Check for the presence of specific TXT records in the zone using AXFR
"""
- query = dns.message.make_query('ixfr.case.', 'AXFR')
+ query = dns.message.make_query("ixfr.case.", "AXFR")
responses = self.sendTCPQueryMultiResponse(query, count=10)
found_txt_records = []
if found_name == expected_name and found_txt == expected_txt:
found = True
break
- self.assertTrue(found,
- f"TXT record '{expected_name}' with content '{expected_txt}' not found in AXFR. Found: {found_txt_records}")
+ self.assertTrue(
+ found,
+ f"TXT record '{expected_name}' with content '{expected_txt}' not found in AXFR. Found: {found_txt_records}",
+ )
def checkIXFRContainsTXTChange(self, fromserial, toserial, expected_removed=None, expected_added=None):
"""
self.assertEqual(soa_latest[0].serial, toserial)
- query = dns.message.make_query('ixfr.case.', 'IXFR')
+ query = dns.message.make_query("ixfr.case.", "IXFR")
query.authority = [soa_requested]
responses = self.sendTCPQueryMultiResponse(query, count=10) # Allow for multiple responses
if expected_removed:
for removed_txt in expected_removed:
- self.assertIn(removed_txt, found_removed,
- f"Expected removed TXT '{removed_txt}' not found in IXFR")
-
+ self.assertIn(removed_txt, found_removed, f"Expected removed TXT '{removed_txt}' not found in IXFR")
+
if expected_added:
for added_txt in expected_added:
- self.assertIn(added_txt, found_added,
- f"Expected added TXT '{added_txt}' not found in IXFR")
+ self.assertIn(added_txt, found_added, f"Expected added TXT '{added_txt}' not found in IXFR")
def test_a_first_version(self):
"""Test first version of zone and verify TXT record presence"""
self.waitUntilCorrectSerialIsLoaded(1)
- self.checkTXTRecord([('test.ixfr.case.', "Hello World")])
+ self.checkTXTRecord([("test.ixfr.case.", "Hello World")])
def test_b_case_change_lowercase(self):
"""Test that changing TXT from 'Hello World' to 'hello world' is detected"""
def test_d_multiple_txt_records_mixed_case(self):
"""Test multiple TXT records with different case variations"""
self.waitUntilCorrectSerialIsLoaded(4)
- self.checkIXFRContainsTXTChange(3, 4, expected_removed=["HELLO WORLD"], expected_added=["Hello World", "Mixed Case", "mixed case"])
+ self.checkIXFRContainsTXTChange(
+ 3, 4, expected_removed=["HELLO WORLD"], expected_added=["Hello World", "Mixed Case", "mixed case"]
+ )
def test_e_case_sensitive_update(self):
"""Test that updating a TXT record's case is properly handled"""
ns2.example. 4242 A 192.0.2.2
newrecord2.example. 8484 A 192.0.2.42
other.example. 1234 TXT "foo"
-"""
+""",
}
xfrServerPort = 4244
xfrServer = AXFRServer(xfrServerPort, zones)
+
class IXFRDistBasicTest(IXFRDistTest):
"""
This test makes sure that we correctly fetch a zone via AXFR, and provide the full AXFR and IXFR
_xfrDone = 0
_config_domains = [
# zone for actual XFR testing
- {"domain" : "example", "master" : "127.0.0.1:" + str(xfrServerPort), 'notify' : "127.0.0.1:" + str(xfrServerPort + 1)},
+ {
+ "domain": "example",
+ "master": "127.0.0.1:" + str(xfrServerPort),
+ "notify": "127.0.0.1:" + str(xfrServerPort + 1),
+ },
# bogus port is intentional - zone is intentionally unloadable
- {"domain" : "example2", "master" : "127.0.0.1:1"},
+ {"domain": "example2", "master": "127.0.0.1:1"},
# for testing how ixfrdist deals with getting the wrong zone on XFR
- {"domain" : "example4", "master" : '127.0.0.1:' + str(xfrServerPort)},
-
+ {"domain": "example4", "master": "127.0.0.1:" + str(xfrServerPort)},
]
_loaded_serials = []
xfrServer.moveToSerial(serial)
if notify:
- notif = dns.message.make_query('example.', 'SOA')
+ notif = dns.message.make_query("example.", "SOA")
notif.set_opcode(dns.opcode.NOTIFY)
notify_response = self.sendUDPQuery(notif)
assert notify_response.rcode() == dns.rcode.NOERROR
def get_current_serial():
- query = dns.message.make_query('example.', 'SOA')
+ query = dns.message.make_query("example.", "SOA")
response_message = self.sendUDPQuery(query)
if response_message.rcode() == dns.rcode.REFUSED:
return 0
- soa_rrset = response_message.find_rrset(dns.message.ANSWER, dns.name.from_text("example."), dns.rdataclass.IN, dns.rdatatype.SOA)
+ soa_rrset = response_message.find_rrset(
+ dns.message.ANSWER, dns.name.from_text("example."), dns.rdataclass.IN, dns.rdatatype.SOA
+ )
return soa_rrset[0].serial
attempts = 0
while attempts < timeout:
- print('attempts=%s timeout=%s' % (attempts, timeout))
+ print("attempts=%s timeout=%s" % (attempts, timeout))
servedSerial = get_current_serial()
- print('servedSerial=%s' % servedSerial)
+ print("servedSerial=%s" % servedSerial)
if servedSerial > serial:
raise AssertionError("Expected serial %d, got %d" % (serial, servedSerial))
if servedSerial == serial:
attempts = attempts + 1
time.sleep(1)
- raise AssertionError("Waited %d seconds for the serial to be updated to %d but the last served serial is still %d" % (timeout, serial, servedSerial))
+ raise AssertionError(
+ "Waited %d seconds for the serial to be updated to %d but the last served serial is still %d"
+ % (timeout, serial, servedSerial)
+ )
def checkFullZone(self, serial):
global zones
zone = []
for i in dns.zone.from_text(zones[serial], relativize=False).iterate_rdatasets():
n, rds = i
- rrs=dns.rrset.RRset(n, rds.rdclass, rds.rdtype)
+ rrs = dns.rrset.RRset(n, rds.rdclass, rds.rdtype)
rrs.update(rds)
zone.append(rrs)
- expected =[[zone[0]], sorted(zone[1:], key=lambda rrset: (rrset.name, rrset.rdtype)), [zone[0]]] # AXFRs are SOA-wrapped
+ expected = [
+ [zone[0]],
+ sorted(zone[1:], key=lambda rrset: (rrset.name, rrset.rdtype)),
+ [zone[0]],
+ ] # AXFRs are SOA-wrapped
- query = dns.message.make_query('example.', 'AXFR')
- res = self.sendTCPQueryMultiResponse(query, count=len(expected)+1) # +1 for trailing data check
+ query = dns.message.make_query("example.", "AXFR")
+ res = self.sendTCPQueryMultiResponse(query, count=len(expected) + 1) # +1 for trailing data check
answers = [r.answer for r in res]
answers[1].sort(key=lambda rrset: (rrset.name, rrset.rdtype))
self.assertEqual(answers, expected)
self.assertEqual(soa_latest[0].serial, toserial)
- query = dns.message.make_query('example.', 'IXFR')
+ query = dns.message.make_query("example.", "IXFR")
query.authority = [soa_requested]
expected = []
- expected.append([soa_latest]) #latest SOA
+ expected.append([soa_latest]) # latest SOA
- def pairwise(iterable): # itertools.pairwise exists in 3.10, but until then...
+ def pairwise(iterable): # itertools.pairwise exists in 3.10, but until then...
# pairwise('ABCDEFG') --> AB BC CD DE EF FG
a, b = itertools.tee(iterable)
next(b, None)
added = [r for r in new_records if r not in old_records]
removed = [r for r in old_records if r not in new_records]
- expected.append([xfrServer._getSOAForSerial(serial_pair[0])]) # old SOA
- if removed: expected.append(removed) # removed records from old SOA (sendTCPQueryMultiResponse skips if empty)
- expected.append([xfrServer._getSOAForSerial(serial_pair[1])]) # new SOA
- if added: expected.append(added) # added records in new SOA (sendTCPQueryMultiResponse skips if empty)
+ expected.append([xfrServer._getSOAForSerial(serial_pair[0])]) # old SOA
+ if removed:
+ expected.append(removed) # removed records from old SOA (sendTCPQueryMultiResponse skips if empty)
+ expected.append([xfrServer._getSOAForSerial(serial_pair[1])]) # new SOA
+ if added:
+ expected.append(added) # added records in new SOA (sendTCPQueryMultiResponse skips if empty)
- expected.append([soa_latest]) # latest SOA
+ expected.append([soa_latest]) # latest SOA
if not found_starting_version:
- raise AssertionError("Did not find zone version with requested serial {fromserial}, impossible to IXFR scenario?")
+ raise AssertionError(
+ "Did not find zone version with requested serial {fromserial}, impossible to IXFR scenario?"
+ )
- res = self.sendTCPQueryMultiResponse(query, count=len(expected)+1) # +1 for trailing data check
+ res = self.sendTCPQueryMultiResponse(query, count=len(expected) + 1) # +1 for trailing data check
answers = [r.answer for r in res]
# answers[1].sort(key=lambda rrset: (rrset.name, rrset.rdtype))
pos = pos + 1
answerPos = answerPos + 1
-
def test_a_XFR(self):
self.waitUntilCorrectSerialIsLoaded(1)
self.checkFullZone(1)
self.waitUntilCorrectSerialIsLoaded(2)
self.checkFullZone(2)
- self.checkIXFR(1,2)
+ self.checkIXFR(1, 2)
# _b_ because we expect post-XFR testing state
def test_b_UDP_SOA_existing(self):
- query = dns.message.make_query('example.', 'SOA')
+ query = dns.message.make_query("example.", "SOA")
expected = dns.message.make_response(query)
expected.flags |= dns.flags.AA
expected.answer.append(xfrServer._getSOAForSerial(2))
pos = pos + 1
def test_b_UDP_SOA_not_loaded(self):
- query = dns.message.make_query('example2.', 'SOA')
+ query = dns.message.make_query("example2.", "SOA")
expected = dns.message.make_response(query)
expected.set_rcode(dns.rcode.REFUSED)
self.assertEqual(expected, response)
def test_b_UDP_SOA_not_configured(self):
- query = dns.message.make_query('example3.', 'SOA')
+ query = dns.message.make_query("example3.", "SOA")
expected = dns.message.make_response(query)
expected.set_rcode(dns.rcode.REFUSED)
def test_c_IXFR_multi(self):
self.waitUntilCorrectSerialIsLoaded(3)
self.checkFullZone(3)
- self.checkIXFR(2,3)
- self.checkIXFR(1,3)
+ self.checkIXFR(2, 3)
+ self.checkIXFR(1, 3)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("127.0.0.1", xfrServerPort + 1))
received = dns.message.from_wire(data)
sock.close()
- notif = dns.message.make_query('example.', 'SOA')
+ notif = dns.message.make_query("example.", "SOA")
notif.set_opcode(dns.opcode.NOTIFY)
notif.flags |= dns.flags.AA
notif.flags &= ~dns.flags.RD
self.assertEqual(received, notif)
self.checkFullZone(4)
- self.checkIXFR(3,4)
- self.checkIXFR(2,4)
- self.checkIXFR(1,4)
+ self.checkIXFR(3, 4)
+ self.checkIXFR(2, 4)
+ self.checkIXFR(1, 4)
xfrServerPort = 4244
+
class IXFRDistStatsTest(IXFRDistTest):
"""
This test makes sure we have statistics in ixfrdist
"""
- webserver_address = '127.0.0.1:8080'
+ webserver_address = "127.0.0.1:8080"
- _config_params = ['_ixfrDistPort', 'webserver_address']
+ _config_params = ["_ixfrDistPort", "webserver_address"]
_config_template = """
listen:
webserver-address: %s
"""
- _config_domains = [{'domain' : 'example', 'master' : '127.0.0.1:' + str(xfrServerPort)}]
-
- metric_prog_stats = ["ixfrdist_uptime_seconds", "ixfrdist_domains",
- "ixfrdist_unknown_domain_inqueries_total",
- "ixfrdist_sys_msec", "ixfrdist_user_msec",
- "ixfrdist_real_memory_usage",
- "ixfrdist_fd_usage",
- "ixfrdist_notimp"]
- metric_domain_stats = ["ixfrdist_soa_serial", "ixfrdist_soa_checks_total",
- "ixfrdist_soa_checks_failed_total",
- "ixfrdist_soa_inqueries_total",
- "ixfrdist_axfr_inqueries_total", "ixfrdist_axfr_failures_total",
- "ixfrdist_ixfr_inqueries_total", "ixfrdist_ixfr_failures_total"]
+ _config_domains = [{"domain": "example", "master": "127.0.0.1:" + str(xfrServerPort)}]
+
+ metric_prog_stats = [
+ "ixfrdist_uptime_seconds",
+ "ixfrdist_domains",
+ "ixfrdist_unknown_domain_inqueries_total",
+ "ixfrdist_sys_msec",
+ "ixfrdist_user_msec",
+ "ixfrdist_real_memory_usage",
+ "ixfrdist_fd_usage",
+ "ixfrdist_notimp",
+ ]
+ metric_domain_stats = [
+ "ixfrdist_soa_serial",
+ "ixfrdist_soa_checks_total",
+ "ixfrdist_soa_checks_failed_total",
+ "ixfrdist_soa_inqueries_total",
+ "ixfrdist_axfr_inqueries_total",
+ "ixfrdist_axfr_failures_total",
+ "ixfrdist_ixfr_inqueries_total",
+ "ixfrdist_ixfr_failures_total",
+ ]
@classmethod
def setUpClass(cls):
def checkPrometheusContentPromtool(self, content):
output = None
try:
- testcmd = ['promtool', 'check', 'metrics']
- process = subprocess.Popen(testcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
+ testcmd = ["promtool", "check", "metrics"]
+ process = subprocess.Popen(
+ testcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True
+ )
output = process.communicate(input=content)
except subprocess.CalledProcessError as exc:
- raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, process.output))
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, process.output))
# promtool may return 3 because of the "_total" suffix warnings
if not process.returncode in [0, 3]:
- raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, output))
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, output))
for line in output[0].splitlines():
- if line.endswith(b"should have \"_total\" suffix"):
+ if line.endswith(b'should have "_total" suffix'):
continue
- raise AssertionError('%s returned an unexpected output. Faulty line is "%s", complete content is "%s"' % (testcmd, line, output))
+ raise AssertionError(
+ '%s returned an unexpected output. Faulty line is "%s", complete content is "%s"'
+ % (testcmd, line, output)
+ )
def test_program_stats_exist(self):
- res = requests.get('http://{}/metrics'.format(self.webserver_address))
+ res = requests.get("http://{}/metrics".format(self.webserver_address))
self.assertEqual(res.status_code, 200)
for line in res.text.splitlines():
if line[0] == "#":
if "{" in line:
continue
tokens = line.split(" ")
- self.assertIn(tokens[0],
- self.metric_prog_stats + self.metric_domain_stats)
- if tokens[0] == 'ixfrdist_unknown_domain_inqueries_total':
+ self.assertIn(tokens[0], self.metric_prog_stats + self.metric_domain_stats)
+ if tokens[0] == "ixfrdist_unknown_domain_inqueries_total":
self.assertEqual(int(tokens[1]), 0)
self.checkPrometheusContentPromtool(res.content)
def test_registered(self):
- res = requests.get('http://{}/metrics'.format(self.webserver_address))
+ res = requests.get("http://{}/metrics".format(self.webserver_address))
self.assertEqual(res.status_code, 200)
for line in res.text.splitlines():
- if line.startswith('ixfrdist_domains'):
- self.assertEqual(line, 'ixfrdist_domains 1')
+ if line.startswith("ixfrdist_domains"):
+ self.assertEqual(line, "ixfrdist_domains 1")
continue
if line[0] == "#":
continue
self.assertIn(line.split("{")[0], self.metric_domain_stats)
def test_metrics_have_help(self):
- res = requests.get('http://{}/metrics'.format(self.webserver_address))
+ res = requests.get("http://{}/metrics".format(self.webserver_address))
self.assertEqual(res.status_code, 200)
for s in self.metric_prog_stats + self.metric_domain_stats:
- self.assertIn('# HELP {}'.format(s), res.text)
+ self.assertIn("# HELP {}".format(s), res.text)
def test_metrics_have_type(self):
- res = requests.get('http://{}/metrics'.format(self.webserver_address))
+ res = requests.get("http://{}/metrics".format(self.webserver_address))
self.assertEqual(res.status_code, 200)
for s in self.metric_prog_stats + self.metric_domain_stats:
- self.assertIn('# TYPE {}'.format(s), res.text)
+ self.assertIn("# TYPE {}".format(s), res.text)
def test_missing_metrics(self):
all_metrics = set()
- res = requests.get('http://{}/metrics'.format(self.webserver_address))
+ res = requests.get("http://{}/metrics".format(self.webserver_address))
self.assertEqual(res.status_code, 200)
for line in res.text.splitlines():
continue
all_metrics.add(line.split(" ")[0])
- should_have_metrics = set(self.metric_prog_stats +
- self.metric_domain_stats)
+ should_have_metrics = set(self.metric_prog_stats + self.metric_domain_stats)
unknown_metrics = all_metrics - should_have_metrics
# TODO use dnspython to parse/check
-MESSAGE=b"\xaf\x03\x00\x20\x00\x01\x00\x00\x00\x00\x00\x01\x04\x75\x6e\x69\x74\x04\x74\x65\x73\x74\x00\x00\x06\x00\x01\x00\x00\x29\x10\x00\x00\x64\x00\x00\x00\x10\x00\x0a\x00\x08\x39\x70\xad\xaf\xca\xa8\x96\xca\x00\x64\x00\x00"
+MESSAGE = b"\xaf\x03\x00\x20\x00\x01\x00\x00\x00\x00\x00\x01\x04\x75\x6e\x69\x74\x04\x74\x65\x73\x74\x00\x00\x06\x00\x01\x00\x00\x29\x10\x00\x00\x64\x00\x00\x00\x10\x00\x0a\x00\x08\x39\x70\xad\xaf\xca\xa8\x96\xca\x00\x64\x00\x00"
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-sock.bind(("127.0.0.1",5502))
+sock.bind(("127.0.0.1", 5502))
sock.sendto(MESSAGE, ("127.0.0.1", 5501))
data, addr = sock.recvfrom(512)
# make sure data is correct
-EXPECT=b"\xaf\x03\x84\x00\x00\x01\x00\x00\x00\x00\x00\x01\x04\x75\x6e\x69\x74\x04\x74\x65\x73\x74\x00\x00\x06\x00\x01\x00\x00\x29\x04\xd0\x01\x00\x00\x00\x00\x00"
+EXPECT = b"\xaf\x03\x84\x00\x00\x01\x00\x00\x00\x00\x00\x01\x04\x75\x6e\x69\x74\x04\x74\x65\x73\x74\x00\x00\x06\x00\x01\x00\x00\x29\x04\xd0\x01\x00\x00\x00\x00\x00"
-if (data != EXPECT):
- print("Invalid EDNS response, expected extended RCODE=BADVERS, no SOA, and OPT version 0")
+if data != EXPECT:
+ print("Invalid EDNS response, expected extended RCODE=BADVERS, no SOA, and OPT version 0")
else:
- print("EDNS response OK")
+ print("EDNS response OK")
import sys
line = sys.stdin.readline()
-items = line.split('\t')
-if (items[0] != 'HELO'):
- print('LOG\tGot unexpected greeting\t%s' % line)
+items = line.split("\t")
+if items[0] != "HELO":
+ print("LOG\tGot unexpected greeting\t%s" % line)
# TOLO
-print('OK\tTest backend firing up')
+print("OK\tTest backend firing up")
while True:
line = sys.stdin.readline()
- items = line.split('\t')
+ items = line.split("\t")
sys.stderr.write(line)
if len(items) < 6:
- print('LOG\tGot an unparseable line')
- print('LOG\t%s' % line)
- print('END')
+ print("LOG\tGot an unparseable line")
+ print("LOG\t%s" % line)
+ print("END")
continue
what, qname, qclass, qtype, id, ip = items
- if qtype in ['SOA', 'ANY'] and qname == 'example2.com':
- print('DATA\t%s\t%s\tSOA\t300\t-1\tns1.example.com ahu.example.com 2008080300 1800 3600 604800 3600' % (qname, qclass))
+ if qtype in ["SOA", "ANY"] and qname == "example2.com":
+ print(
+ "DATA\t%s\t%s\tSOA\t300\t-1\tns1.example.com ahu.example.com 2008080300 1800 3600 604800 3600"
+ % (qname, qclass)
+ )
- if qtype in ['NS', 'ANY'] and qname == 'example2.com':
- print('DATA\t%s\t%s\tNS\t3600\t-1\tns1.example.com' % (qname, qclass))
- print('DATA\t%s\t%s\tNS\t3600\t-1\tns2.example.com' % (qname, qclass))
+ if qtype in ["NS", "ANY"] and qname == "example2.com":
+ print("DATA\t%s\t%s\tNS\t3600\t-1\tns1.example.com" % (qname, qclass))
+ print("DATA\t%s\t%s\tNS\t3600\t-1\tns2.example.com" % (qname, qclass))
- if qtype in ['A', 'ANY'] and qname.endswith('example2.com'):
+ if qtype in ["A", "ANY"] and qname.endswith("example2.com"):
# We were asked a specific record
- print('DATA\t%s\t%s\tCNAME\t3600\t-1\twww.example.com.' % (qname, qclass))
+ print("DATA\t%s\t%s\tCNAME\t3600\t-1\twww.example.com." % (qname, qclass))
- print('END')
+ print("END")
from recursortests import RecursorTest
import os
+
class BasicDNSSEC(RecursorTest):
__test__ = False
_config_template = """dnssec=validate"""
@classmethod
def setUp(cls):
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
cls.wipeRecursorCache(confdir)
def testSecureAnswer(self):
- res = self.sendQuery('ns.secure.example.', 'A')
- expected = dns.rrset.from_text('ns.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
+ res = self.sendQuery("ns.secure.example.", "A")
+ expected = dns.rrset.from_text(
+ "ns.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMatchingRRSIGInAnswer(res, expected)
self.assertMessageIsAuthenticated(res)
def testInsecureAnswer(self):
- res = self.sendQuery('node1.insecure.example.', 'A')
+ res = self.sendQuery("node1.insecure.example.", "A")
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
# now we request the DS for insecure.example., which does not exist,
# to check that we correctly get the SOA and not just the denial proof
# that the recursor received on the delegation from example. to insecure.example.
- res = self.sendQuery('insecure.example.', 'DS')
+ res = self.sendQuery("insecure.example.", "DS")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMessageIsAuthenticated(res)
self.assertAuthorityHasSOA(res)
def testBogusAnswer(self):
- res = self.sendQuery('ted.bogus.example.', 'A')
+ res = self.sendQuery("ted.bogus.example.", "A")
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertAnswerEmpty(res)
def testSecureNXDOMAIN(self):
- res = self.sendQuery('nxdomain.secure.example.', 'A')
+ res = self.sendQuery("nxdomain.secure.example.", "A")
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
def testInsecureNXDOMAIN(self):
- res = self.sendQuery('nxdomain.insecure.example.', 'A')
+ res = self.sendQuery("nxdomain.insecure.example.", "A")
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
def testBogusNXDOMAIN(self):
- res = self.sendQuery('nxdomain.bogus.example.', 'A')
+ res = self.sendQuery("nxdomain.bogus.example.", "A")
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
def testSecureOptoutAnswer(self):
- res = self.sendQuery('node1.secure.optout.example.', 'A')
- expected = dns.rrset.from_text('node1.secure.optout.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.8')
+ res = self.sendQuery("node1.secure.optout.example.", "A")
+ expected = dns.rrset.from_text("node1.secure.optout.example.", 0, dns.rdataclass.IN, "A", "192.0.2.8")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMatchingRRSIGInAnswer(res, expected)
self.assertMessageIsAuthenticated(res)
def testInsecureOptoutAnswer(self):
- res = self.sendQuery('node1.insecure.optout.example.', 'A')
+ res = self.sendQuery("node1.insecure.optout.example.", "A")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertNoRRSIGsInAnswer(res)
def testSecureSubtreeInZoneAnswer(self):
- res = self.sendQuery('host1.sub.secure.example.', 'A')
- expected = dns.rrset.from_text('host1.sub.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.11')
+ res = self.sendQuery("host1.sub.secure.example.", "A")
+ expected = dns.rrset.from_text("host1.sub.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.11")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMatchingRRSIGInAnswer(res, expected)
self.assertMessageIsAuthenticated(res)
def testSecureSubtreeInZoneNXDOMAIN(self):
- res = self.sendQuery('host2.sub.secure.example.', 'A')
+ res = self.sendQuery("host2.sub.secure.example.", "A")
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertMessageIsAuthenticated(res)
def testSecureWildcardAnswer(self):
- res = self.sendQuery('something.wildcard.secure.example.', 'A')
- expected = dns.rrset.from_text('something.wildcard.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.10')
+ res = self.sendQuery("something.wildcard.secure.example.", "A")
+ expected = dns.rrset.from_text("something.wildcard.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.10")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMatchingRRSIGInAnswer(res, expected)
self.assertMessageIsAuthenticated(res)
def testSecureCNAMEWildCardAnswer(self):
- res = self.sendQuery('something.cnamewildcard.secure.example.', 'A')
- expectedCNAME = dns.rrset.from_text('something.cnamewildcard.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'host1.secure.example.')
- expectedA = dns.rrset.from_text('host1.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.2')
+ res = self.sendQuery("something.cnamewildcard.secure.example.", "A")
+ expectedCNAME = dns.rrset.from_text(
+ "something.cnamewildcard.secure.example.", 0, dns.rdataclass.IN, "CNAME", "host1.secure.example."
+ )
+ expectedA = dns.rrset.from_text("host1.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.2")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMatchingRRSIGInAnswer(res, expectedCNAME)
def testSecureCNAMEWildCardNXDOMAIN(self):
# the answer to this query reaches the UDP truncation threshold, so let's use TCP
- res = self.sendQuery('something.cnamewildcardnxdomain.secure.example.', 'A', useTCP=True)
- expectedCNAME = dns.rrset.from_text('something.cnamewildcardnxdomain.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'doesnotexist.secure.example.')
+ res = self.sendQuery("something.cnamewildcardnxdomain.secure.example.", "A", useTCP=True)
+ expectedCNAME = dns.rrset.from_text(
+ "something.cnamewildcardnxdomain.secure.example.",
+ 0,
+ dns.rdataclass.IN,
+ "CNAME",
+ "doesnotexist.secure.example.",
+ )
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertMatchingRRSIGInAnswer(res, expectedCNAME)
self.assertMessageIsAuthenticated(res)
def testSecureNoData(self):
- res = self.sendQuery('host1.secure.example.', 'AAAA')
+ res = self.sendQuery("host1.secure.example.", "AAAA")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnswerEmpty(res)
self.assertMessageIsAuthenticated(res)
def testSecureCNAMENoData(self):
- res = self.sendQuery('cname.secure.example.', 'AAAA')
- expectedCNAME = dns.rrset.from_text('cname.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'host1.secure.example.')
+ res = self.sendQuery("cname.secure.example.", "AAAA")
+ expectedCNAME = dns.rrset.from_text(
+ "cname.secure.example.", 0, dns.rdataclass.IN, "CNAME", "host1.secure.example."
+ )
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMatchingRRSIGInAnswer(res, expectedCNAME)
self.assertMessageIsAuthenticated(res)
def testSecureWildCardNoData(self):
- res = self.sendQuery('something.cnamewildcard.secure.example.', 'AAAA')
- expectedCNAME = dns.rrset.from_text('something.cnamewildcard.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'host1.secure.example.')
+ res = self.sendQuery("something.cnamewildcard.secure.example.", "AAAA")
+ expectedCNAME = dns.rrset.from_text(
+ "something.cnamewildcard.secure.example.", 0, dns.rdataclass.IN, "CNAME", "host1.secure.example."
+ )
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMatchingRRSIGInAnswer(res, expectedCNAME)
self.assertMessageIsAuthenticated(res)
def testInsecureToSecureCNAMEAnswer(self):
- res = self.sendQuery('cname-to-secure.insecure.example.', 'A')
- expectedA = dns.rrset.from_text('host1.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.2')
- expectedCNAME = dns.rrset.from_text('cname-to-secure.insecure.example.', 0, dns.rdataclass.IN, 'CNAME', 'host1.secure.example.')
+ res = self.sendQuery("cname-to-secure.insecure.example.", "A")
+ expectedA = dns.rrset.from_text("host1.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.2")
+ expectedCNAME = dns.rrset.from_text(
+ "cname-to-secure.insecure.example.", 0, dns.rdataclass.IN, "CNAME", "host1.secure.example."
+ )
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA"], ["DO"])
self.assertRRsetInAnswer(res, expectedCNAME)
self.assertMatchingRRSIGInAnswer(res, expectedA)
def testSecureToInsecureCNAMEAnswer(self):
- res = self.sendQuery('cname-to-insecure.secure.example.', 'A')
- expectedA = dns.rrset.from_text('node1.insecure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.6')
- expectedCNAME = dns.rrset.from_text('cname-to-insecure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'node1.secure.example.')
+ res = self.sendQuery("cname-to-insecure.secure.example.", "A")
+ expectedA = dns.rrset.from_text("node1.insecure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.6")
+ expectedCNAME = dns.rrset.from_text(
+ "cname-to-insecure.secure.example.", 0, dns.rdataclass.IN, "CNAME", "node1.secure.example."
+ )
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA"], ["DO"])
self.assertRRsetInAnswer(res, expectedA)
self.assertMatchingRRSIGInAnswer(res, expectedCNAME)
def testSecureDNAMEToSecureAnswer(self):
- res = self.sendQuery('host1.dname-secure.secure.example.', 'A')
- expectedDNAME = dns.rrset.from_text('dname-secure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.')
- expectedCNAME = dns.rrset.from_text('host1.dname-secure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'host1.dname-secure.example.')
- expectedA = dns.rrset.from_text('host1.dname-secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.21')
+ res = self.sendQuery("host1.dname-secure.secure.example.", "A")
+ expectedDNAME = dns.rrset.from_text(
+ "dname-secure.secure.example.", 0, dns.rdataclass.IN, "DNAME", "dname-secure.example."
+ )
+ expectedCNAME = dns.rrset.from_text(
+ "host1.dname-secure.secure.example.", 0, dns.rdataclass.IN, "CNAME", "host1.dname-secure.example."
+ )
+ expectedA = dns.rrset.from_text("host1.dname-secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.21")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'AD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA", "AD"], ["DO"])
self.assertRRsetInAnswer(res, expectedA)
self.assertRRsetInAnswer(res, expectedCNAME)
self.assertRRsetInAnswer(res, expectedDNAME)
self.assertMatchingRRSIGInAnswer(res, expectedA)
def testSecureDNAMEToSecureNXDomain(self):
- res = self.sendQuery('nxd.dname-secure.secure.example.', 'A')
- expectedDNAME = dns.rrset.from_text('dname-secure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.')
- expectedCNAME = dns.rrset.from_text('nxd.dname-secure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'nxd.dname-secure.example.')
+ res = self.sendQuery("nxd.dname-secure.secure.example.", "A")
+ expectedDNAME = dns.rrset.from_text(
+ "dname-secure.secure.example.", 0, dns.rdataclass.IN, "DNAME", "dname-secure.example."
+ )
+ expectedCNAME = dns.rrset.from_text(
+ "nxd.dname-secure.secure.example.", 0, dns.rdataclass.IN, "CNAME", "nxd.dname-secure.example."
+ )
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'AD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA", "AD"], ["DO"])
self.assertRRsetInAnswer(res, expectedCNAME)
self.assertRRsetInAnswer(res, expectedDNAME)
self.assertMatchingRRSIGInAnswer(res, expectedDNAME)
def testSecureDNAMEToInsecureAnswer(self):
- res = self.sendQuery('node1.dname-insecure.secure.example.', 'A')
- expectedDNAME = dns.rrset.from_text('dname-insecure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'insecure.example.')
- expectedCNAME = dns.rrset.from_text('node1.dname-insecure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'node1.insecure.example.')
- expectedA = dns.rrset.from_text('node1.insecure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.6')
+ res = self.sendQuery("node1.dname-insecure.secure.example.", "A")
+ expectedDNAME = dns.rrset.from_text(
+ "dname-insecure.secure.example.", 0, dns.rdataclass.IN, "DNAME", "insecure.example."
+ )
+ expectedCNAME = dns.rrset.from_text(
+ "node1.dname-insecure.secure.example.", 0, dns.rdataclass.IN, "CNAME", "node1.insecure.example."
+ )
+ expectedA = dns.rrset.from_text("node1.insecure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.6")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA"], ["DO"])
self.assertRRsetInAnswer(res, expectedA)
self.assertRRsetInAnswer(res, expectedCNAME)
self.assertRRsetInAnswer(res, expectedDNAME)
self.assertMatchingRRSIGInAnswer(res, expectedDNAME)
def testSecureDNAMEToInsecureNXDomain(self):
- res = self.sendQuery('nxd.dname-insecure.secure.example.', 'A')
- expectedDNAME = dns.rrset.from_text('dname-insecure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'insecure.example.')
- expectedCNAME = dns.rrset.from_text('nxd.dname-insecure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'nxd.insecure.example.')
+ res = self.sendQuery("nxd.dname-insecure.secure.example.", "A")
+ expectedDNAME = dns.rrset.from_text(
+ "dname-insecure.secure.example.", 0, dns.rdataclass.IN, "DNAME", "insecure.example."
+ )
+ expectedCNAME = dns.rrset.from_text(
+ "nxd.dname-insecure.secure.example.", 0, dns.rdataclass.IN, "CNAME", "nxd.insecure.example."
+ )
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA"], ["DO"])
self.assertRRsetInAnswer(res, expectedCNAME)
self.assertRRsetInAnswer(res, expectedDNAME)
self.assertMatchingRRSIGInAnswer(res, expectedDNAME)
def testSecureDNAMEToBogusAnswer(self):
- res = self.sendQuery('ted.dname-bogus.secure.example.', 'A')
+ res = self.sendQuery("ted.dname-bogus.secure.example.", "A")
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertAnswerEmpty(res)
def testSecureDNAMEToBogusNXDomain(self):
- res = self.sendQuery('nxd.dname-bogus.secure.example.', 'A')
+ res = self.sendQuery("nxd.dname-bogus.secure.example.", "A")
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertAnswerEmpty(res)
def testInsecureDNAMEtoSecureAnswer(self):
- res = self.sendQuery('host1.dname-to-secure.insecure.example.', 'A')
- expectedDNAME = dns.rrset.from_text('dname-to-secure.insecure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.')
- expectedCNAME = dns.rrset.from_text('host1.dname-to-secure.insecure.example.', 0, dns.rdataclass.IN, 'CNAME', 'host1.dname-secure.example.')
- expectedA = dns.rrset.from_text('host1.dname-secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.21')
+ res = self.sendQuery("host1.dname-to-secure.insecure.example.", "A")
+ expectedDNAME = dns.rrset.from_text(
+ "dname-to-secure.insecure.example.", 0, dns.rdataclass.IN, "DNAME", "dname-secure.example."
+ )
+ expectedCNAME = dns.rrset.from_text(
+ "host1.dname-to-secure.insecure.example.", 0, dns.rdataclass.IN, "CNAME", "host1.dname-secure.example."
+ )
+ expectedA = dns.rrset.from_text("host1.dname-secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.21")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA"], ["DO"])
self.assertRRsetInAnswer(res, expectedA)
self.assertRRsetInAnswer(res, expectedCNAME)
self.assertRRsetInAnswer(res, expectedDNAME)
self.assertMatchingRRSIGInAnswer(res, expectedA)
def testSecureDNAMEToSecureCNAMEAnswer(self):
- res = self.sendQuery('cname-to-secure.dname-secure.secure.example.', 'A')
-
- expectedDNAME = dns.rrset.from_text('dname-secure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.')
- expectedCNAME1 = dns.rrset.from_text('cname-to-secure.dname-secure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'cname-to-secure.dname-secure.example.')
- expectedCNAME2 = dns.rrset.from_text('cname-to-secure.dname-secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'host1.secure.example.')
- expectedA = dns.rrset.from_text('host1.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.2')
+ res = self.sendQuery("cname-to-secure.dname-secure.secure.example.", "A")
+
+ expectedDNAME = dns.rrset.from_text(
+ "dname-secure.secure.example.", 0, dns.rdataclass.IN, "DNAME", "dname-secure.example."
+ )
+ expectedCNAME1 = dns.rrset.from_text(
+ "cname-to-secure.dname-secure.secure.example.",
+ 0,
+ dns.rdataclass.IN,
+ "CNAME",
+ "cname-to-secure.dname-secure.example.",
+ )
+ expectedCNAME2 = dns.rrset.from_text(
+ "cname-to-secure.dname-secure.example.", 0, dns.rdataclass.IN, "CNAME", "host1.secure.example."
+ )
+ expectedA = dns.rrset.from_text("host1.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.2")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'AD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA", "AD"], ["DO"])
self.assertRRsetInAnswer(res, expectedA)
self.assertRRsetInAnswer(res, expectedCNAME1)
self.assertRRsetInAnswer(res, expectedCNAME2)
self.assertMatchingRRSIGInAnswer(res, expectedA)
def testSecureDNAMEToInsecureCNAMEAnswer(self):
- res = self.sendQuery('cname-to-insecure.dname-secure.secure.example.', 'A')
-
- expectedDNAME = dns.rrset.from_text('dname-secure.secure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.')
- expectedCNAME1 = dns.rrset.from_text('cname-to-insecure.dname-secure.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'cname-to-insecure.dname-secure.example.')
- expectedCNAME2 = dns.rrset.from_text('cname-to-insecure.dname-secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'node1.insecure.example.')
- expectedA = dns.rrset.from_text('node1.insecure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.6')
+ res = self.sendQuery("cname-to-insecure.dname-secure.secure.example.", "A")
+
+ expectedDNAME = dns.rrset.from_text(
+ "dname-secure.secure.example.", 0, dns.rdataclass.IN, "DNAME", "dname-secure.example."
+ )
+ expectedCNAME1 = dns.rrset.from_text(
+ "cname-to-insecure.dname-secure.secure.example.",
+ 0,
+ dns.rdataclass.IN,
+ "CNAME",
+ "cname-to-insecure.dname-secure.example.",
+ )
+ expectedCNAME2 = dns.rrset.from_text(
+ "cname-to-insecure.dname-secure.example.", 0, dns.rdataclass.IN, "CNAME", "node1.insecure.example."
+ )
+ expectedA = dns.rrset.from_text("node1.insecure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.6")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA"], ["DO"])
self.assertRRsetInAnswer(res, expectedA)
self.assertRRsetInAnswer(res, expectedCNAME1)
self.assertRRsetInAnswer(res, expectedCNAME2)
self.assertMatchingRRSIGInAnswer(res, expectedDNAME)
def testSecureDNAMEToBogusCNAMEAnswer(self):
- res = self.sendQuery('cname-to-bogus.dname-secure.secure.example.', 'A')
+ res = self.sendQuery("cname-to-bogus.dname-secure.secure.example.", "A")
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertAnswerEmpty(res)
def testInsecureDNAMEtoSecureNXDomain(self):
- res = self.sendQuery('nxd.dname-to-secure.insecure.example.', 'A')
- expectedDNAME = dns.rrset.from_text('dname-to-secure.insecure.example.', 0, dns.rdataclass.IN, 'DNAME', 'dname-secure.example.')
- expectedCNAME = dns.rrset.from_text('nxd.dname-to-secure.insecure.example.', 0, dns.rdataclass.IN, 'CNAME', 'nxd.dname-secure.example.')
+ res = self.sendQuery("nxd.dname-to-secure.insecure.example.", "A")
+ expectedDNAME = dns.rrset.from_text(
+ "dname-to-secure.insecure.example.", 0, dns.rdataclass.IN, "DNAME", "dname-secure.example."
+ )
+ expectedCNAME = dns.rrset.from_text(
+ "nxd.dname-to-secure.insecure.example.", 0, dns.rdataclass.IN, "CNAME", "nxd.dname-secure.example."
+ )
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA"], ["DO"])
self.assertRRsetInAnswer(res, expectedCNAME)
self.assertRRsetInAnswer(res, expectedDNAME)
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-""" Class to implement draft-ietf-dnsop-edns-client-subnet (previously known as
+"""Class to implement draft-ietf-dnsop-edns-client-subnet (previously known as
draft-vandergaast-edns-client-subnet.
The contained class supports both IPv4 and IPv6 addresses.
Requirements:
dnspython (http://www.dnspython.org/)
"""
+
from __future__ import print_function
from __future__ import division
n = socket.inet_pton(family, ip)
if family == socket.AF_INET6:
f = FAMILY_IPV6
- hi, lo = struct.unpack('!QQ', n)
+ hi, lo = struct.unpack("!QQ", n)
ip = hi << 64 | lo
elif family == socket.AF_INET:
f = FAMILY_IPV4
- ip = struct.unpack('!L', n)[0]
+ ip = struct.unpack("!L", n)[0]
except Exception:
pass
ip = self.ip >> bits - self.mask
- if (self.mask % 8 != 0):
+ if self.mask % 8 != 0:
ip = ip << 8 - (self.mask % 8)
return ip
def is_draft(self):
- """" Determines whether this instance is using the draft option code """
+ """ " Determines whether this instance is using the draft option code"""
return self.option == DRAFT_OPTION_CODE
def to_wire(self, file=None):
mask_bits = self.mask
if mask_bits % 8 != 0:
- mask_bits += 8 - (self.mask % 8)
+ mask_bits += 8 - (self.mask % 8)
if self.family == FAMILY_IPV4:
test = struct.pack("!L", ip)
elif self.family == FAMILY_IPV6:
- test = struct.pack("!QQ", ip >> 64, ip & (2 ** 64 - 1))
- test = test[-(mask_bits // 8):]
+ test = struct.pack("!QQ", ip >> 64, ip & (2**64 - 1))
+ test = test[-(mask_bits // 8) :]
format = "!HBB%ds" % (mask_bits // 8)
data = struct.pack(format, self.family, self.mask, self.scope, test)
An instance of ClientSubnetOption based on the ENDS packet
"""
- data = wire[current:current + olen]
+ data = wire[current : current + olen]
(family, mask, scope) = struct.unpack("!HBB", data[:4])
c_mask = mask
ip = struct.unpack_from("!%ds" % (c_mask // 8), data, 4)[0]
- if (family == FAMILY_IPV4):
- ip = ip + b'\0' * ((32 - c_mask) // 8)
+ if family == FAMILY_IPV4:
+ ip = ip + b"\0" * ((32 - c_mask) // 8)
ip = socket.inet_ntop(socket.AF_INET, ip)
- elif (family == FAMILY_IPV6):
- ip = ip + b'\0' * ((128 - c_mask) // 8)
+ elif family == FAMILY_IPV6:
+ ip = ip + b"\0" * ((128 - c_mask) // 8)
ip = socket.inet_ntop(socket.AF_INET6, ip)
else:
raise Exception("Returned a family other then IPv4 or IPv6")
# needed in 2.0.0..
@classmethod
def from_wire_parser(cls, otype, parser):
- family, src, scope = parser.get_struct('!HBB')
+ family, src, scope = parser.get_struct("!HBB")
addrlen = int(math.ceil(src / 8.0))
prefix = parser.get_bytes(addrlen)
if family == 1:
pad = 4 - addrlen
- addr = dns.ipv4.inet_ntoa(prefix + b'\x00' * pad)
+ addr = dns.ipv4.inet_ntoa(prefix + b"\x00" * pad)
elif family == 2:
pad = 16 - addrlen
- addr = dns.ipv6.inet_ntoa(prefix + b'\x00' * pad)
+ addr = dns.ipv6.inet_ntoa(prefix + b"\x00" * pad)
else:
- raise ValueError('unsupported family')
+ raise ValueError("unsupported family")
return cls(addr, src, scope, otype)
def __repr__(self):
if self.family == FAMILY_IPV4:
- ip = socket.inet_ntop(socket.AF_INET, struct.pack('!L', self.ip))
+ ip = socket.inet_ntop(socket.AF_INET, struct.pack("!L", self.ip))
elif self.family == FAMILY_IPV6:
- ip = socket.inet_ntop(socket.AF_INET6,
- struct.pack('!QQ',
- self.ip >> 64,
- self.ip & (2 ** 64 - 1)))
-
- return "%s(%s, %s, %s)" % (
- self.__class__.__name__,
- ip,
- self.mask,
- self.scope
- )
+ ip = socket.inet_ntop(socket.AF_INET6, struct.pack("!QQ", self.ip >> 64, self.ip & (2**64 - 1)))
+
+ return "%s(%s, %s, %s)" % (self.__class__.__name__, ip, self.mask, self.scope)
def to_text(self):
return self.__repr__()
print("Found ClientSubnetOption...", end=None, file=sys.stderr)
if not cso.family == options.family:
error = True
- print("\nFailed: returned family (%d) is different from the passed family (%d)" % (options.family, cso.family), file=sys.stderr)
+ print(
+ "\nFailed: returned family (%d) is different from the passed family (%d)"
+ % (options.family, cso.family),
+ file=sys.stderr,
+ )
if not cso.calculate_ip() == options.calculate_ip():
error = True
- print("\nFailed: returned ip (%s) is different from the passed ip (%s)." % (options.calculate_ip(), cso.calculate_ip()), file=sys.stderr)
+ print(
+ "\nFailed: returned ip (%s) is different from the passed ip (%s)."
+ % (options.calculate_ip(), cso.calculate_ip()),
+ file=sys.stderr,
+ )
if not options.mask == cso.mask:
error = True
- print("\nFailed: returned mask bits (%d) is different from the passed mask bits (%d)" % (options.mask, cso.mask), file=sys.stderr)
+ print(
+ "\nFailed: returned mask bits (%d) is different from the passed mask bits (%d)"
+ % (options.mask, cso.mask),
+ file=sys.stderr,
+ )
if not options.scope != 0:
print("\nWarning: scope indicates edns-clientsubnet data is not used", file=sys.stderr)
if options.is_draft():
else:
print("Failed: No ClientSubnetOption returned", file=sys.stderr)
- parser = argparse.ArgumentParser(description='draft-vandergaast-edns-client-subnet-01 tester')
- parser.add_argument('nameserver', help='The nameserver to test')
- parser.add_argument('rr', help='DNS record that should return an EDNS enabled response')
- parser.add_argument('-s', '--subnet', help='Specifies an IP to pass as the client subnet.', default='192.0.2.0')
- parser.add_argument('-m', '--mask', type=int, help='CIDR mask to use for subnet')
- parser.add_argument('--timeout', type=int, help='Set the timeout for query to TIMEOUT seconds, default=10', default=10)
- parser.add_argument('-t', '--type', help='DNS query type, default=A', default='A')
+ parser = argparse.ArgumentParser(description="draft-vandergaast-edns-client-subnet-01 tester")
+ parser.add_argument("nameserver", help="The nameserver to test")
+ parser.add_argument("rr", help="DNS record that should return an EDNS enabled response")
+ parser.add_argument("-s", "--subnet", help="Specifies an IP to pass as the client subnet.", default="192.0.2.0")
+ parser.add_argument("-m", "--mask", type=int, help="CIDR mask to use for subnet")
+ parser.add_argument(
+ "--timeout", type=int, help="Set the timeout for query to TIMEOUT seconds, default=10", default=10
+ )
+ parser.add_argument("-t", "--type", help="DNS query type, default=A", default="A")
args = parser.parse_args()
if not args.mask:
- if ':' in args.subnet:
+ if ":" in args.subnet:
args.mask = 48
else:
args.mask = 24
from recursortests import RecursorTest
import pytest
-@pytest.fixture(scope='session')
+
+@pytest.fixture(scope="session")
def run_auths() -> str:
- confdir = 'configs/auths'
+ confdir = "configs/auths"
shutil.rmtree(confdir, True)
os.mkdir(confdir)
- print('\nStarting auths from fixture...')
+ print("\nStarting auths from fixture...")
RecursorTest.generateAllAuthConfig(confdir)
RecursorTest.startAllAuth(confdir)
yield "Here's Johnny!"
- print('\nStopping auths by fixture')
+ print("\nStopping auths by fixture")
RecursorTest.tearDownAuth()
import dns.message
import dns.query
+
class ExtendedErrorOption(dns.edns.Option):
- """Implementation of rfc8914
- """
+ """Implementation of rfc8914"""
def __init__(self, code, extra):
super(ExtendedErrorOption, self).__init__(15)
def to_wire(self, file=None):
"""Create EDNS packet."""
- data = struct.pack('!H', self.code)
+ data = struct.pack("!H", self.code)
data = data + self.extra
if not file:
return data
"""
if olen < 2:
- raise Exception('Invalid EDNS Extended Error option')
+ raise Exception("Invalid EDNS Extended Error option")
- (code,) = struct.unpack('!H', wire[current:current+2])
+ (code,) = struct.unpack("!H", wire[current : current + 2])
if olen > 2:
- extra = wire[current + 2:current + olen]
+ extra = wire[current + 2 : current + olen]
else:
- extra = b''
+ extra = b""
return cls(code, extra)
data = parser.get_remaining()
if len(data) < 2:
- raise Exception('Invalid EDNS Extended Error option')
+ raise Exception("Invalid EDNS Extended Error option")
- (code,) = struct.unpack('!H', data[0:2])
+ (code,) = struct.unpack("!H", data[0:2])
if len(data) > 2:
extra = data[2:]
else:
- extra = b''
+ extra = b""
return cls(code, extra)
def __repr__(self):
- return '%s(%d, %s)' % (
- self.__class__.__name__,
- self.code,
- self.extra
- )
+ return "%s(%d, %s)" % (self.__class__.__name__, self.code, self.extra)
def to_text(self):
return self.__repr__()
import dns.message
import dns.query
+
class PaddingOption(dns.edns.Option):
- """Implementation of rfc7830.
- """
+ """Implementation of rfc7830."""
def __init__(self, numberOfBytes):
super(PaddingOption, self).__init__(12)
return cls(len(data))
def __repr__(self):
- return '%s(%d)' % (
- self.__class__.__name__,
- self.numberOfBytes
- )
+ return "%s(%d)" % (self.__class__.__name__, self.numberOfBytes)
def to_text(self):
return self.__repr__()
import os.path
import glob
-e = xml.etree.ElementTree.parse('pytest.xml')
+e = xml.etree.ElementTree.parse("pytest.xml")
testsuites = e.getroot()
for testsuite in testsuites:
for testcase in testsuite:
cls = testcase.get("classname")
name = testcase.get("name")
- if '_' not in cls or '.' not in cls:
- print('Unexpected classname %s; name %s' % (cls, name))
+ if "_" not in cls or "." not in cls:
+ print("Unexpected classname %s; name %s" % (cls, name))
getstdout = True
continue
- confdirnames = [cls.split('_')[1].split('.')[0], cls.split('.')[1].split('Test')[0]]
+ confdirnames = [cls.split("_")[1].split(".")[0], cls.split(".")[1].split("Test")[0]]
found = False
for confdirname in confdirnames:
confdir = os.path.join("configs", confdirname)
if elem.tag in ["failure", "error"]:
print("==============> %s <==============" % recursorlog)
with open(recursorlog) as f:
- print(''.join(f.readlines()))
+ print("".join(f.readlines()))
authdirs = glob.glob(os.path.join(confdir, "auth-*"))
for authdir in authdirs:
authlog = os.path.join(authdir, "pdns.log")
if os.path.exists(recursorlog):
print("==============> %s <==============" % authlog)
with open(authlog) as f:
- print(''.join(f.readlines()))
- if not found and confdirnames[0] != 'Flags':
+ print("".join(f.readlines()))
+ if not found and confdirnames[0] != "Flags":
print("%s not found, configdir does not mach expected pattern" % confdirnames)
- if getstdout and elem.tag == 'system-out':
+ if getstdout and elem.tag == "system-out":
print("==============> STDOUT LOG FROM XML <==============")
print(elem.text)
print("==============> END STDOUT LOG FROM XML <==============")
"""
try:
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
- sock.bind(('::1', 56581))
+ sock.bind(("::1", 56581))
sock.close()
return True
except Exception:
Setup all recursors and auths required for the tests
"""
- _confdir = 'recursor'
+ _confdir = "recursor"
_recursorPort = 5300
_recursor = None
- _PREFIX = os.environ['PREFIX']
+ _PREFIX = os.environ["PREFIX"]
_config_template_default = """
daemon=no
_config_params = []
_lua_config_file = None
_lua_dns_script_file = None
- _roothints = """
+ _roothints = (
+ """
. 3600 IN NS ns.root.
ns.root. 3600 IN A %s.8
ns.root. 3600 IN AAAA ::1
-""" % _PREFIX
+"""
+ % _PREFIX
+ )
_root_DS = "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
# The default SOA for zones in the authoritative servers
# - {soa} => value of _SOA
# - {prefix} value of _PREFIX
_zones = {
- 'ROOT': """
+ "ROOT": """
. 3600 IN SOA {soa}
. 3600 IN NS ns.root.
ns.root. 3600 IN A {prefix}.8
ns1.example. 3600 IN A {prefix}.10
ns2.example. 3600 IN A {prefix}.18
""",
- 'example': """
+ "example": """
example. 3600 IN SOA {soa}
example. 3600 IN NS ns1.example.
example. 3600 IN NS ns2.example.
cname-custom-a.example. 3600 IN CNAME cname-custom-a-target.example.
cname-custom-a-target.example. 3600 IN A 192.0.2.102
""",
- 'secure.example': """
+ "secure.example": """
secure.example. 3600 IN SOA {soa}
secure.example. 3600 IN NS ns.secure.example.
ns.secure.example. 3600 IN A {prefix}.9
non-apex-dnskey2.secure.example. 3600 IN DNSKEY 256 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
non-apex-dnskey3.secure.example. 3600 IN DNSKEY 256 3 13 DT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
""",
- 'dname-secure.example': """
+ "dname-secure.example": """
dname-secure.example. 3600 IN SOA {soa}
dname-secure.example. 3600 IN NS ns.dname-secure.example.
ns.dname-secure.example. 3600 IN A {prefix}.13
cname-to-insecure.dname-secure.example. 3600 IN CNAME node1.insecure.example.
cname-to-bogus.dname-secure.example. 3600 IN CNAME ted.bogus.example.
""",
- 'cname-secure.example': """
+ "cname-secure.example": """
cname-secure.example. 3600 IN SOA {soa}
cname-secure.example. 3600 IN NS ns.cname-secure.example.
ns.cname-secure.example. 3600 IN A {prefix}.15
cname-secure.example. 3600 IN CNAME secure.example.
""",
- 'bogus.example': """
+ "bogus.example": """
bogus.example. 3600 IN SOA {soa}
bogus.example. 3600 IN NS ns1.bogus.example.
ns1.bogus.example. 3600 IN A {prefix}.12
ted.bogus.example. 3600 IN A 192.0.2.1
bill.bogus.example. 3600 IN AAAA 2001:db8:12::3
""",
- 'insecure.sub2.secure.example': """
+ "insecure.sub2.secure.example": """
insecure.sub2.secure.example. 3600 IN SOA {soa}
insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
node1.insecure.sub2.secure.example. 3600 IN A 192.0.2.18
""",
- 'insecure.example': """
+ "insecure.example": """
insecure.example. 3600 IN SOA {soa}
insecure.example. 3600 IN NS ns1.insecure.example.
ns1.insecure.example. 3600 IN A {prefix}.13
dname-to-secure.insecure.example. 3600 IN DNAME dname-secure.example.
""",
- 'optout.example': """
+ "optout.example": """
optout.example. 3600 IN SOA {soa}
optout.example. 3600 IN NS ns1.optout.example.
ns1.optout.example. 3600 IN A {prefix}.14
secure.optout.example. 3600 IN DS 64215 13 1 b88284d7a8d8605c398e8942262f97b9a5a31787
ns1.secure.optout.example. 3600 IN A {prefix}.15
""",
- 'insecure.optout.example': """
+ "insecure.optout.example": """
insecure.optout.example. 3600 IN SOA {soa}
insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
ns1.insecure.optout.example. 3600 IN A {prefix}.15
node1.insecure.optout.example. 3600 IN A 192.0.2.7
""",
- 'secure.optout.example': """
+ "secure.optout.example": """
secure.optout.example. 3600 IN SOA {soa}
secure.optout.example. 3600 IN NS ns1.secure.optout.example.
ns1.secure.optout.example. 3600 IN A {prefix}.15
node1.secure.optout.example. 3600 IN A 192.0.2.8
""",
- 'islandofsecurity.example': """
+ "islandofsecurity.example": """
islandofsecurity.example. 3600 IN SOA {soa}
islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
ns1.islandofsecurity.example. 3600 IN A {prefix}.9
node1.islandofsecurity.example. 3600 IN A 192.0.2.20
""",
- 'undelegated.secure.example': """
+ "undelegated.secure.example": """
undelegated.secure.example. 3600 IN SOA {soa}
undelegated.secure.example. 3600 IN NS ns1.undelegated.secure.example.
node1.undelegated.secure.example. 3600 IN A 192.0.2.21
""",
- 'undelegated.insecure.example': """
+ "undelegated.insecure.example": """
undelegated.insecure.example. 3600 IN SOA {soa}
undelegated.insecure.example. 3600 IN NS ns1.undelegated.insecure.example.
node1.undelegated.insecure.example. 3600 IN A 192.0.2.22
""",
-
- 'delay1.example': """
+ "delay1.example": """
delay1.example. 3600 IN SOA {soa}
delay1.example. 3600 IN NS n1.delay1.example.
ns1.delay1.example. 3600 IN A {prefix}.16
*.delay1.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
*.delay1.example. 0 LUA AAAA ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return '1::2'"
""",
-
- 'delay2.example': """
+ "delay2.example": """
delay2.example. 3600 IN SOA {soa}
delay2.example. 3600 IN NS n1.delay2.example.
ns1.delay2.example. 3600 IN A {prefix}.17
*.delay2.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
- """
+ """,
}
# The private keys for the zones (note that DS records should go into
# the zonecontent in _zones
_zone_keys = {
- 'ROOT': """
+ "ROOT": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: rhWuEydDz3QaIspSVj683B8Xq5q/ozzA38XUgzD4Fbo=
""",
-
- 'example': """
+ "example": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
""",
-
- 'secure.example': """
+ "secure.example": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: 1G4WRoOFJJXk+fotDCHVORtJmIG2OUhKi8AO2jDPGZA=
""",
-
- 'bogus.example': """
+ "bogus.example": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: f5jV7Q8kd5hDpMWObsuQ6SQda0ftf+JrO3uZwEg6nVw=
""",
-
- 'optout.example': """
+ "optout.example": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
""",
-
- 'secure.optout.example': """
+ "secure.optout.example": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
""",
-
- 'islandofsecurity.example': """
+ "islandofsecurity.example": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: o9F5iix8V68tnMcuOaM2Lt8XXhIIY//SgHIHEePk6cM=
""",
-
- 'cname-secure.example': """
+ "cname-secure.example": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: kvoV/g4IO/tefSro+FLJ5UC7H3BUf0IUtZQSUOfQGyA=
""",
-
- 'dname-secure.example': """
+ "dname-secure.example": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
""",
-
- 'delay1.example': """
+ "delay1.example": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
""",
-
- 'delay2.example': """
+ "delay2.example": """
Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
-"""
+""",
}
# This dict is keyed with the suffix of the IP address and its value
# is a list of zones hosted on that IP. Note that delegations should
# go into the _zones's zonecontent
_default_auth_zones = {
- '8': {'threads': 1,
- 'zones': ['ROOT']},
- '9': {'threads': 1,
- 'zones': ['secure.example', 'islandofsecurity.example']},
- '10': {'threads': 1,
- 'zones': ['example']},
-
+ "8": {"threads": 1, "zones": ["ROOT"]},
+ "9": {"threads": 1, "zones": ["secure.example", "islandofsecurity.example"]},
+ "10": {"threads": 1, "zones": ["example"]},
# 11 is used by CircleCI provided resolver
-
- '12': {'threads': 1,
- 'zones': ['bogus.example', 'undelegated.secure.example', 'undelegated.insecure.example']},
- '13': {'threads': 1,
- 'zones': ['insecure.example', 'insecure.sub2.secure.example', 'dname-secure.example']},
- '14': {'threads': 1,
- 'zones': ['optout.example']},
- '15': {'threads': 1,
- 'zones': ['insecure.optout.example', 'secure.optout.example', 'cname-secure.example']},
- '16': {'threads': 10,
- 'zones': ['delay1.example']},
- '17': {'threads': 10,
- 'zones': ['delay2.example']},
- '18': {'threads': 1,
- 'zones': ['example']}
+ "12": {"threads": 1, "zones": ["bogus.example", "undelegated.secure.example", "undelegated.insecure.example"]},
+ "13": {"threads": 1, "zones": ["insecure.example", "insecure.sub2.secure.example", "dname-secure.example"]},
+ "14": {"threads": 1, "zones": ["optout.example"]},
+ "15": {"threads": 1, "zones": ["insecure.optout.example", "secure.optout.example", "cname-secure.example"]},
+ "16": {"threads": 10, "zones": ["delay1.example"]},
+ "17": {"threads": 10, "zones": ["delay2.example"]},
+ "18": {"threads": 1, "zones": ["example"]},
}
_auth_zones = _default_auth_zones
# Other IPs used:
# 25: test_Cookies.py
# 26: test_Cookies.py
- _auth_cmd = ['authbind',
- os.environ['PDNS']]
+ _auth_cmd = ["authbind", os.environ["PDNS"]]
_auth_env = {}
_auths = {}
if e.errno != errno.ENOENT:
raise
os.mkdir(confdir, 0o755)
- os.mkdir(confdir + '/include', 0o755)
+ os.mkdir(confdir + "/include", 0o755)
@classmethod
def generateAuthZone(cls, confdir, zonename, zonecontent):
- with open(os.path.join(confdir, '%s.zone' % zonename), 'w') as zonefile:
+ with open(os.path.join(confdir, "%s.zone" % zonename), "w") as zonefile:
zonefile.write(zonecontent.format(prefix=cls._PREFIX, soa=cls._SOA))
@classmethod
def generateAuthNamedConf(cls, confdir, zones):
- with open(os.path.join(confdir, 'named.conf'), 'w') as namedconf:
- namedconf.write("""
+ with open(os.path.join(confdir, "named.conf"), "w") as namedconf:
+ namedconf.write(
+ """
options {
directory "%s";
-};""" % confdir)
+};"""
+ % confdir
+ )
for zonename in zones:
- zone = '.' if zonename == 'ROOT' else zonename
+ zone = "." if zonename == "ROOT" else zonename
- namedconf.write("""
+ namedconf.write(
+ """
zone "%s" {
type master;
file "%s.zone";
- };""" % (zone, zonename))
+ };"""
+ % (zone, zonename)
+ )
@classmethod
- def generateAuthConfig(cls, confdir, threads, extra=''):
- bind_dnssec_db = os.path.join(confdir, 'bind-dnssec.sqlite3')
- with open(os.path.join(confdir, 'pdns.conf'), 'w') as pdnsconf:
- pdnsconf.write("""
+ def generateAuthConfig(cls, confdir, threads, extra=""):
+ bind_dnssec_db = os.path.join(confdir, "bind-dnssec.sqlite3")
+ with open(os.path.join(confdir, "pdns.conf"), "w") as pdnsconf:
+ pdnsconf.write(
+ """
module-dir={moduledir}
launch=bind
daemon=no
enable-lua-records
dname-processing=yes
distributor-threads={threads}
-{extra}""".format(moduledir=os.environ['PDNSMODULEDIR'],
- confdir=confdir,
- bind_dnssec_db=bind_dnssec_db,
- threads=threads,
- extra=extra))
+{extra}""".format(
+ moduledir=os.environ["PDNSMODULEDIR"],
+ confdir=confdir,
+ bind_dnssec_db=bind_dnssec_db,
+ threads=threads,
+ extra=extra,
+ )
+ )
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'create-bind-db',
- bind_dnssec_db]
+ pdnsutilCmd = [os.environ["PDNSUTIL"], "--config-dir=%s" % confdir, "create-bind-db", bind_dnssec_db]
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
@classmethod
def secureZone(cls, confdir, zonename, key=None):
- zone = '.' if zonename == 'ROOT' else zonename
+ zone = "." if zonename == "ROOT" else zonename
if not key:
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'secure-zone',
- zone]
+ pdnsutilCmd = [os.environ["PDNSUTIL"], "--config-dir=%s" % confdir, "secure-zone", zone]
else:
- keyfile = os.path.join(confdir, 'dnssec.key')
- with open(keyfile, 'w') as fdKeyfile:
+ keyfile = os.path.join(confdir, "dnssec.key")
+ with open(keyfile, "w") as fdKeyfile:
fdKeyfile.write(key)
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'import-zone-key',
- zone,
- keyfile,
- 'active',
- 'ksk']
-
- print(' '.join(pdnsutilCmd))
+ pdnsutilCmd = [
+ os.environ["PDNSUTIL"],
+ "--config-dir=%s" % confdir,
+ "import-zone-key",
+ zone,
+ keyfile,
+ "active",
+ "ksk",
+ ]
+
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
@classmethod
def generateAllAuthConfig(cls, confdir):
if cls._auth_zones:
for auth_suffix, zoneinfo in cls._auth_zones.items():
- threads = zoneinfo['threads']
- zones = zoneinfo['zones']
- authconfdir = os.path.join(confdir, 'auth-%s' % auth_suffix)
+ threads = zoneinfo["threads"]
+ zones = zoneinfo["zones"]
+ authconfdir = os.path.join(confdir, "auth-%s" % auth_suffix)
os.mkdir(authconfdir)
cls.generateAuthConfig(authconfdir, threads)
cls.generateAuthNamedConf(authconfdir, zones)
for zone in zones:
- cls.generateAuthZone(authconfdir,
- zone,
- cls._zones[zone])
+ cls.generateAuthZone(authconfdir, zone, cls._zones[zone])
if cls._zone_keys.get(zone, None):
cls.secureZone(authconfdir, zone, cls._zone_keys.get(zone))
@classmethod
def setUpClassSpecialAuths(cls):
# tear down existing auths, and start with our own special config
- confdir = os.path.join('configs', 'auths')
+ confdir = os.path.join("configs", "auths")
cls.tearDownAuth()
- #confdir = os.path.join('configs', cls._confdir)
+ # confdir = os.path.join('configs', cls._confdir)
print("Specialized auth setup" + confdir)
cls.createConfigDir(confdir)
cls.generateAllAuthConfig(confdir)
def startAllAuth(cls, confdir):
if cls._auth_zones:
for auth_suffix, _ in cls._auth_zones.items():
- authconfdir = os.path.join(confdir, 'auth-%s' % auth_suffix)
- ipaddress = cls._PREFIX + '.' + auth_suffix
+ authconfdir = os.path.join(confdir, "auth-%s" % auth_suffix)
+ ipaddress = cls._PREFIX + "." + auth_suffix
cls.startAuth(authconfdir, ipaddress)
@classmethod
return
except Exception as err:
if err.errno != errno.ECONNREFUSED:
- print(f'Error occurred: {try_number} {err}', file=sys.stderr)
+ print(f"Error occurred: {try_number} {err}", file=sys.stderr)
time.sleep(0.01)
@classmethod
def startAuth(cls, confdir, ipaddress):
print("Launching pdns_server..")
authcmd = list(cls._auth_cmd)
- authcmd.append('--config-dir=%s' % confdir)
+ authcmd.append("--config-dir=%s" % confdir)
ipconfig = ipaddress
# auth-8 is the auth serving the root, it gets an ipv6 address
if (confdir[-6:] == "auth-8") and have_ipv6():
- ipconfig += ',::1'
- authcmd.append('--local-address=%s' % ipconfig)
- print(' '.join(authcmd))
+ ipconfig += ",::1"
+ authcmd.append("--local-address=%s" % ipconfig)
+ print(" ".join(authcmd))
- logFile = os.path.join(confdir, 'pdns.log')
- with open(logFile, 'w') as fdLog:
- cls._auths[ipaddress] = subprocess.Popen(authcmd, close_fds=True,
- stdout=fdLog, stderr=fdLog,
- env=cls._auth_env)
+ logFile = os.path.join(confdir, "pdns.log")
+ with open(logFile, "w") as fdLog:
+ cls._auths[ipaddress] = subprocess.Popen(
+ authcmd, close_fds=True, stdout=fdLog, stderr=fdLog, env=cls._auth_env
+ )
cls.waitForTCPSocket(ipaddress, 53)
cls.checkAuth(cls._auths[ipaddress], authcmd, logFile)
def checkAuth(cls, auth, authcmd, logFile):
if auth.poll() is not None:
print(f"\n*** startAuth log for {logFile} ***")
- with open(logFile, 'r') as fdLog:
+ with open(logFile, "r") as fdLog:
print(fdLog.read())
print(f"*** End startAuth log for {logFile} ***")
- raise AssertionError('%s failed (%d)' % (authcmd, auth.returncode))
+ raise AssertionError("%s failed (%d)" % (authcmd, auth.returncode))
@classmethod
def checkConfdir(cls, confdir):
- if cls.__name__ != 'FlagsTest' and os.path.basename(confdir) + 'Test' != cls.__name__:
- raise AssertionError('conf dir ' + confdir + ' and ' + cls.__name__ + ' inconsistent with convention')
+ if cls.__name__ != "FlagsTest" and os.path.basename(confdir) + "Test" != cls.__name__:
+ raise AssertionError("conf dir " + confdir + " and " + cls.__name__ + " inconsistent with convention")
@classmethod
def generateRecursorConfig(cls, confdir):
if len(params):
print(params)
- recursorconf = os.path.join(confdir, 'recursor.conf')
+ recursorconf = os.path.join(confdir, "recursor.conf")
- with open(recursorconf, 'w') as conf:
+ with open(recursorconf, "w") as conf:
conf.write("# Autogenerated by recursortests.py\n")
conf.write(cls._config_template_default)
conf.write(cls._config_template % params)
conf.write("\n")
conf.write("socket-dir=%s\n" % confdir)
if cls._lua_config_file or cls._root_DS:
- luaconfpath = os.path.join(confdir, 'conffile.lua')
- with open(luaconfpath, 'w') as luaconf:
+ luaconfpath = os.path.join(confdir, "conffile.lua")
+ with open(luaconfpath, "w") as luaconf:
if cls._root_DS:
luaconf.write("addTA('.', '%s')\n" % cls._root_DS)
if cls._lua_config_file:
luaconf.write(cls._lua_config_file)
conf.write("lua-config-file=%s\n" % luaconfpath)
if cls._lua_dns_script_file:
- luascriptpath = os.path.join(confdir, 'dnsscript.lua')
- with open(luascriptpath, 'w') as luascript:
+ luascriptpath = os.path.join(confdir, "dnsscript.lua")
+ with open(luascriptpath, "w") as luascript:
luascript.write(cls._lua_dns_script_file)
conf.write("lua-dns-script=%s\n" % luascriptpath)
if cls._roothints:
- roothintspath = os.path.join(confdir, 'root.hints')
- with open(roothintspath, 'w') as roothints:
+ roothintspath = os.path.join(confdir, "root.hints")
+ with open(roothintspath, "w") as roothints:
roothints.write(cls._roothints)
conf.write("hint-file=%s\n" % roothintspath)
if len(params):
print(params)
- recursorconf = os.path.join(confdir, 'recursor.yml')
+ recursorconf = os.path.join(confdir, "recursor.yml")
- with open(recursorconf, 'w') as conf:
+ with open(recursorconf, "w") as conf:
conf.write("# Autogenerated by recursortests.py\n")
- conf.write(cls._config_template_yaml_default % os.path.join(confdir, 'include'))
- recursorconf = os.path.join(confdir, 'include', 'recursor01.yml')
- with open(recursorconf, 'w') as conf:
+ conf.write(cls._config_template_yaml_default % os.path.join(confdir, "include"))
+ recursorconf = os.path.join(confdir, "include", "recursor01.yml")
+ with open(recursorconf, "w") as conf:
conf.write(cls._config_template % params)
conf.write("\n")
- recursorconf = os.path.join(confdir, 'include', 'recursor02.yml')
- with open(recursorconf, 'w') as conf:
+ recursorconf = os.path.join(confdir, "include", "recursor02.yml")
+ with open(recursorconf, "w") as conf:
conf.write("recursor:\n")
conf.write(" socket_dir: %s\n" % confdir)
if luaConfig and (cls._lua_config_file or cls._root_DS):
- luaconfpath = os.path.join(confdir, 'conffile.lua')
- with open(luaconfpath, 'w') as luaconf:
+ luaconfpath = os.path.join(confdir, "conffile.lua")
+ with open(luaconfpath, "w") as luaconf:
if cls._root_DS:
luaconf.write("addTA('.', '%s')\n" % cls._root_DS)
if cls._lua_config_file:
luaconf.write(cls._lua_config_file)
conf.write(" lua_config_file: %s\n" % luaconfpath)
if cls._lua_dns_script_file:
- luascriptpath = os.path.join(confdir, 'dnsscript.lua')
- with open(luascriptpath, 'w') as luascript:
+ luascriptpath = os.path.join(confdir, "dnsscript.lua")
+ with open(luascriptpath, "w") as luascript:
luascript.write(cls._lua_dns_script_file)
conf.write(" lua_dns_script: %s\n" % luascriptpath)
if cls._roothints:
- roothintspath = os.path.join(confdir, 'root.hints')
- with open(roothintspath, 'w') as roothints:
+ roothintspath = os.path.join(confdir, "root.hints")
+ with open(roothintspath, "w") as roothints:
roothints.write(cls._roothints)
conf.write(" hint_file: %s\n" % roothintspath)
@classmethod
def startRecursor(cls, confdir, port):
print("Launching pdns_recursor..")
- recursorcmd = [os.environ['PDNSRECURSOR'],
- '--config-dir=%s' % confdir,
- '--local-port=%s' % port,
- '--security-poll-suffix=',
- '--enable-old-settings']
- print(' '.join(recursorcmd))
-
- logFile = os.path.join(confdir, 'recursor.log')
- with open(logFile, 'w') as fdLog:
- cls._recursor = subprocess.Popen(recursorcmd, close_fds=True,
- stdout=fdLog, stderr=fdLog)
+ recursorcmd = [
+ os.environ["PDNSRECURSOR"],
+ "--config-dir=%s" % confdir,
+ "--local-port=%s" % port,
+ "--security-poll-suffix=",
+ "--enable-old-settings",
+ ]
+ print(" ".join(recursorcmd))
+
+ logFile = os.path.join(confdir, "recursor.log")
+ with open(logFile, "w") as fdLog:
+ cls._recursor = subprocess.Popen(recursorcmd, close_fds=True, stdout=fdLog, stderr=fdLog)
cls.waitForTCPSocket("127.0.0.1", port)
if cls._recursor.poll() is not None:
print(f"\n*** startRecursor log for {logFile} ***")
- with open(logFile, 'r') as fdLog:
+ with open(logFile, "r") as fdLog:
print(fdLog.read())
print(f"*** End startRecursor log for {logFile} ***")
- raise AssertionError('%s failed (%d)' % (recursorcmd, cls._recursor.returncode))
+ raise AssertionError("%s failed (%d)" % (recursorcmd, cls._recursor.returncode))
@classmethod
- def wipeRecursorCache(cls, confdir, name='.$'):
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % confdir,
- 'wipe-cache',
- name]
+ def wipeRecursorCache(cls, confdir, name=".$"):
+ rec_controlCmd = [os.environ["RECCONTROL"], "--config-dir=%s" % confdir, "wipe-cache", name]
try:
subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (rec_controlCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (rec_controlCmd, e.returncode, e.output))
@classmethod
def recControl(cls, confdir, *command):
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % confdir
- ] + list(command)
+ rec_controlCmd = [os.environ["RECCONTROL"], "--config-dir=%s" % confdir] + list(command)
try:
return subprocess.check_output(rec_controlCmd, text=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (rec_controlCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (rec_controlCmd, e.returncode, e.output))
@classmethod
def recFeatures(cls):
- rec_versionCmd = [os.environ['PDNSRECURSOR'],
- '--version']
+ rec_versionCmd = [os.environ["PDNSRECURSOR"], "--version"]
try:
full = subprocess.check_output(rec_versionCmd, text=True, stderr=subprocess.STDOUT)
for line in full.splitlines():
if line.startswith("Features: "):
return line
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (rec_versionCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (rec_versionCmd, e.returncode, e.output))
@classmethod
def setUpSockets(cls):
cls.startResponders()
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
cls.createConfigDir(confdir)
cls.generateRecursorConfig(confdir)
cls.startRecursor(confdir, cls._recursorPort)
-# for auth_suffix, _ in RecursorTest._auth_zones.items():
-# ip = RecursorTest._PREFIX + '.' + auth_suffix
-# auth = RecursorTest._auths[ip]
-# logFile = os.path.join(confdir, 'auth-'+ auth_suffix, 'pdns.log')
-# RecursorTest.checkAuth(auth, 'auth-' + ip, logFile)
+ # for auth_suffix, _ in RecursorTest._auth_zones.items():
+ # ip = RecursorTest._PREFIX + '.' + auth_suffix
+ # auth = RecursorTest._auths[ip]
+ # logFile = os.path.join(confdir, 'auth-'+ auth_suffix, 'pdns.log')
+ # RecursorTest.checkAuth(auth, 'auth-' + ip, logFile)
print("Launching tests..")
return
try:
p.terminate()
- for count in range(1000): # tsan can be slow
+ for count in range(1000): # tsan can be slow
x = p.poll()
if x is not None:
break
def tearDownRecursor(cls, subdir=None):
# We now kill the recursor in a friendly way, as systemd is doing the same.
if subdir is None:
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
else:
- confdir = os.path.join('configs', cls._confdir, subdir)
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % confdir,
- '--timeout=20',
- 'quit-nicely']
+ confdir = os.path.join("configs", cls._confdir, subdir)
+ rec_controlCmd = [os.environ["RECCONTROL"], "--config-dir=%s" % confdir, "--timeout=20", "quit-nicely"]
try:
subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (rec_controlCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (rec_controlCmd, e.returncode, e.output))
# Wait for it, as the process really should have exited
p = cls._recursor
- for count in range(1000): # tsan can be slow
+ for count in range(1000): # tsan can be slow
if p.poll() is not None:
break
time.sleep(0.01)
if p.poll() is None:
- raise AssertionError('Process did not exit on request within 10s')
+ raise AssertionError("Process did not exit on request within 10s")
if p.returncode not in (0, -15):
- raise AssertionError('Process exited with return code %d' % (p.returncode))
+ raise AssertionError("Process exited with return code %d" % (p.returncode))
@classmethod
def sendUDPQuery(cls, query, timeout=2.0, decode=True, fwparams=dict()):
missingEdnsFlags = [ednsflag for ednsflag in ednsflags if ednsflag not in msgEdnsFlags]
if len(missingFlags) or len(missingEdnsFlags) or len(msgFlags) > len(flags):
- raise AssertionError("Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s" %
- (' '.join(flags), ' '.join(ednsflags),
- ' '.join(msgFlags), ' '.join(msgEdnsFlags),
- msg.question[0]))
+ raise AssertionError(
+ "Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s"
+ % (" ".join(flags), " ".join(ednsflags), " ".join(msgFlags), " ".join(msgEdnsFlags), msg.question[0])
+ )
def assertMessageIsAuthenticated(self, msg):
"""Asserts that the message has the AD bit set
raise TypeError("msg is not a dns.message.Message")
msgFlags = dns.flags.to_text(msg.flags)
- self.assertIn('AD', msgFlags, "No AD flag found in the message for %s" % msg.question[0].name)
+ self.assertIn("AD", msgFlags, "No AD flag found in the message for %s" % msg.question[0].name)
def assertRRsetInAnswer(self, msg, rrset):
"""Asserts the rrset (without comparing TTL) exists in the
@param msg: the dns.message.Message to check
@param rrset: a dns.rrset.RRset object"""
- ret = ''
+ ret = ""
if not isinstance(msg, dns.message.Message):
raise TypeError("msg is not a dns.message.Message")
@param msg: the dns.message.Message to check
@param rrset: a dns.rrset.RRset object"""
- ret = ''
+ ret = ""
if not isinstance(msg, dns.message.Message):
raise TypeError("msg is not a dns.message.Message")
msgRRsigRRSet = None
msgRRSet = None
- ret = ''
+ ret = ""
for ans in msg.answer:
ret += ans.to_text() + "\n"
raise AssertionError("RRset for '%s' not found in answer" % msg.question[0].to_text())
if not msgRRsigRRSet:
- raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg.question[0].to_text(), ret))
+ raise AssertionError(
+ "No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg.question[0].to_text(), ret)
+ )
if keys:
try:
msgRRsigRRSet = None
msgRRSet = None
- ret = ''
+ ret = ""
for ans in msg.additional:
ret += ans.to_text() + "\n"
raise AssertionError("RRset for '%s' not found in additional" % msg.question[0].to_text())
if not msgRRsigRRSet:
- raise AssertionError("No RRSIGs found in additional for %s:\nFull answer:\n%s" % (msg.question[0].to_text(), ret))
+ raise AssertionError(
+ "No RRSIGs found in additional for %s:\nFull answer:\n%s" % (msg.question[0].to_text(), ret)
+ )
if keys:
try:
raise AssertionError("RRSIG found in answers for:\n%s" % ret)
def assertAnswerEmpty(self, msg):
- self.assertEqual(len(msg.answer), 0, "Data found in the answer section for %s:\n%s" % (msg.question[0].to_text(), '\n'.join([i.to_text() for i in msg.answer])))
+ self.assertEqual(
+ len(msg.answer),
+ 0,
+ "Data found in the answer section for %s:\n%s"
+ % (msg.question[0].to_text(), "\n".join([i.to_text() for i in msg.answer])),
+ )
def assertAdditionalEmpty(self, msg):
- self.assertEqual(len(msg.additional), 0, "Data found in the additional section for %s:\n%s" % (msg.question[0].to_text(), '\n'.join([i.to_text() for i in msg.additional])))
+ self.assertEqual(
+ len(msg.additional),
+ 0,
+ "Data found in the additional section for %s:\n%s"
+ % (msg.question[0].to_text(), "\n".join([i.to_text() for i in msg.additional])),
+ )
def assertRcodeEqual(self, msg, rcode):
if not isinstance(msg, dns.message.Message):
msgRcode = dns.rcode.to_text(msg.rcode())
wantedRcode = dns.rcode.to_text(rcode)
- raise AssertionError("Rcode for %s is %s, expected %s." % (msg.question[0].to_text(), msgRcode, wantedRcode))
+ raise AssertionError(
+ "Rcode for %s is %s, expected %s." % (msg.question[0].to_text(), msgRcode, wantedRcode)
+ )
def assertAuthorityHasSOA(self, msg):
if not isinstance(msg, dns.message.Message):
The flags need to be strings (no checking is performed atm)"""
msg = dns.message.make_query(name, rdtype)
msg.flags = dns.flags.from_text(flags)
- msg.flags += dns.flags.from_text('RD')
+ msg.flags += dns.flags.from_text("RD")
msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text(ednsflags))
return msg
@classmethod
- def sendUDPQueryWithProxyProtocol(cls, query, v6, source, destination, sourcePort, destinationPort, values=[], timeout=2.0):
+ def sendUDPQueryWithProxyProtocol(
+ cls, query, v6, source, destination, sourcePort, destinationPort, values=[], timeout=2.0
+ ):
queryPayload = query.to_wire()
ppPayload = ProxyProtocol.getPayload(False, False, v6, source, destination, sourcePort, destinationPort, values)
payload = ppPayload + queryPayload
return message
@classmethod
- def sendTCPQueryWithProxyProtocol(cls, query, v6, source, destination, sourcePort, destinationPort, values=[], timeout=2.0):
+ def sendTCPQueryWithProxyProtocol(
+ cls, query, v6, source, destination, sourcePort, destinationPort, values=[], timeout=2.0
+ ):
queryPayload = query.to_wire()
ppPayload = ProxyProtocol.getPayload(False, False, v6, source, destination, sourcePort, destinationPort, values)
def checkMetrics(self, map):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/api/v1/servers/localhost/statistics"
r = requests.get(url, headers=headers, timeout=self._wsTimeout)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
count = 0
for entry in content:
for key, expected in map.items():
- if entry['name'] == key:
- value = int(entry['value'])
+ if entry["name"] == key:
+ value = int(entry["value"])
if callable(expected):
self.assertTrue(expected(value), key + ": value " + str(value) + " is not expected")
else:
@classmethod
def startReactor(cls):
if not reactor.running:
- cls.Responder = threading.Thread(name='Responder', target=reactor.run, args=(False,))
+ cls.Responder = threading.Thread(name="Responder", target=reactor.run, args=(False,))
cls.Responder.daemon = True
cls.Responder.start()
-
@classmethod
def _ResponderIncrementCounter(cls):
if threading.current_thread().name in cls._responsesCounter:
response.id = request.id
if synthesize is not None:
- response = dns.message.make_response(request)
- response.set_rcode(synthesize)
+ response = dns.message.make_response(request)
+ response.set_rcode(synthesize)
if not response:
if cls._answerUnexpected:
return response
@classmethod
- def handleTCPConnection(cls, conn, fromQueue, toQueue, trailingDataResponse=False, multipleResponses=False, callback=None, partialWrite=False,clientCert=False):
- ignoreTrailing = trailingDataResponse is True
- try:
- data = conn.recv(2)
- except Exception as err:
- data = None
- print(f'Error while reading query size in TCP responder thread {err=}, {type(err)=}')
- if not data:
- conn.close()
- return
-
- if clientCert:
- print("Checking client cert")
- cert = conn.getpeercert()
- if cert is None:
- raise AssertionError("Client certificate expected, got none")
- if cert['subject'][0][0][1] != 'client.tests.powerdns.com':
- print(cert)
- raise AssertionError('Unexpected subject in cert')
-
- (datalen,) = struct.unpack("!H", data)
- data = conn.recv(datalen)
- forceRcode = None
- try:
- request = dns.message.from_wire(data, ignore_trailing=ignoreTrailing)
- except dns.message.TrailingJunk as e:
- if trailingDataResponse is False or forceRcode is True:
- raise
- print("TCP query with trailing data, synthesizing response")
- request = dns.message.from_wire(data, ignore_trailing=True)
- forceRcode = trailingDataResponse
-
- if callback:
- wire = callback(request)
- else:
- if request.edns > 1:
- forceRcode = dns.rcode.BADVERS
- response = cls._getResponse(request, fromQueue, toQueue, synthesize=forceRcode)
- if response:
- wire = response.to_wire(max_size=65535)
-
- if not wire:
- conn.close()
- return
- elif isinstance(wire, ResponderDropAction):
- return
-
- wireLen = struct.pack("!H", len(wire))
- if partialWrite:
- for b in wireLen:
- conn.send(bytes([b]))
- time.sleep(0.5)
- else:
- conn.send(wireLen)
- conn.send(wire)
-
- while multipleResponses:
- # do not block, and stop as soon as the queue is empty, either the next response is already here or we are done
- # otherwise we might read responses intended for the next connection
- if fromQueue.empty():
- break
-
- response = fromQueue.get(False)
- if not response:
- break
+ def handleTCPConnection(
+ cls,
+ conn,
+ fromQueue,
+ toQueue,
+ trailingDataResponse=False,
+ multipleResponses=False,
+ callback=None,
+ partialWrite=False,
+ clientCert=False,
+ ):
+ ignoreTrailing = trailingDataResponse is True
+ try:
+ data = conn.recv(2)
+ except Exception as err:
+ data = None
+ print(f"Error while reading query size in TCP responder thread {err=}, {type(err)=}")
+ if not data:
+ conn.close()
+ return
- response = copy.copy(response)
- response.id = request.id
- wire = response.to_wire(max_size=65535)
+ if clientCert:
+ print("Checking client cert")
+ cert = conn.getpeercert()
+ if cert is None:
+ raise AssertionError("Client certificate expected, got none")
+ if cert["subject"][0][0][1] != "client.tests.powerdns.com":
+ print(cert)
+ raise AssertionError("Unexpected subject in cert")
+
+ (datalen,) = struct.unpack("!H", data)
+ data = conn.recv(datalen)
+ forceRcode = None
try:
- conn.send(struct.pack("!H", len(wire)))
- conn.send(wire)
- except socket.error as e:
- # some of the tests are going to close
- # the connection on us, just deal with it
- break
+ request = dns.message.from_wire(data, ignore_trailing=ignoreTrailing)
+ except dns.message.TrailingJunk as e:
+ if trailingDataResponse is False or forceRcode is True:
+ raise
+ print("TCP query with trailing data, synthesizing response")
+ request = dns.message.from_wire(data, ignore_trailing=True)
+ forceRcode = trailingDataResponse
- conn.close()
+ if callback:
+ wire = callback(request)
+ else:
+ if request.edns > 1:
+ forceRcode = dns.rcode.BADVERS
+ response = cls._getResponse(request, fromQueue, toQueue, synthesize=forceRcode)
+ if response:
+ wire = response.to_wire(max_size=65535)
+
+ if not wire:
+ conn.close()
+ return
+ elif isinstance(wire, ResponderDropAction):
+ return
+
+ wireLen = struct.pack("!H", len(wire))
+ if partialWrite:
+ for b in wireLen:
+ conn.send(bytes([b]))
+ time.sleep(0.5)
+ else:
+ conn.send(wireLen)
+ conn.send(wire)
+
+ while multipleResponses:
+ # do not block, and stop as soon as the queue is empty, either the next response is already here or we are done
+ # otherwise we might read responses intended for the next connection
+ if fromQueue.empty():
+ break
+
+ response = fromQueue.get(False)
+ if not response:
+ break
+
+ response = copy.copy(response)
+ response.id = request.id
+ wire = response.to_wire(max_size=65535)
+ try:
+ conn.send(struct.pack("!H", len(wire)))
+ conn.send(wire)
+ except socket.error as e:
+ # some of the tests are going to close
+ # the connection on us, just deal with it
+ break
+
+ conn.close()
@classmethod
- def TCPResponder(cls, port, fromQueue, toQueue, trailingDataResponse=False, multipleResponses=False, callback=None, tlsContext=None, multipleConnections=False, listeningAddr='127.0.0.1', partialWrite=False, clientCert=False):
+ def TCPResponder(
+ cls,
+ port,
+ fromQueue,
+ toQueue,
+ trailingDataResponse=False,
+ multipleResponses=False,
+ callback=None,
+ tlsContext=None,
+ multipleConnections=False,
+ listeningAddr="127.0.0.1",
+ partialWrite=False,
+ clientCert=False,
+ ):
cls._backgroundThreads[threading.get_native_id()] = True
# trailingDataResponse=True means "ignore trailing data".
# Other values are either False (meaning "raise an exception")
sock.listen(100)
sock.settimeout(0.5)
if tlsContext:
- sock = tlsContext.wrap_socket(sock, server_side=True)
+ sock = tlsContext.wrap_socket(sock, server_side=True)
while True:
try:
- (conn, _) = sock.accept()
+ (conn, _) = sock.accept()
except ssl.SSLError:
- continue
+ continue
except ConnectionResetError:
- continue
- except socket.timeout:
- if cls._backgroundThreads.get(threading.get_native_id(), False) == False:
- del cls._backgroundThreads[threading.get_native_id()]
- break
- else:
continue
+ except socket.timeout:
+ if cls._backgroundThreads.get(threading.get_native_id(), False) == False:
+ del cls._backgroundThreads[threading.get_native_id()]
+ break
+ else:
+ continue
conn.settimeout(5.0)
if multipleConnections:
- thread = threading.Thread(name='TCP Connection Handler',
- target=cls.handleTCPConnection,
- args=[conn, fromQueue, toQueue, trailingDataResponse, multipleResponses, callback, partialWrite, clientCert])
- thread.daemon = True
- thread.start()
+ thread = threading.Thread(
+ name="TCP Connection Handler",
+ target=cls.handleTCPConnection,
+ args=[
+ conn,
+ fromQueue,
+ toQueue,
+ trailingDataResponse,
+ multipleResponses,
+ callback,
+ partialWrite,
+ clientCert,
+ ],
+ )
+ thread.daemon = True
+ thread.start()
else:
- cls.handleTCPConnection(conn, fromQueue, toQueue, trailingDataResponse, multipleResponses, callback, partialWrite, clientCert)
+ cls.handleTCPConnection(
+ conn,
+ fromQueue,
+ toQueue,
+ trailingDataResponse,
+ multipleResponses,
+ callback,
+ partialWrite,
+ clientCert,
+ )
sock.close()
+
class ResponderDropAction(object):
"""
An object to indicate a drop action shall be taken
"""
- pass
-
+ pass
from recursortests import RecursorTest
+
class APIAllowedRecursorTest(RecursorTest):
- _confdir = 'APIAllowedRecursor'
+ _confdir = "APIAllowedRecursor"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
webserver=yes
def testAPI(self):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/api/v1/servers/localhost/statistics"
r = requests.get(url, headers=headers, timeout=self._wsTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
+
class APIDeniedRecursorTest(RecursorTest):
- _confdir = 'APIDeniedRecursor'
+ _confdir = "APIDeniedRecursor"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
webserver=yes
def testAPI(self):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/api/v1/servers/localhost/statistics"
try:
requests.get(url, headers=headers, timeout=self._wsTimeout)
self.fail()
import dns
from recursortests import RecursorTest
+
class AdditionalsDefaultTest(RecursorTest):
- _confdir = 'AdditionalsDefault'
+ _confdir = "AdditionalsDefault"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
dnssec=validate
"""
def testMX(self):
- expected = dns.rrset.from_text('secure.example.', 0, dns.rdataclass.IN, 'MX', '10 mx1.secure.example.', '20 mx2.secure.example.')
- adds1 = dns.rrset.from_text('mx1.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.18')
- adds2 = dns.rrset.from_text('mx2.secure.example.', 0, dns.rdataclass.IN, 'AAAA', '1::2')
- query1 = dns.message.make_query('secure.example', 'MX', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "secure.example.", 0, dns.rdataclass.IN, "MX", "10 mx1.secure.example.", "20 mx2.secure.example."
+ )
+ adds1 = dns.rrset.from_text("mx1.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.18")
+ adds2 = dns.rrset.from_text("mx2.secure.example.", 0, dns.rdataclass.IN, "AAAA", "1::2")
+ query1 = dns.message.make_query("secure.example", "MX", want_dnssec=True)
query1.flags |= dns.flags.AD
- query2 = dns.message.make_query('mx1.secure.example', 'A', want_dnssec=True)
+ query2 = dns.message.make_query("mx1.secure.example", "A", want_dnssec=True)
query2.flags |= dns.flags.AD
- query3 = dns.message.make_query('mx2.secure.example', 'AAAA', want_dnssec=True)
+ query3 = dns.message.make_query("mx2.secure.example", "AAAA", want_dnssec=True)
query3.flags |= dns.flags.AD
res = self.sendUDPQuery(query1)
self.assertRRsetInAdditional(res, adds1)
self.assertRRsetInAdditional(res, adds2)
+
class AdditionalsResolveImmediatelyTest(RecursorTest):
- _confdir = 'AdditionalsResolveImmediately'
+ _confdir = "AdditionalsResolveImmediately"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
dnssec=validate
"""
def testMX(self):
- expected = dns.rrset.from_text('secure.example.', 0, dns.rdataclass.IN, 'MX', '10 mx1.secure.example.', '20 mx2.secure.example.')
- adds1 = dns.rrset.from_text('mx1.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.18')
- adds2 = dns.rrset.from_text('mx2.secure.example.', 0, dns.rdataclass.IN, 'AAAA', '1::2')
- query1 = dns.message.make_query('secure.example', 'MX', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "secure.example.", 0, dns.rdataclass.IN, "MX", "10 mx1.secure.example.", "20 mx2.secure.example."
+ )
+ adds1 = dns.rrset.from_text("mx1.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.18")
+ adds2 = dns.rrset.from_text("mx2.secure.example.", 0, dns.rdataclass.IN, "AAAA", "1::2")
+ query1 = dns.message.make_query("secure.example", "MX", want_dnssec=True)
query1.flags |= dns.flags.AD
res = self.sendUDPQuery(query1)
self.assertMatchingRRSIGInAdditional(res, adds2)
def testNAPTR(self):
- exp = dns.rrset.from_text('naptr.secure.example.', 0, dns.rdataclass.IN, 'NAPTR',
- '10 10 "s" "Z" "C" service2.secure.example.',
- '10 10 "s" "Y" "B" service1.secure.example.',
- '10 10 "a" "X" "A" s1.secure.example.');
- adds1 = dns.rrset.from_text('s1.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.19')
- adds2 = dns.rrset.from_text('service1.secure.example.', 0, dns.rdataclass.IN, 'SRV', '20 100 8080 a.secure.example.')
- adds3 = dns.rrset.from_text('service2.secure.example.', 0, dns.rdataclass.IN, 'SRV', '20 100 8080 b.secure.example.')
- adds4 = dns.rrset.from_text('a.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.20', '192.0.2.22')
- adds5 = dns.rrset.from_text('b.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.21')
- adds6 = dns.rrset.from_text('b.secure.example.', 0, dns.rdataclass.IN, 'AAAA', '1::3')
- adds7 = dns.rrset.from_text('s1.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.19')
+ exp = dns.rrset.from_text(
+ "naptr.secure.example.",
+ 0,
+ dns.rdataclass.IN,
+ "NAPTR",
+ '10 10 "s" "Z" "C" service2.secure.example.',
+ '10 10 "s" "Y" "B" service1.secure.example.',
+ '10 10 "a" "X" "A" s1.secure.example.',
+ )
+ adds1 = dns.rrset.from_text("s1.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.19")
+ adds2 = dns.rrset.from_text(
+ "service1.secure.example.", 0, dns.rdataclass.IN, "SRV", "20 100 8080 a.secure.example."
+ )
+ adds3 = dns.rrset.from_text(
+ "service2.secure.example.", 0, dns.rdataclass.IN, "SRV", "20 100 8080 b.secure.example."
+ )
+ adds4 = dns.rrset.from_text("a.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.20", "192.0.2.22")
+ adds5 = dns.rrset.from_text("b.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.21")
+ adds6 = dns.rrset.from_text("b.secure.example.", 0, dns.rdataclass.IN, "AAAA", "1::3")
+ adds7 = dns.rrset.from_text("s1.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.19")
- query1 = dns.message.make_query('naptr.secure.example', 'NAPTR', want_dnssec=True)
+ query1 = dns.message.make_query("naptr.secure.example", "NAPTR", want_dnssec=True)
query1.flags |= dns.flags.AD
res = self.sendUDPQuery(query1)
self.assertMessageIsAuthenticated(res)
self.assertRRsetInAdditional(res, adds7)
self.assertMatchingRRSIGInAdditional(res, adds7)
+
class AdditionalsResolveCacheOnlyTest(RecursorTest):
- _confdir = 'AdditionalsResolveCacheOnly'
+ _confdir = "AdditionalsResolveCacheOnly"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
dnssec=validate
"""
def testMX(self):
- expected = dns.rrset.from_text('secure.example.', 0, dns.rdataclass.IN, 'MX', '10 mx1.secure.example.', '20 mx2.secure.example.')
- adds1 = dns.rrset.from_text('mx1.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.18')
- adds2 = dns.rrset.from_text('mx2.secure.example.', 0, dns.rdataclass.IN, 'AAAA', '1::2')
- query1 = dns.message.make_query('secure.example', 'MX', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "secure.example.", 0, dns.rdataclass.IN, "MX", "10 mx1.secure.example.", "20 mx2.secure.example."
+ )
+ adds1 = dns.rrset.from_text("mx1.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.18")
+ adds2 = dns.rrset.from_text("mx2.secure.example.", 0, dns.rdataclass.IN, "AAAA", "1::2")
+ query1 = dns.message.make_query("secure.example", "MX", want_dnssec=True)
query1.flags |= dns.flags.AD
res = self.sendUDPQuery(query1)
self.assertRRsetInAdditional(res, adds2)
self.assertMatchingRRSIGInAdditional(res, adds1)
self.assertMatchingRRSIGInAdditional(res, adds2)
-
import pytest
import shutil
+
class AggressiveNSECCacheBase(RecursorTest):
__test__ = False
_wsPort = 8042
_wsTimeout = 10
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
dnssec=validate
@classmethod
def wipe(cls):
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
# Only wipe examples, as wiping the root triggers root NS refreshes
cls.wipeRecursorCache(confdir, "example$")
def getMetric(self, name):
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/api/v1/servers/localhost/statistics"
r = requests.get(url, headers=headers, timeout=self._wsTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
content = r.json()
for entry in content:
- if entry['name'] == name:
- return int(entry['value'])
+ if entry["name"] == name:
+ return int(entry["value"])
self.fail()
return -1
# https://github.com/PowerDNS/pdns/pull/12694
self.wipe()
- res = self.sendQuery('host1.sub.secure.example.', 'TXT')
+ res = self.sendQuery("host1.sub.secure.example.", "TXT")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 0)
- res = self.sendQuery('host1.sub.secure.example.', 'A')
+ res = self.sendQuery("host1.sub.secure.example.", "A")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMessageIsAuthenticated(res)
self.assertEqual(res.edns, 0)
self.wipe()
# first we query a nonexistent type, to get the NSEC in our cache
- entries = self.getMetric('aggressive-nsec-cache-entries')
- res = self.sendQuery('host1.secure.example.', 'TXT')
+ entries = self.getMetric("aggressive-nsec-cache-entries")
+ res = self.sendQuery("host1.secure.example.", "TXT")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
self.assertMessageIsAuthenticated(res)
- self.assertGreater(self.getMetric('aggressive-nsec-cache-entries'), entries)
+ self.assertGreater(self.getMetric("aggressive-nsec-cache-entries"), entries)
# now we ask for a different type, we should generate the answer from the NSEC,
# and no outgoing query should be made
- nbQueries = self.getMetric('all-outqueries')
- entries = self.getMetric('aggressive-nsec-cache-entries')
- res = self.sendQuery('host1.secure.example.', 'AAAA')
+ nbQueries = self.getMetric("all-outqueries")
+ entries = self.getMetric("aggressive-nsec-cache-entries")
+ res = self.sendQuery("host1.secure.example.", "AAAA")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
self.assertMessageIsAuthenticated(res)
- self.assertEqual(nbQueries, self.getMetric('all-outqueries'))
- self.assertEqual(self.getMetric('aggressive-nsec-cache-entries'), entries)
+ self.assertEqual(nbQueries, self.getMetric("all-outqueries"))
+ self.assertEqual(self.getMetric("aggressive-nsec-cache-entries"), entries)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(29, b'Result synthesized from aggressive NSEC cache (RFC8198)'))
+ self.assertEqual(
+ res.options[0],
+ extendederrors.ExtendedErrorOption(29, b"Result synthesized from aggressive NSEC cache (RFC8198)"),
+ )
+
class AggressiveNSECCacheNSECTest(AggressiveNSECCacheBase):
- _confdir = 'AggressiveNSECCacheNSEC'
+ _confdir = "AggressiveNSECCacheNSEC"
__test__ = True
# we can't use the same tests for NSEC and NSEC3 because the hashed NSEC3s
self.wipe()
# first we query a nonexistent name, to get the needed NSECs (name + widcard) in our cache
- entries = self.getMetric('aggressive-nsec-cache-entries')
- hits = self.getMetric('aggressive-nsec-cache-nsec-hits')
- res = self.sendQuery('host2.secure.example.', 'TXT')
+ entries = self.getMetric("aggressive-nsec-cache-entries")
+ hits = self.getMetric("aggressive-nsec-cache-nsec-hits")
+ res = self.sendQuery("host2.secure.example.", "TXT")
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
self.assertMessageIsAuthenticated(res)
- self.assertGreater(self.getMetric('aggressive-nsec-cache-entries'), entries)
- self.assertEqual(self.getMetric('aggressive-nsec-cache-nsec-hits'), hits)
+ self.assertGreater(self.getMetric("aggressive-nsec-cache-entries"), entries)
+ self.assertEqual(self.getMetric("aggressive-nsec-cache-nsec-hits"), hits)
# now we ask for a different name that is covered by the NSEC,
# we should generate the answer from the NSEC and no outgoing query should be made
- nbQueries = self.getMetric('all-outqueries')
- entries = self.getMetric('aggressive-nsec-cache-entries')
- hits = self.getMetric('aggressive-nsec-cache-nsec-hits')
- res = self.sendQuery('host3.secure.example.', 'AAAA')
+ nbQueries = self.getMetric("all-outqueries")
+ entries = self.getMetric("aggressive-nsec-cache-entries")
+ hits = self.getMetric("aggressive-nsec-cache-nsec-hits")
+ res = self.sendQuery("host3.secure.example.", "AAAA")
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
self.assertMessageIsAuthenticated(res)
- self.assertEqual(nbQueries, self.getMetric('all-outqueries'))
- self.assertEqual(self.getMetric('aggressive-nsec-cache-entries'), entries)
- self.assertGreater(self.getMetric('aggressive-nsec-cache-nsec-hits'), hits)
+ self.assertEqual(nbQueries, self.getMetric("all-outqueries"))
+ self.assertEqual(self.getMetric("aggressive-nsec-cache-entries"), entries)
+ self.assertGreater(self.getMetric("aggressive-nsec-cache-nsec-hits"), hits)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(29, b'Result synthesized from aggressive NSEC cache (RFC8198)'))
+ self.assertEqual(
+ res.options[0],
+ extendederrors.ExtendedErrorOption(29, b"Result synthesized from aggressive NSEC cache (RFC8198)"),
+ )
def testWildcard(self):
self.wipe()
# first we query a nonexistent name, but for which a wildcard matches,
# to get the NSEC in our cache
- res = self.sendQuery('test1.wildcard.secure.example.', 'A')
- expected = dns.rrset.from_text('test1.wildcard.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
+ res = self.sendQuery("test1.wildcard.secure.example.", "A")
+ expected = dns.rrset.from_text(
+ "test1.wildcard.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMatchingRRSIGInAnswer(res, expected)
self.assertMessageIsAuthenticated(res)
# now we ask for a different name, we should generate the answer from the NSEC and the wildcard,
# and no outgoing query should be made
- hits = self.getMetric('aggressive-nsec-cache-nsec-wc-hits')
- nbQueries = self.getMetric('all-outqueries')
- res = self.sendQuery('test2.wildcard.secure.example.', 'A')
- expected = dns.rrset.from_text('test2.wildcard.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
+ hits = self.getMetric("aggressive-nsec-cache-nsec-wc-hits")
+ nbQueries = self.getMetric("all-outqueries")
+ res = self.sendQuery("test2.wildcard.secure.example.", "A")
+ expected = dns.rrset.from_text(
+ "test2.wildcard.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMatchingRRSIGInAnswer(res, expected)
self.assertMessageIsAuthenticated(res)
- self.assertEqual(nbQueries, self.getMetric('all-outqueries'))
- self.assertGreater(self.getMetric('aggressive-nsec-cache-nsec-wc-hits'), hits)
+ self.assertEqual(nbQueries, self.getMetric("all-outqueries"))
+ self.assertGreater(self.getMetric("aggressive-nsec-cache-nsec-wc-hits"), hits)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(29, b'Result synthesized from aggressive NSEC cache (RFC8198)'))
+ self.assertEqual(
+ res.options[0],
+ extendederrors.ExtendedErrorOption(29, b"Result synthesized from aggressive NSEC cache (RFC8198)"),
+ )
# now we ask for a type that does not exist at the wildcard
- hits = self.getMetric('aggressive-nsec-cache-nsec-hits')
- nbQueries = self.getMetric('all-outqueries')
- res = self.sendQuery('test1.wildcard.secure.example.', 'AAAA')
+ hits = self.getMetric("aggressive-nsec-cache-nsec-hits")
+ nbQueries = self.getMetric("all-outqueries")
+ res = self.sendQuery("test1.wildcard.secure.example.", "AAAA")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
self.assertMessageIsAuthenticated(res)
- self.assertEqual(nbQueries, self.getMetric('all-outqueries'))
- self.assertGreater(self.getMetric('aggressive-nsec-cache-nsec-hits'), hits)
+ self.assertEqual(nbQueries, self.getMetric("all-outqueries"))
+ self.assertGreater(self.getMetric("aggressive-nsec-cache-nsec-hits"), hits)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(29, b'Result synthesized from aggressive NSEC cache (RFC8198)'))
+ self.assertEqual(
+ res.options[0],
+ extendederrors.ExtendedErrorOption(29, b"Result synthesized from aggressive NSEC cache (RFC8198)"),
+ )
# we can also ask a different type, for a different name that is covered
# by the NSEC and matches the wildcard (but the type does not exist)
- hits = self.getMetric('aggressive-nsec-cache-nsec-wc-hits')
- nbQueries = self.getMetric('all-outqueries')
- res = self.sendQuery('test3.wildcard.secure.example.', 'TXT')
+ hits = self.getMetric("aggressive-nsec-cache-nsec-wc-hits")
+ nbQueries = self.getMetric("all-outqueries")
+ res = self.sendQuery("test3.wildcard.secure.example.", "TXT")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
self.assertMessageIsAuthenticated(res)
- self.assertEqual(nbQueries, self.getMetric('all-outqueries'))
- self.assertGreater(self.getMetric('aggressive-nsec-cache-nsec-hits'), hits)
+ self.assertEqual(nbQueries, self.getMetric("all-outqueries"))
+ self.assertGreater(self.getMetric("aggressive-nsec-cache-nsec-hits"), hits)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(29, b'Result synthesized from aggressive NSEC cache (RFC8198)'))
+ self.assertEqual(
+ res.options[0],
+ extendederrors.ExtendedErrorOption(29, b"Result synthesized from aggressive NSEC cache (RFC8198)"),
+ )
def test_Bogus(self):
self.wipe()
# query a name in example to fill the aggressive negcache
- res = self.sendQuery('example.', 'A')
+ res = self.sendQuery("example.", "A")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnswerEmpty(res)
- self.assertEqual(1, self.getMetric('aggressive-nsec-cache-entries'))
+ self.assertEqual(1, self.getMetric("aggressive-nsec-cache-entries"))
# query a name in a Bogus zone
- res = self.sendQuery('ted1.bogus.example.', 'A')
+ res = self.sendQuery("ted1.bogus.example.", "A")
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertAnswerEmpty(res)
# disable validation
- msg = dns.message.make_query('ted1.bogus.example.', 'A', want_dnssec=True)
+ msg = dns.message.make_query("ted1.bogus.example.", "A", want_dnssec=True)
msg.flags |= dns.flags.CD
res = self.sendUDPQuery(msg)
self.assertAuthorityHasSOA(res)
# check that we _do not_ use the aggressive NSEC cache
- nbQueries = self.getMetric('all-outqueries')
- msg = dns.message.make_query('ted2.bogus.example.', 'A', want_dnssec=True)
+ nbQueries = self.getMetric("all-outqueries")
+ msg = dns.message.make_query("ted2.bogus.example.", "A", want_dnssec=True)
msg.flags |= dns.flags.CD
res = self.sendUDPQuery(msg)
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
- self.assertGreater(self.getMetric('all-outqueries'), nbQueries)
+ self.assertGreater(self.getMetric("all-outqueries"), nbQueries)
# Check that we still have one aggressive cache entry
- self.assertEqual(1, self.getMetric('aggressive-nsec-cache-entries'))
+ self.assertEqual(1, self.getMetric("aggressive-nsec-cache-entries"))
print(res.options)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(9, b''))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(9, b""))
+
class AggressiveNSECCacheNSEC3Test(AggressiveNSECCacheBase):
- _confdir = 'AggressiveNSECCacheNSEC3'
+ _confdir = "AggressiveNSECCacheNSEC3"
__test__ = True
@classmethod
@classmethod
def tearDownClass(cls):
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
print("Specialized auth teardown " + confdir)
# tear down specialized auths, and then start standard ones
super().tearDownClass(True)
print("Starting default auths")
- confdir = 'configs/auths'
+ confdir = "configs/auths"
shutil.rmtree(confdir, True)
os.mkdir(confdir)
# Be careful here, we don't want the overridden secureZone(), so call RecursorTest explicitly
@classmethod
def secureZone(cls, confdir, zonename, key=None):
- zone = '.' if zonename == 'ROOT' else zonename
+ zone = "." if zonename == "ROOT" else zonename
if not key:
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'secure-zone',
- zone]
+ pdnsutilCmd = [os.environ["PDNSUTIL"], "--config-dir=%s" % confdir, "secure-zone", zone]
else:
- keyfile = os.path.join(confdir, 'dnssec.key')
- with open(keyfile, 'w') as fdKeyfile:
+ keyfile = os.path.join(confdir, "dnssec.key")
+ with open(keyfile, "w") as fdKeyfile:
fdKeyfile.write(key)
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'import-zone-key',
- zone,
- keyfile,
- 'active',
- 'ksk']
-
- print(' '.join(pdnsutilCmd))
+ pdnsutilCmd = [
+ os.environ["PDNSUTIL"],
+ "--config-dir=%s" % confdir,
+ "import-zone-key",
+ zone,
+ keyfile,
+ "active",
+ "ksk",
+ ]
+
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
params = "1 0 100 AABBCCDDEEFF112233"
if zone == "optout.example":
params = "1 1 100 AABBCCDDEEFF112233"
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'set-nsec3',
- zone,
- params]
+ pdnsutilCmd = [os.environ["PDNSUTIL"], "--config-dir=%s" % confdir, "set-nsec3", zone, params]
- print(' '.join(pdnsutilCmd))
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
def testNXD(self):
self.wipe()
# first we query a nonexistent name, to get the needed NSEC3s in our cache
- res = self.sendQuery('host2.secure.example.', 'TXT')
+ res = self.sendQuery("host2.secure.example.", "TXT")
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
# now we ask for a different name that is covered by the NSEC3s,
# we should generate the answer from the NSEC3s and no outgoing query should be made
- nbQueries = self.getMetric('all-outqueries')
- res = self.sendQuery('host6.secure.example.', 'AAAA')
+ nbQueries = self.getMetric("all-outqueries")
+ res = self.sendQuery("host6.secure.example.", "AAAA")
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
self.assertMessageIsAuthenticated(res)
- self.assertEqual(nbQueries, self.getMetric('all-outqueries'))
+ self.assertEqual(nbQueries, self.getMetric("all-outqueries"))
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(29, b'Result synthesized from aggressive NSEC cache (RFC8198)'))
+ self.assertEqual(
+ res.options[0],
+ extendederrors.ExtendedErrorOption(29, b"Result synthesized from aggressive NSEC cache (RFC8198)"),
+ )
def testWildcard(self):
self.wipe()
# first let's get the SOA and wildcard NSEC in our cache by asking a name that matches the wildcard
# but a type that does not exist
- res = self.sendQuery('test1.wildcard.secure.example.', 'AAAA')
+ res = self.sendQuery("test1.wildcard.secure.example.", "AAAA")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
# we query a nonexistent name, but for which a wildcard matches,
# to get the NSEC3 in our cache
- res = self.sendQuery('test5.wildcard.secure.example.', 'A')
- expected = dns.rrset.from_text('test5.wildcard.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
+ res = self.sendQuery("test5.wildcard.secure.example.", "A")
+ expected = dns.rrset.from_text(
+ "test5.wildcard.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMatchingRRSIGInAnswer(res, expected)
self.assertMessageIsAuthenticated(res)
# now we ask for a different name, we should generate the answer from the NSEC3s and the wildcard,
# and no outgoing query should be made
- nbQueries = self.getMetric('all-outqueries')
- res = self.sendQuery('test6.wildcard.secure.example.', 'A')
- expected = dns.rrset.from_text('test6.wildcard.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
+ nbQueries = self.getMetric("all-outqueries")
+ res = self.sendQuery("test6.wildcard.secure.example.", "A")
+ expected = dns.rrset.from_text(
+ "test6.wildcard.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertMatchingRRSIGInAnswer(res, expected)
self.assertMessageIsAuthenticated(res)
- self.assertEqual(nbQueries, self.getMetric('all-outqueries'))
+ self.assertEqual(nbQueries, self.getMetric("all-outqueries"))
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(29, b'Result synthesized from aggressive NSEC cache (RFC8198)'))
+ self.assertEqual(
+ res.options[0],
+ extendederrors.ExtendedErrorOption(29, b"Result synthesized from aggressive NSEC cache (RFC8198)"),
+ )
# now we ask for a type that does not exist at the wildcard
- nbQueries = self.getMetric('all-outqueries')
- res = self.sendQuery('test5.wildcard.secure.example.', 'AAAA')
+ nbQueries = self.getMetric("all-outqueries")
+ res = self.sendQuery("test5.wildcard.secure.example.", "AAAA")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
self.assertMessageIsAuthenticated(res)
- self.assertEqual(nbQueries, self.getMetric('all-outqueries'))
+ self.assertEqual(nbQueries, self.getMetric("all-outqueries"))
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(29, b'Result synthesized from aggressive NSEC cache (RFC8198)'))
+ self.assertEqual(
+ res.options[0],
+ extendederrors.ExtendedErrorOption(29, b"Result synthesized from aggressive NSEC cache (RFC8198)"),
+ )
# we can also ask a different type, for a different name that is covered
# by the NSEC3s and matches the wildcard (but the type does not exist)
- nbQueries = self.getMetric('all-outqueries')
- res = self.sendQuery('test6.wildcard.secure.example.', 'TXT')
+ nbQueries = self.getMetric("all-outqueries")
+ res = self.sendQuery("test6.wildcard.secure.example.", "TXT")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
self.assertMessageIsAuthenticated(res)
- self.assertEqual(nbQueries, self.getMetric('all-outqueries'))
+ self.assertEqual(nbQueries, self.getMetric("all-outqueries"))
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(29, b'Result synthesized from aggressive NSEC cache (RFC8198)'))
+ self.assertEqual(
+ res.options[0],
+ extendederrors.ExtendedErrorOption(29, b"Result synthesized from aggressive NSEC cache (RFC8198)"),
+ )
def test_OptOut(self):
self.wipe()
# query a name in an opt-out zone
- res = self.sendQuery('ns2.optout.example.', 'A')
+ res = self.sendQuery("ns2.optout.example.", "A")
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
# check that we _do not_ use the aggressive NSEC cache
- nbQueries = self.getMetric('all-outqueries')
- res = self.sendQuery('ns3.optout.example.', 'A')
+ nbQueries = self.getMetric("all-outqueries")
+ res = self.sendQuery("ns3.optout.example.", "A")
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertAnswerEmpty(res)
self.assertAuthorityHasSOA(res)
- self.assertGreater(self.getMetric('all-outqueries'), nbQueries)
+ self.assertGreater(self.getMetric("all-outqueries"), nbQueries)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 0)
import socket
from recursortests import RecursorTest
+
class AnyBindTest(RecursorTest):
- _confdir = 'AnyBind'
+ _confdir = "AnyBind"
_auth_zones = RecursorTest._default_auth_zones
- _config_template = """dnssec=validate
+ _config_template = (
+ """dnssec=validate
local-address=0.0.0.0
-auth-zones=authzone.example=configs/%s/authzone.zone""" % _confdir
+auth-zones=authzone.example=configs/%s/authzone.zone"""
+ % _confdir
+ )
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'authzone.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN authzone.example.
+ authzonepath = os.path.join(confdir, "authzone.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN authzone.example.
@ 3600 IN SOA {soa}
@ 3600 IN A 192.0.2.88
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(AnyBindTest, cls).generateRecursorConfig(confdir)
@classmethod
def testA(self):
"""Test to see if we get a reply from 127.0.0.2 if rec is bound to ANY address"""
- expected = dns.rrset.from_text('ns.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.9'.format(prefix=self._PREFIX))
- query = dns.message.make_query('ns.secure.example', 'A', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "ns.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.9".format(prefix=self._PREFIX)
+ )
+ query = dns.message.make_query("ns.secure.example", "A", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
self.assertMessageIsAuthenticated(res)
self.assertRRsetInAnswer(res, expected)
self.assertMatchingRRSIGInAnswer(res, expected)
-
from recursortests import RecursorTest
+
class CarbonTest(RecursorTest):
- _confdir = 'Carbon'
- _carbonNamespace = 'NS'
- _carbonInstance = 'Instance'
+ _confdir = "Carbon"
+ _carbonNamespace = "NS"
+ _carbonInstance = "Instance"
_carbonServerName = "carbonname1"
_carbonInterval = 1
_carbonServer1Port = 8000
carbon-interval=%s
carbon-ourname=%s
carbon-server=127.0.0.1:%s,127.0.01:%s
- """ % (_carbonNamespace, _carbonInstance, _carbonInterval, _carbonServerName, _carbonServer1Port, _carbonServer2Port)
+ """ % (
+ _carbonNamespace,
+ _carbonInstance,
+ _carbonInterval,
+ _carbonServerName,
+ _carbonServer1Port,
+ _carbonServer2Port,
+ )
@classmethod
def CarbonResponder(cls, port):
while True:
(conn, _) = sock.accept()
conn.settimeout(2.0)
- lines = b''
+ lines = b""
while True:
data = conn.recv(4096)
if not data:
@classmethod
def startResponders(cls):
- cls._CarbonResponder1 = threading.Thread(name='Carbon Responder 1', target=cls.CarbonResponder, args=[cls._carbonServer1Port])
+ cls._CarbonResponder1 = threading.Thread(
+ name="Carbon Responder 1", target=cls.CarbonResponder, args=[cls._carbonServer1Port]
+ )
cls._CarbonResponder1.daemon = True
cls._CarbonResponder1.start()
- cls._CarbonResponder2 = threading.Thread(name='Carbon Responder 2', target=cls.CarbonResponder, args=[cls._carbonServer2Port])
+ cls._CarbonResponder2 = threading.Thread(
+ name="Carbon Responder 2", target=cls.CarbonResponder, args=[cls._carbonServer2Port]
+ )
cls._CarbonResponder2.daemon = True
cls._CarbonResponder2.start()
self.assertTrue(data1)
self.assertGreater(len(data1.splitlines()), 1)
- expectedStart = b"%s.%s.%s." % (self._carbonNamespace.encode('UTF8'), self._carbonServerName.encode('UTF-8'), self._carbonInstance.encode('UTF8'))
+ expectedStart = b"%s.%s.%s." % (
+ self._carbonNamespace.encode("UTF8"),
+ self._carbonServerName.encode("UTF-8"),
+ self._carbonInstance.encode("UTF8"),
+ )
for line in data1.splitlines():
self.assertTrue(line.startswith(expectedStart))
- parts = line.split(b' ')
+ parts = line.split(b" ")
self.assertEqual(len(parts), 3)
self.assertTrue(parts[1].isdigit())
self.assertTrue(parts[2].isdigit())
self.assertTrue(data2)
self.assertGreater(len(data2.splitlines()), 1)
- expectedStart = b"%s.%s.%s." % (self._carbonNamespace.encode('UTF8'), self._carbonServerName.encode('UTF-8'), self._carbonInstance.encode('UTF8'))
+ expectedStart = b"%s.%s.%s." % (
+ self._carbonNamespace.encode("UTF8"),
+ self._carbonServerName.encode("UTF-8"),
+ self._carbonInstance.encode("UTF8"),
+ )
for line in data2.splitlines():
self.assertTrue(line.startswith(expectedStart))
- parts = line.split(b' ')
+ parts = line.split(b" ")
self.assertEqual(len(parts), 3)
self.assertTrue(parts[1].isdigit())
self.assertTrue(parts[2].isdigit())
for key in self._carbonCounters:
value = self._carbonCounters[key]
self.assertGreaterEqual(value, 1)
-
import clientsubnetoption
from recursortests import RecursorTest
+
class ChainTest(RecursorTest):
"""
These regression tests test the chaining of outgoing requests.
"""
+
_auth_zones = RecursorTest._default_auth_zones
_chainSize = 200
- _confdir = 'Chain'
+ _confdir = "Chain"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """dnssec=validate
trace=no
clashing waiter ids.
"""
count = self._chainSize
- name = '9.delay1.example.'
- exp = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', 'a')
+ name = "9.delay1.example."
+ exp = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "TXT", "a")
queries = []
for i in range(count):
- query = dns.message.make_query(name, 'TXT', want_dnssec=True)
+ query = dns.message.make_query(name, "TXT", want_dnssec=True)
query.flags |= dns.flags.AD
queries.append(query)
self.assertRRsetInAnswer(res, exp)
self.assertMatchingRRSIGInAnswer(res, exp)
- self.checkMetrics({
- 'max-chain-length': count - 1, # first request has count - 1 requests chained to it
- 'servfail-answers': 0,
- 'noerror-answers': count,
- })
+ self.checkMetrics(
+ {
+ "max-chain-length": count - 1, # first request has count - 1 requests chained to it
+ "servfail-answers": 0,
+ "noerror-answers": count,
+ }
+ )
+
class ChainECSTest(RecursorTest):
"""
These regression tests test the chaining of outgoing requests with ECS
"""
+
_auth_zones = RecursorTest._default_auth_zones
_chainSize = 200
- _confdir = 'ChainECS'
+ _confdir = "ChainECS"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """dnssec=validate
trace=no
clashing waiter ids.
"""
count = self._chainSize
- name1 = '1.delay1.example.'
- name2 = '2.delay1.example.'
- exp1 = dns.rrset.from_text(name1, 0, dns.rdataclass.IN, 'TXT', 'a')
- exp2 = dns.rrset.from_text(name2, 0, dns.rdataclass.IN, 'TXT', 'a')
+ name1 = "1.delay1.example."
+ name2 = "2.delay1.example."
+ exp1 = dns.rrset.from_text(name1, 0, dns.rdataclass.IN, "TXT", "a")
+ exp2 = dns.rrset.from_text(name2, 0, dns.rdataclass.IN, "TXT", "a")
queries = []
for i in range(count):
if i % 3 == 0:
name = name1
else:
- name = name2
+ name = name2
if i % 2 == 0:
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.0', 24)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.0", 24)
else:
- ecso = clientsubnetoption.ClientSubnetOption('192.0.3.0', 24)
- query = dns.message.make_query(name, 'TXT', use_edns=True, options=[ecso], want_dnssec=True)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.3.0", 24)
+ query = dns.message.make_query(name, "TXT", use_edns=True, options=[ecso], want_dnssec=True)
query.flags |= dns.flags.AD
queries.append(query)
print("?? " + res.question[0].name.to_text())
self.assertEqual(0, 1)
- self.checkMetrics({
- 'servfail-answers': 0,
- 'noerror-answers': count,
- })
+ self.checkMetrics(
+ {
+ "servfail-answers": 0,
+ "noerror-answers": count,
+ }
+ )
+
class ChainECSHardenedTest(RecursorTest):
"""
These regression tests test the chaining of outgoing requests with ECS
"""
+
_auth_zones = RecursorTest._default_auth_zones
_chainSize = 200
- _confdir = 'ChainECSHardened'
+ _confdir = "ChainECSHardened"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """dnssec=validate
trace=no
clashing waiter ids.
"""
count = self._chainSize
- name1 = '1.delay1.example.'
- name2 = '2.delay1.example.'
- exp1 = dns.rrset.from_text(name1, 0, dns.rdataclass.IN, 'TXT', 'a')
- exp2 = dns.rrset.from_text(name2, 0, dns.rdataclass.IN, 'TXT', 'a')
+ name1 = "1.delay1.example."
+ name2 = "2.delay1.example."
+ exp1 = dns.rrset.from_text(name1, 0, dns.rdataclass.IN, "TXT", "a")
+ exp2 = dns.rrset.from_text(name2, 0, dns.rdataclass.IN, "TXT", "a")
queries = []
for i in range(count):
if i % 3 == 0:
name = name1
else:
- name = name2
+ name = name2
if i % 2 == 0:
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.0', 24)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.0", 24)
else:
- ecso = clientsubnetoption.ClientSubnetOption('192.0.3.0', 24)
- query = dns.message.make_query(name, 'TXT', use_edns=True, options=[ecso], want_dnssec=True)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.3.0", 24)
+ query = dns.message.make_query(name, "TXT", use_edns=True, options=[ecso], want_dnssec=True)
query.flags |= dns.flags.AD
queries.append(query)
print("?? " + res.question[0].name.to_text())
self.assertEqual(0, 1)
- self.checkMetrics({
- 'servfail-answers': 0,
- 'noerror-answers': count,
- })
-
+ self.checkMetrics(
+ {
+ "servfail-answers": 0,
+ "noerror-answers": count,
+ }
+ )
cookieReactorRunning = False
+
class CookiesTest(RecursorTest):
- _confdir = 'Cookies'
+ _confdir = "Cookies"
_config_template = """
recursor:
forward_zones:
cookies: true
packetcache:
disable: true
-""" % (os.environ['PREFIX'], os.environ['PREFIX'])
+""" % (os.environ["PREFIX"], os.environ["PREFIX"])
- _expectedCookies = 'no'
+ _expectedCookies = "no"
@classmethod
def generateRecursorConfig(cls, confdir):
cls.startResponders()
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
cls.createConfigDir(confdir)
cls.generateRecursorConfig(confdir)
global cookieReactorRunning
print("Launching responders..")
- address1 = cls._PREFIX + '.25'
- address2 = cls._PREFIX + '.26'
+ address1 = cls._PREFIX + ".25"
+ address2 = cls._PREFIX + ".26"
port = 53
if not cookieReactorRunning:
cls.startReactor()
- def checkCookies(self, support, server='127.0.0.25'):
- confdir = os.path.join('configs', self._confdir)
- output = self.recControl(confdir, 'dump-cookies', '-')
+ def checkCookies(self, support, server="127.0.0.25"):
+ confdir = os.path.join("configs", self._confdir)
+ output = self.recControl(confdir, "dump-cookies", "-")
for line in output.splitlines():
tokens = line.split()
if tokens[0] != server:
continue
- #print(tokens)
+ # print(tokens)
self.assertEqual(len(tokens), 5)
self.assertEqual(tokens[3], support)
def checkAtLeastOneCookies(self, support):
- confdir = os.path.join('configs', self._confdir)
- output = self.recControl(confdir, 'dump-cookies', '-')
+ confdir = os.path.join("configs", self._confdir)
+ output = self.recControl(confdir, "dump-cookies", "-")
ok = False
for line in output.splitlines():
tokens = line.split()
continue
if tokens[3] == support:
ok = True
- assert(ok)
+ assert ok
def testAuthDoesnotSendCookies(self):
# Case: rec does not get a cookie back
- expected = dns.rrset.from_text('unsupported.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
- query = dns.message.make_query('unsupported.cookies.example.', 'A')
+ expected = dns.rrset.from_text("unsupported.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
+ query = dns.message.make_query("unsupported.cookies.example.", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
- self.checkCookies('Unsupported')
+ self.checkCookies("Unsupported")
def testAuthRepliesWithCookie(self):
- confdir = os.path.join('configs', self._confdir)
+ confdir = os.path.join("configs", self._confdir)
# Case: rec gets a proper client and server cookie back
- self.recControl(confdir, 'clear-cookies', '*')
- tcp1 = self.recControl(confdir, 'get tcp-outqueries')
- query = dns.message.make_query('supported.cookies.example.', 'A')
- expected = dns.rrset.from_text('supported.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ self.recControl(confdir, "clear-cookies", "*")
+ tcp1 = self.recControl(confdir, "get tcp-outqueries")
+ query = dns.message.make_query("supported.cookies.example.", "A")
+ expected = dns.rrset.from_text("supported.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
- self.checkAtLeastOneCookies('Supported')
- tcp2 = self.recControl(confdir, 'get tcp-outqueries')
+ self.checkAtLeastOneCookies("Supported")
+ tcp2 = self.recControl(confdir, "get tcp-outqueries")
self.assertEqual(tcp1, tcp2)
# Case: we get a correct client and server cookie back
# We do not clear the cookie tables, so the old server cookie gets re-used
- query = dns.message.make_query('supported2.cookies.example.', 'A')
- expected = dns.rrset.from_text('supported2.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ query = dns.message.make_query("supported2.cookies.example.", "A")
+ expected = dns.rrset.from_text("supported2.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
- self.checkAtLeastOneCookies('Supported')
+ self.checkAtLeastOneCookies("Supported")
def testAuthSendsIncorrectClientCookie(self):
- confdir = os.path.join('configs', self._confdir)
+ confdir = os.path.join("configs", self._confdir)
# Case: rec gets an incorrect client cookie back, we ignore that and go to TCP
- self.recControl(confdir, 'clear-cookies', '*')
- tcp1 = self.recControl(confdir, 'get tcp-outqueries')
- query = dns.message.make_query('wrongcc.cookies.example.', 'A')
- expected = dns.rrset.from_text('wrongcc.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ self.recControl(confdir, "clear-cookies", "*")
+ tcp1 = self.recControl(confdir, "get tcp-outqueries")
+ query = dns.message.make_query("wrongcc.cookies.example.", "A")
+ expected = dns.rrset.from_text("wrongcc.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
- self.checkCookies('Probing')
- tcp2 = int(self.recControl(confdir, 'get tcp-outqueries'))
+ self.checkCookies("Probing")
+ tcp2 = int(self.recControl(confdir, "get tcp-outqueries"))
self.assertEqual(int(tcp1) + 1, int(tcp2))
def testAuthSendsBADCOOKIEOverUDP(self):
- confdir = os.path.join('configs', self._confdir)
+ confdir = os.path.join("configs", self._confdir)
# Case: rec gets a BADCOOKIE, even on retry and should fall back to TCP
- self.recControl(confdir, 'clear-cookies', '*')
- tcp1 = self.recControl(confdir, 'get tcp-outqueries')
- query = dns.message.make_query('badcookie.cookies.example.', 'A')
- expected = dns.rrset.from_text('badcookie.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ self.recControl(confdir, "clear-cookies", "*")
+ tcp1 = self.recControl(confdir, "get tcp-outqueries")
+ query = dns.message.make_query("badcookie.cookies.example.", "A")
+ expected = dns.rrset.from_text("badcookie.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
- self.checkCookies('Supported')
- tcp2 = int(self.recControl(confdir, 'get tcp-outqueries'))
+ self.checkCookies("Supported")
+ tcp2 = int(self.recControl(confdir, "get tcp-outqueries"))
self.assertEqual(int(tcp1) + 1, int(tcp2))
def testAuthSendsMalformedCookie(self):
- confdir = os.path.join('configs', self._confdir)
+ confdir = os.path.join("configs", self._confdir)
# Case: rec gets a malformed cookie, should ignore packet
- self.recControl(confdir, 'clear-cookies', '*')
- query = dns.message.make_query('malformed.cookies.example.', 'A')
- expected = dns.rrset.from_text('malformed.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ self.recControl(confdir, "clear-cookies", "*")
+ query = dns.message.make_query("malformed.cookies.example.", "A")
+ expected = dns.rrset.from_text("malformed.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
- self.checkCookies('Probing', '127.0.0.25')
- self.checkCookies('Supported', '127.0.0.26')
+ self.checkCookies("Probing", "127.0.0.25")
+ self.checkCookies("Supported", "127.0.0.26")
def testForgottenCookie(self):
- confdir = os.path.join('configs', self._confdir)
+ confdir = os.path.join("configs", self._confdir)
# Case: rec gets a proper client and server cookie back
- self.recControl(confdir, 'clear-cookies', '*')
- query = dns.message.make_query('supported3.cookies.example.', 'A')
- expected = dns.rrset.from_text('supported3.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ self.recControl(confdir, "clear-cookies", "*")
+ query = dns.message.make_query("supported3.cookies.example.", "A")
+ expected = dns.rrset.from_text("supported3.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
- self.checkCookies('Supported')
+ self.checkCookies("Supported")
# Case: we get a correct client and server cookie back
# We HAVE cleared the cookie tables, so the old server cookie is forgotten
- self.recControl(confdir, 'clear-cookies', '*')
- query = dns.message.make_query('supported4.cookies.example.', 'A')
- expected = dns.rrset.from_text('supported4.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ self.recControl(confdir, "clear-cookies", "*")
+ query = dns.message.make_query("supported4.cookies.example.", "A")
+ expected = dns.rrset.from_text("supported4.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
- self.checkCookies('Supported')
+ self.checkCookies("Supported")
+
class UDPResponder(DatagramProtocol):
def getCookie(self, message):
def createCookie(self, clientcookie):
clientcookie = clientcookie[0:8]
timestamp = int(time.time())
- server = clientcookie + b'\x01\x00\x00\x00' + timestamp.to_bytes(4, 'big')
- h = hash(server + b'\x01\x00\x00\x7f' + b'secret') % pow(2, 64)
- full = dns.edns.GenericOption(dns.edns.COOKIE, server + h.to_bytes(8, 'big'))
+ server = clientcookie + b"\x01\x00\x00\x00" + timestamp.to_bytes(4, "big")
+ h = hash(server + b"\x01\x00\x00\x7f" + b"secret") % pow(2, 64)
+ full = dns.edns.GenericOption(dns.edns.COOKIE, server + h.to_bytes(8, "big"))
return full
def question(self, datagram, tcp=False):
question = request.question[0]
# Case: do not send cookie back
- if question.name == dns.name.from_text('unsupported.cookies.example.') and question.rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('unsupported.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ if question.name == dns.name.from_text("unsupported.cookies.example.") and question.rdtype == dns.rdatatype.A:
+ answer = dns.rrset.from_text("unsupported.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
response.answer.append(answer)
# Case: do send cookie back
- elif question.name == dns.name.from_text('supported.cookies.example.') and question.rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('supported.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ elif question.name == dns.name.from_text("supported.cookies.example.") and question.rdtype == dns.rdatatype.A:
+ answer = dns.rrset.from_text("supported.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
clientcookie = self.getCookie(request)
if clientcookie is not None:
- response.use_edns(options = [self.createCookie(clientcookie)])
+ response.use_edns(options=[self.createCookie(clientcookie)])
response.answer.append(answer)
# We get a good client and server cookie
- elif question.name == dns.name.from_text('supported2.cookies.example.') and question.rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('supported2.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ elif question.name == dns.name.from_text("supported2.cookies.example.") and question.rdtype == dns.rdatatype.A:
+ answer = dns.rrset.from_text("supported2.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
clientcookie = self.getCookie(request)
if clientcookie is not None:
- response.use_edns(options = [self.createCookie(clientcookie)])
+ response.use_edns(options=[self.createCookie(clientcookie)])
response.answer.append(answer)
# Case: do send cookie back
- elif question.name == dns.name.from_text('supported3.cookies.example.') and question.rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('supported3.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ elif question.name == dns.name.from_text("supported3.cookies.example.") and question.rdtype == dns.rdatatype.A:
+ answer = dns.rrset.from_text("supported3.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
clientcookie = self.getCookie(request)
if clientcookie is not None:
- response.use_edns(options = [self.createCookie(clientcookie)])
+ response.use_edns(options=[self.createCookie(clientcookie)])
response.answer.append(answer)
# We get a new client cookie as the cookie store was cleared
- elif question.name == dns.name.from_text('supported4.cookies.example.') and question.rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('supported4.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ elif question.name == dns.name.from_text("supported4.cookies.example.") and question.rdtype == dns.rdatatype.A:
+ answer = dns.rrset.from_text("supported4.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
clientcookie = self.getCookie(request)
if clientcookie is not None:
- response.use_edns(options = [self.createCookie(clientcookie)])
+ response.use_edns(options=[self.createCookie(clientcookie)])
response.answer.append(answer)
# Case: do send incorrect client cookie back
- elif question.name == dns.name.from_text('wrongcc.cookies.example.') and question.rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('wrongcc.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ elif question.name == dns.name.from_text("wrongcc.cookies.example.") and question.rdtype == dns.rdatatype.A:
+ answer = dns.rrset.from_text("wrongcc.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
clientcookie = self.getCookie(request)
if clientcookie is not None:
mod = bytearray(clientcookie)
mod[0] = 1
- response.use_edns(options = [self.createCookie(bytes(mod))])
+ response.use_edns(options=[self.createCookie(bytes(mod))])
response.answer.append(answer)
# Case: do send BADCOOKIE cookie back if UDP
- elif question.name == dns.name.from_text('badcookie.cookies.example.') and question.rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('badcookie.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ elif question.name == dns.name.from_text("badcookie.cookies.example.") and question.rdtype == dns.rdatatype.A:
+ answer = dns.rrset.from_text("badcookie.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
clientcookie = self.getCookie(request)
if clientcookie is not None:
- response.use_edns(options = [self.createCookie(clientcookie)])
+ response.use_edns(options=[self.createCookie(clientcookie)])
if not tcp:
- response.set_rcode(23) # BADCOOKIE
+ response.set_rcode(23) # BADCOOKIE
response.answer.append(answer)
# Case send malformed cookie for server .25
- elif question.name == dns.name.from_text('malformed.cookies.example.') and question.rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('malformed.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ elif question.name == dns.name.from_text("malformed.cookies.example.") and question.rdtype == dns.rdatatype.A:
+ answer = dns.rrset.from_text("malformed.cookies.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
clientcookie = self.getCookie(request)
print(self.transport.getHost().host)
- if self.transport.getHost().host == os.environ['PREFIX'] + '.26':
+ if self.transport.getHost().host == os.environ["PREFIX"] + ".26":
if clientcookie is not None:
- response.use_edns(options = [self.createCookie(clientcookie)])
+ response.use_edns(options=[self.createCookie(clientcookie)])
else:
- full = dns.edns.GenericOption(dns.edns.COOKIE, '')
- response.use_edns(options = [full])
+ full = dns.edns.GenericOption(dns.edns.COOKIE, "")
+ response.use_edns(options=[full])
response.answer.append(answer)
return response.to_wire()
response = self.question(datagram)
self.transport.write(response, address)
+
class TCPResponder(Protocol):
def dataReceived(self, data):
handler = UDPResponder()
response = handler.question(data[2:], True)
length = len(response)
- header = length.to_bytes(2, 'big')
+ header = length.to_bytes(2, "big")
self.transport.write(header + response)
+
class TCPFactory(Factory):
def buildProtocol(self, addr):
return TCPResponder()
from recursortests import RecursorTest
-class DNS64Test(RecursorTest):
- _confdir = 'DNS64'
+class DNS64Test(RecursorTest):
+ _confdir = "DNS64"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
serve-rfc6303=no
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.dns64.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.dns64
+ authzonepath = os.path.join(confdir, "example.dns64.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.dns64
@ 3600 IN SOA {soa}
www 3600 IN A 192.0.2.42
www 3600 IN TXT "does exist"
cname2 3600 IN CNAME www.example.dns64.
cname3 3600 IN CNAME txt.example.dns64.
formerr 3600 IN A 192.0.2.43
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- authzonepath = os.path.join(confdir, 'in-addr.arpa.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN in-addr.arpa
+ authzonepath = os.path.join(confdir, "in-addr.arpa.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN in-addr.arpa
@ 3600 IN SOA {soa}
42.2.0.192 IN PTR www.example.dns64.
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- authzonepath = os.path.join(confdir, 'ip6.arpa.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN ip6.arpa
+ authzonepath = os.path.join(confdir, "ip6.arpa.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN ip6.arpa
@ 3600 IN SOA {soa}
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2 IN PTR aaaa.example.dns64.
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(DNS64Test, cls).generateRecursorConfig(confdir)
# this type (A) exists for this name
def testExistingA(self):
- qname = 'www.example.dns64.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+ qname = "www.example.dns64."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.42")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# there is no A record, we should get a NODATA
def testNonExistingA(self):
- qname = 'aaaa.example.dns64.'
+ qname = "aaaa.example.dns64."
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# this type (AAAA) does not exist for this name but there is an A record, we should get a DNS64-wrapped AAAA
def testNonExistingAAAA(self):
- qname = 'www.example.dns64.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'AAAA', '64:ff9b::c000:22a')
+ qname = "www.example.dns64."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "AAAA", "64:ff9b::c000:22a")
- query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ query = dns.message.make_query(qname, "AAAA", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# there is a CNAME from that name to a second one, then to a name for which this type (AAAA)
# does not exist, but an A record does, so we should get a DNS64-wrapped AAAA
def testCNAMEToA(self):
- qname = 'cname.example.dns64.'
+ qname = "cname.example.dns64."
expectedResults = [
- dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'CNAME', 'cname2.example.dns64.'),
- dns.rrset.from_text('cname2.example.dns64.', 0, dns.rdataclass.IN, 'CNAME', 'www.example.dns64.'),
- dns.rrset.from_text('www.example.dns64.', 0, dns.rdataclass.IN, 'AAAA', '64:ff9b::c000:22a')
+ dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "CNAME", "cname2.example.dns64."),
+ dns.rrset.from_text("cname2.example.dns64.", 0, dns.rdataclass.IN, "CNAME", "www.example.dns64."),
+ dns.rrset.from_text("www.example.dns64.", 0, dns.rdataclass.IN, "AAAA", "64:ff9b::c000:22a"),
]
- query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ query = dns.message.make_query(qname, "AAAA", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# there is a CNAME from the name to a name that is NODATA for both A and AAAA
# so we should get a NODATA with a single SOA record (#14362)
def testCNAMEToNoData(self):
- qname = 'cname3.example.dns64.'
+ qname = "cname3.example.dns64."
- expectedAnswer = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'CNAME', 'txt.example.dns64.')
- query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ expectedAnswer = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "CNAME", "txt.example.dns64.")
+ query = dns.message.make_query(qname, "AAAA", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
- res = sender(query, 2.0, True, {"one_rr_per_rrset": True}) # we want to detect dups
+ res = sender(query, 2.0, True, {"one_rr_per_rrset": True}) # we want to detect dups
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(len(res.answer), 1)
self.assertEqual(len(res.authority), 1)
# this type (AAAA) does not exist for this name and there is no A record either, we should get a NXDomain
def testNXD(self):
- qname = 'nxd.example.dns64.'
+ qname = "nxd.example.dns64."
- query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ query = dns.message.make_query(qname, "AAAA", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# this type (AAAA) does not exist for this name and there is no A record either, we should get a NODATA as TXT does exist
def testNoData(self):
- qname = 'txt.example.dns64.'
+ qname = "txt.example.dns64."
- query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ query = dns.message.make_query(qname, "AAAA", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
- res = sender(query, 2.0, True, {"one_rr_per_rrset": True}) # we want to detect dups
+ res = sender(query, 2.0, True, {"one_rr_per_rrset": True}) # we want to detect dups
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(len(res.answer), 0)
self.assertEqual(len(res.authority), 1)
# there is an AAAA record, we should get it
def testExistingAAAA(self):
- qname = 'aaaa.example.dns64.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'AAAA', '2001:db8::1')
+ qname = "aaaa.example.dns64."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "AAAA", "2001:db8::1")
- query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ query = dns.message.make_query(qname, "AAAA", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# If the AAAA is handled by Lua code, we should not get a dns64 result
def testFormerr(self):
- qname = 'formerr.example.dns64'
+ qname = "formerr.example.dns64"
- query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ query = dns.message.make_query(qname, "AAAA", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# If the AAAA times out, we still should get a dns64 result
def testTimeout(self):
- qname = '8.delay1.example.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'AAAA', '64:ff9b::c000:264')
+ qname = "8.delay1.example."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "AAAA", "64:ff9b::c000:264")
- query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ query = dns.message.make_query(qname, "AAAA", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# there is a TXT record, we should get it
def testExistingTXT(self):
- qname = 'www.example.dns64.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'TXT', '"does exist"')
+ qname = "www.example.dns64."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "TXT", '"does exist"')
- query = dns.message.make_query(qname, 'TXT', want_dnssec=True)
+ query = dns.message.make_query(qname, "TXT", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# the PTR records for the DNS64 prefix should be generated
def testNonExistingPTR(self):
- qname = 'a.2.2.0.0.0.0.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.b.9.f.f.4.6.0.0.ip6.arpa.'
- expectedCNAME = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'CNAME', '42.2.0.192.in-addr.arpa.')
- expected = dns.rrset.from_text('42.2.0.192.in-addr.arpa.', 0, dns.rdataclass.IN, 'PTR', 'www.example.dns64.')
+ qname = "a.2.2.0.0.0.0.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.b.9.f.f.4.6.0.0.ip6.arpa."
+ expectedCNAME = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "CNAME", "42.2.0.192.in-addr.arpa.")
+ expected = dns.rrset.from_text("42.2.0.192.in-addr.arpa.", 0, dns.rdataclass.IN, "PTR", "www.example.dns64.")
- query = dns.message.make_query(qname, 'PTR', want_dnssec=True)
+ query = dns.message.make_query(qname, "PTR", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# but not for other prefixes
def testExistingPTR(self):
- qname = '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'PTR', 'aaaa.example.dns64.')
+ qname = "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "PTR", "aaaa.example.dns64.")
- query = dns.message.make_query(qname, 'PTR', want_dnssec=True)
+ query = dns.message.make_query(qname, "PTR", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
from recursortests import RecursorTest
+
class SimpleDoTTest(RecursorTest):
"""
This tests DoT to auth server in a very basic way and is dependent on powerdns.com nameservers having DoT enabled.
"""
- _confdir = 'SimpleDoT'
+ _confdir = "SimpleDoT"
_config_template = """
dnssec=validate
dot-to-auth-names=powerdns.com
@pytest.mark.external
def testTXT(self):
- query = dns.message.make_query('.', 'DNSKEY', want_dnssec=True)
+ query = dns.message.make_query(".", "DNSKEY", want_dnssec=True)
query.flags |= dns.flags.AD
# As this test uses external servers, be more generous wrt timeouts than the default 2.0s
res = self.sendUDPQuery(query, timeout=5.0)
self.assertMessageIsAuthenticated(res)
- self.assertRcodeEqual(res, 0);
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'get tcp-outqueries']
+ self.assertRcodeEqual(res, 0)
+ rec_controlCmd = [
+ os.environ["RECCONTROL"],
+ "--config-dir=%s" % "configs/" + self._confdir,
+ "get tcp-outqueries",
+ ]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
tcpcount = ret
print(e.output)
raise
- expected = dns.rrset.from_text('dot-test-target.powerdns.org.', 0, dns.rdataclass.IN, 'TXT', 'https://github.com/PowerDNS/pdns/pull/12825')
- query = dns.message.make_query('dot-test-target.powerdns.org', 'TXT', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "dot-test-target.powerdns.org.", 0, dns.rdataclass.IN, "TXT", "https://github.com/PowerDNS/pdns/pull/12825"
+ )
+ query = dns.message.make_query("dot-test-target.powerdns.org", "TXT", want_dnssec=True)
query.flags |= dns.flags.AD
# As this test uses external servers, be a more generous wrt timeouts than the default 2.0s
self.assertRRsetInAnswer(res, expected)
self.assertMatchingRRSIGInAnswer(res, expected)
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'get dot-outqueries']
+ rec_controlCmd = [
+ os.environ["RECCONTROL"],
+ "--config-dir=%s" % "configs/" + self._confdir,
+ "get dot-outqueries",
+ ]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
- self.assertNotEqual(ret, b'UNKNOWN\n')
- self.assertNotEqual(ret, b'0\n')
+ self.assertNotEqual(ret, b"UNKNOWN\n")
+ self.assertNotEqual(ret, b"0\n")
except subprocess.CalledProcessError as e:
print(e.output)
raise
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'get tcp-outqueries']
+ rec_controlCmd = [
+ os.environ["RECCONTROL"],
+ "--config-dir=%s" % "configs/" + self._confdir,
+ "get tcp-outqueries",
+ ]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
self.assertEqual(ret, tcpcount)
print(e.output)
raise
+
class DoTTest(RecursorTest):
"""
This tests DoT to auth server with validation and is dependent on powerdns.com nameservers having DoT enabled.
"""
- _confdir = 'DoT'
+ _confdir = "DoT"
_config_template = """
dnssec:
validation: validate
@pytest.mark.external
def testTXT(self):
- query = dns.message.make_query('.', 'DNSKEY', want_dnssec=True)
+ query = dns.message.make_query(".", "DNSKEY", want_dnssec=True)
query.flags |= dns.flags.AD
# As this test uses external servers, be more generous wrt timeouts than the default 2.0s
res = self.sendUDPQuery(query, timeout=5.0)
self.assertMessageIsAuthenticated(res)
- self.assertRcodeEqual(res, 0);
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'get tcp-outqueries']
+ self.assertRcodeEqual(res, 0)
+ rec_controlCmd = [
+ os.environ["RECCONTROL"],
+ "--config-dir=%s" % "configs/" + self._confdir,
+ "get tcp-outqueries",
+ ]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
tcpcount = ret
print(e.output)
raise
- expected = dns.rrset.from_text('dot-test-target.powerdns.org.', 0, dns.rdataclass.IN, 'TXT', 'https://github.com/PowerDNS/pdns/pull/12825')
- query = dns.message.make_query('dot-test-target.powerdns.org', 'TXT', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "dot-test-target.powerdns.org.", 0, dns.rdataclass.IN, "TXT", "https://github.com/PowerDNS/pdns/pull/12825"
+ )
+ query = dns.message.make_query("dot-test-target.powerdns.org", "TXT", want_dnssec=True)
query.flags |= dns.flags.AD
# As this test uses external servers, be a more generous wrt timeouts than the default 2.0s
self.assertRRsetInAnswer(res, expected)
self.assertMatchingRRSIGInAnswer(res, expected)
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'get dot-outqueries']
+ rec_controlCmd = [
+ os.environ["RECCONTROL"],
+ "--config-dir=%s" % "configs/" + self._confdir,
+ "get dot-outqueries",
+ ]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
- self.assertNotEqual(ret, b'UNKNOWN\n')
- self.assertNotEqual(ret, b'0\n')
+ self.assertNotEqual(ret, b"UNKNOWN\n")
+ self.assertNotEqual(ret, b"0\n")
except subprocess.CalledProcessError as e:
print(e.output)
raise
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'get tcp-outqueries']
+ rec_controlCmd = [
+ os.environ["RECCONTROL"],
+ "--config-dir=%s" % "configs/" + self._confdir,
+ "get tcp-outqueries",
+ ]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
self.assertEqual(ret, tcpcount)
print(e.output)
raise
+
class DoTWithGNUTLSTest(RecursorTest):
"""
This tests DoT to auth server with validation and is dependent on powerdns.com nameservers having DoT enabled.
"""
- _confdir = 'DoTWithGNUTLS'
+ _confdir = "DoTWithGNUTLS"
_config_template = """
dnssec:
validation: validate
@pytest.mark.external
def testTXT(self):
- query = dns.message.make_query('.', 'DNSKEY', want_dnssec=True)
+ query = dns.message.make_query(".", "DNSKEY", want_dnssec=True)
query.flags |= dns.flags.AD
# As this test uses external servers, be more generous wrt timeouts than the default 2.0s
res = self.sendUDPQuery(query, timeout=5.0)
self.assertMessageIsAuthenticated(res)
- self.assertRcodeEqual(res, 0);
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'get tcp-outqueries']
+ self.assertRcodeEqual(res, 0)
+ rec_controlCmd = [
+ os.environ["RECCONTROL"],
+ "--config-dir=%s" % "configs/" + self._confdir,
+ "get tcp-outqueries",
+ ]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
tcpcount = ret
print(e.output)
raise
- expected = dns.rrset.from_text('dot-test-target.powerdns.org.', 0, dns.rdataclass.IN, 'TXT', 'https://github.com/PowerDNS/pdns/pull/12825')
- query = dns.message.make_query('dot-test-target.powerdns.org', 'TXT', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "dot-test-target.powerdns.org.", 0, dns.rdataclass.IN, "TXT", "https://github.com/PowerDNS/pdns/pull/12825"
+ )
+ query = dns.message.make_query("dot-test-target.powerdns.org", "TXT", want_dnssec=True)
query.flags |= dns.flags.AD
# As this test uses external servers, be a more generous wrt timeouts than the default 2.0s
self.assertRRsetInAnswer(res, expected)
self.assertMatchingRRSIGInAnswer(res, expected)
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'get dot-outqueries']
+ rec_controlCmd = [
+ os.environ["RECCONTROL"],
+ "--config-dir=%s" % "configs/" + self._confdir,
+ "get dot-outqueries",
+ ]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
- self.assertNotEqual(ret, b'UNKNOWN\n')
- self.assertNotEqual(ret, b'0\n')
+ self.assertNotEqual(ret, b"UNKNOWN\n")
+ self.assertNotEqual(ret, b"0\n")
except subprocess.CalledProcessError as e:
print(e.output)
raise
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'get tcp-outqueries']
+ rec_controlCmd = [
+ os.environ["RECCONTROL"],
+ "--config-dir=%s" % "configs/" + self._confdir,
+ "get tcp-outqueries",
+ ]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
self.assertEqual(ret, tcpcount)
print(e.output)
raise
+
class DoTWithLocalResponderTests(RecursorTest):
"""
This tests DoT to responder with validation"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
- _tlsBackendPort = 853 # If binding to this port fails, add an empty !853 file to /etc/authbind/byport with execute permissons for you
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
+ _tlsBackendPort = 853 # If binding to this port fails, add an empty !853 file to /etc/authbind/byport with execute permissons for you
_queueTimeout = 1
_toResponderQueue = Queue()
_fromResponderQueue = Queue()
@staticmethod
def sniCallback(sslSocket, sni, sslContext):
- assert(sni == 'tls.tests.powerdns.com')
+ assert sni == "tls.tests.powerdns.com"
return None
@classmethod
@classmethod
def startResponders(cls):
tlsContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
- tlsContext.load_cert_chain('server.chain', 'server.key')
+ tlsContext.load_cert_chain("server.chain", "server.key")
# requires Python 3.7+
- if hasattr(tlsContext, 'sni_callback'):
+ if hasattr(tlsContext, "sni_callback"):
tlsContext.sni_callback = cls.sniCallback
if cls._clientCert:
tlsContext.load_verify_locations(cafile="ca.pem")
print("Launching TLS responder..")
- cls._TLSResponder = threading.Thread(name='TLS Responder', target=cls.TCPResponder, args=[cls._tlsBackendPort, cls._toResponderQueue, cls._fromResponderQueue, False, False, None, tlsContext, False, '127.0.0.1', False, cls._clientCert])
+ cls._TLSResponder = threading.Thread(
+ name="TLS Responder",
+ target=cls.TCPResponder,
+ args=[
+ cls._tlsBackendPort,
+ cls._toResponderQueue,
+ cls._fromResponderQueue,
+ False,
+ False,
+ None,
+ tlsContext,
+ False,
+ "127.0.0.1",
+ False,
+ cls._clientCert,
+ ],
+ )
cls._TLSResponder.daemon = True
cls._TLSResponder.start()
cls._backgroundThreads[cls._TLSResponder.native_id] = False
count = 0
while count < 200 and len(cls._backgroundThreads) != 0:
- print(f'Waiting for background responder thread to exit {count}...')
+ print(f"Waiting for background responder thread to exit {count}...")
time.sleep(0.01)
count = count + 1
def checkOnlyTLSResponderHit(self, numberOfTLSQueries=1):
- self.assertNotIn('UDP Responder', self._responsesCounter)
- self.assertNotIn('TCP Responder', self._responsesCounter)
- self.assertEqual(self._responsesCounter['TLS Responder'], numberOfTLSQueries)
+ self.assertNotIn("UDP Responder", self._responsesCounter)
+ self.assertNotIn("TCP Responder", self._responsesCounter)
+ self.assertEqual(self._responsesCounter["TLS Responder"], numberOfTLSQueries)
+
class DoTOKOpenSSLTest(DoTWithLocalResponderTests):
"""
This tests DoT to responder with openssl validation using a proper CA store for the locally generated cert
"""
- _confdir = 'DoTOKOpenSSL'
+ _confdir = "DoTOKOpenSSL"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
dnssec:
validation: off
"""
Outgoing TLS: UDP query is sent via TLS
"""
- name = 'udp.outgoing-tls.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.outgoing-tls.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query, True)
- rrset = dns.rrset.from_text(name,
- 15,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 15, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
currentCount = 0
- if 'TLS Responder' in self._responsesCounter:
- currentCount = self._responsesCounter['TLS Responder']
+ if "TLS Responder" in self._responsesCounter:
+ currentCount = self._responsesCounter["TLS Responder"]
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, expectedResponse)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
# there was one TCP query
self.checkOnlyTLSResponderHit(currentCount + 1)
- self.checkMetrics({
- 'dot-outqueries': 1
- })
+ self.checkMetrics({"dot-outqueries": 1})
+
class DoTOKWithClientCertPEMTest(DoTWithLocalResponderTests):
"""
This tests DoT to responder with openssl validation using a proper CA store for the locally generated cert
"""
- _confdir = 'DoTOKWithClientCertPEM'
+ _confdir = "DoTOKWithClientCertPEM"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_clientCert = True
_config_template = """
dnssec:
"""
Outgoing TLS: UDP query is sent via TLS
"""
- name = 'udp.outgoing-tls.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.outgoing-tls.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query, True)
- rrset = dns.rrset.from_text(name,
- 15,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 15, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
currentCount = 0
- if 'TLS Responder' in self._responsesCounter:
- currentCount = self._responsesCounter['TLS Responder']
+ if "TLS Responder" in self._responsesCounter:
+ currentCount = self._responsesCounter["TLS Responder"]
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, expectedResponse)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
# there was one TCP query
self.checkOnlyTLSResponderHit(currentCount + 1)
- self.checkMetrics({
- 'dot-outqueries': 1
- })
+ self.checkMetrics({"dot-outqueries": 1})
+
class DoTOKWithClientCertPKCS12Test(DoTWithLocalResponderTests):
"""
This tests DoT to responder with openssl validation using a proper CA store for the locally generated cert
"""
- _confdir = 'DoTOKWithClientCertPKCS12'
+ _confdir = "DoTOKWithClientCertPKCS12"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_clientCert = True
_config_template = """
dnssec:
"""
Outgoing TLS: UDP query is sent via TLS
"""
- name = 'udp.outgoing-tls.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.outgoing-tls.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query, True)
- rrset = dns.rrset.from_text(name,
- 15,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 15, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
currentCount = 0
- if 'TLS Responder' in self._responsesCounter:
- currentCount = self._responsesCounter['TLS Responder']
+ if "TLS Responder" in self._responsesCounter:
+ currentCount = self._responsesCounter["TLS Responder"]
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, expectedResponse)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
# there was one TCP query
self.checkOnlyTLSResponderHit(currentCount + 1)
- self.checkMetrics({
- 'dot-outqueries': 1
- })
-
+ self.checkMetrics({"dot-outqueries": 1})
+
+
class DoTOKGnuTLSTest(DoTWithLocalResponderTests):
"""
This tests DoT to responder with gnutls validation using a proper CA store for the locally generated cert
"""
- _confdir = 'DoTOKGnuTLS'
+ _confdir = "DoTOKGnuTLS"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
dnssec:
validation: off
"""
Outgoing TLS: UDP query is sent via TLS
"""
- name = 'udp.outgoing-tls.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.outgoing-tls.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query, True)
- rrset = dns.rrset.from_text(name,
- 15,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 15, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
currentCount = 0
- if 'TLS Responder' in self._responsesCounter:
- currentCount = self._responsesCounter['TLS Responder']
+ if "TLS Responder" in self._responsesCounter:
+ currentCount = self._responsesCounter["TLS Responder"]
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, expectedResponse)
receivedQuery.id = query.id
self.assertEqual(query, receivedQuery)
# there was one TCP query
self.checkOnlyTLSResponderHit(currentCount + 1)
- self.checkMetrics({
- 'dot-outqueries': 1
- })
+ self.checkMetrics({"dot-outqueries": 1})
+
class DoTNOKOpenSSLTest(DoTWithLocalResponderTests):
"""
This tests DoT to responder with openssl validation using a missing CA store for the locally generated cert
"""
- _confdir = 'DoTNOKOpenSSL'
+ _confdir = "DoTNOKOpenSSL"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
dnssec:
validation: off
"""
Outgoing TLS: UDP query is sent via TLS
"""
- name = 'udp.outgoing-tls.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.outgoing-tls.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query, True)
- rrset = dns.rrset.from_text(name,
- 15,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 15, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
currentCount = 0
- if 'TLS Responder' in self._responsesCounter:
- currentCount = self._responsesCounter['TLS Responder']
+ if "TLS Responder" in self._responsesCounter:
+ currentCount = self._responsesCounter["TLS Responder"]
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, expectedResponse)
self.assertRcodeEqual(receivedResponse, dns.rcode.SERVFAIL)
# there was no successful DoT query
self.checkOnlyTLSResponderHit(currentCount)
- self.checkMetrics({
- 'dot-outqueries': 1
- })
+ self.checkMetrics({"dot-outqueries": 1})
class DoTNOKGnuTLSTest(DoTWithLocalResponderTests):
This tests DoT to responder with gnutls validation using a missing CA store for the locally generated cert
"""
- _confdir = 'DoTNOKGnuTLS'
+ _confdir = "DoTNOKGnuTLS"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
dnssec:
validation: off
"""
Outgoing TLS: UDP query is sent via TLS
"""
- name = 'udp.outgoing-tls.test.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
+ name = "udp.outgoing-tls.test.powerdns.com."
+ query = dns.message.make_query(name, "A", "IN")
expectedResponse = dns.message.make_response(query, True)
- rrset = dns.rrset.from_text(name,
- 15,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1')
+ rrset = dns.rrset.from_text(name, 15, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1")
expectedResponse.answer.append(rrset)
currentCount = 0
- if 'TLS Responder' in self._responsesCounter:
- currentCount = self._responsesCounter['TLS Responder']
+ if "TLS Responder" in self._responsesCounter:
+ currentCount = self._responsesCounter["TLS Responder"]
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, expectedResponse)
self.assertRcodeEqual(receivedResponse, dns.rcode.SERVFAIL)
# there was no succesful DoT query
self.checkOnlyTLSResponderHit(currentCount)
- self.checkMetrics({
- 'dot-outqueries': 1
- })
-
+ self.checkMetrics({"dot-outqueries": 1})
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
-emptyECSText = 'No ECS received'
-mismatchedECSText = 'Mismatched ECS'
-nameECS = 'ecs-echo.example.'
-nameECSInvalidScope = 'invalid-scope.ecs-echo.example.'
+emptyECSText = "No ECS received"
+mismatchedECSText = "Mismatched ECS"
+nameECS = "ecs-echo.example."
+nameECSInvalidScope = "invalid-scope.ecs-echo.example."
ttlECS = 60
ecsReactorRunning = False
ecsReactorv6Running = False
+
class ECSTest(RecursorTest):
_config_template_default = """
daemon=no
global ecsReactorv6Running
print("Launching responders..")
- address = cls._PREFIX + '.21'
+ address = cls._PREFIX + ".21"
port = 53
if not ecsReactorRunning:
ecsReactorRunning = True
if not ecsReactorv6Running and have_ipv6():
- reactor.listenUDP(53000, UDPECSResponder(), interface='::1')
- reactor.listenTCP(53000, TCPECSFactory(), interface='::1')
+ reactor.listenUDP(53000, UDPECSResponder(), interface="::1")
+ reactor.listenTCP(53000, TCPECSFactory(), interface="::1")
ecsReactorv6Running = True
cls.startReactor()
+
class NoECSTest(ECSTest):
- _confdir = 'NoECS'
+ _confdir = "NoECS"
_config_template = """edns-subnet-allow-list=
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- query = dns.message.make_query(nameECS, 'TXT')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ query = dns.message.make_query(nameECS, "TXT")
self.sendECSQuery(query, expected)
def testRequireNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
+
class NoECSHardenedTest(NoECSTest):
- _confdir = 'NoECSHardened'
+ _confdir = "NoECSHardened"
_config_template = """
edns-subnet-harden=yes
edns-subnet-allow-list=
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
+
class NoECSInAnswerTest(ECSTest):
- _confdir = 'NoECSInAnswer'
+ _confdir = "NoECSInAnswer"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """edns-subnet-allow-list=xecs-echo.example
forward-zones=xecs-echo.example=%s.21
webserver=yes
webserver-password=%s
webserver-allow-from=127.0.0.1
api-key=%s
- """ % (os.environ['PREFIX'], _wsPort, _wsPassword, _apiKey)
+ """ % (os.environ["PREFIX"], _wsPort, _wsPassword, _apiKey)
def test1SendECS(self):
- expected = dns.rrset.from_text('x'+ nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query('x' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text("x" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", "X")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query("x" + nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
- self.checkMetrics({
- 'ecs-missing': 0
- })
+ self.checkMetrics({"ecs-missing": 0})
def test2NoECS(self):
- expected = dns.rrset.from_text('x' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X')
- query = dns.message.make_query('x' + nameECS, 'TXT')
+ expected = dns.rrset.from_text("x" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", "X")
+ query = dns.message.make_query("x" + nameECS, "TXT")
self.sendECSQuery(query, expected)
- self.checkMetrics({
- 'ecs-missing': 0
- })
+ self.checkMetrics({"ecs-missing": 0})
def test3RequireNoECS(self):
- expected = dns.rrset.from_text('x' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X')
+ expected = dns.rrset.from_text("x" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", "X")
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query('x' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query("x" + nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
- self.checkMetrics({
- 'ecs-missing': 0
- })
+ self.checkMetrics({"ecs-missing": 0})
+
class NoECSInAnswerHardenedTest(NoECSInAnswerTest):
- _confdir = 'NoECSInAnswerHardened'
+ _confdir = "NoECSInAnswerHardened"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
edns-subnet-harden=yes
edns-subnet-allow-list=xecs-echo.example
webserver-password=%s
webserver-allow-from=127.0.0.1
api-key=%s
- """ % (os.environ['PREFIX'], _wsPort, _wsPassword, _apiKey)
+ """ % (os.environ["PREFIX"], _wsPort, _wsPassword, _apiKey)
# All test below have ecs-missing count to be 1, as they result in a non ecs scoped answer in the cache
def test1SendECS(self):
- expected = dns.rrset.from_text('x'+ nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query('x' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text("x" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", "X")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query("x" + nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
- self.checkMetrics({
- 'ecs-missing': 1
- })
+ self.checkMetrics({"ecs-missing": 1})
def test2NoECS(self):
- expected = dns.rrset.from_text('x' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X')
- query = dns.message.make_query('x' + nameECS, 'TXT')
+ expected = dns.rrset.from_text("x" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", "X")
+ query = dns.message.make_query("x" + nameECS, "TXT")
self.sendECSQuery(query, expected)
- self.checkMetrics({
- 'ecs-missing': 1
- })
+ self.checkMetrics({"ecs-missing": 1})
def test3RequireNoECS(self):
- expected = dns.rrset.from_text('x' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X')
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query('x' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text("x" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", "X")
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query("x" + nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
- self.checkMetrics({
- 'ecs-missing': 1
- })
+ self.checkMetrics({"ecs-missing": 1})
+
class MismatchedECSInAnswerTest(ECSTest):
- _confdir = 'MismatchedECSInAnswer'
+ _confdir = "MismatchedECSInAnswer"
_config_template = """edns-subnet-allow-list=mecs-echo.example
forward-zones=mecs-echo.example=%s.21
dont-throttle-netmasks=0.0.0.0/0
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def test1SendECS(self):
- expected = dns.rrset.from_text('m'+ nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query('m' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text("m" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query("m" + nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
def test2NoECS(self):
- expected = dns.rrset.from_text('m' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- query = dns.message.make_query('m' + nameECS, 'TXT')
+ expected = dns.rrset.from_text("m" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ query = dns.message.make_query("m" + nameECS, "TXT")
self.sendECSQuery(query, expected)
def test3RequireNoECS(self):
- expected = dns.rrset.from_text('m' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query('m' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text("m" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query("m" + nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
+
class MismatchedECSInAnswerHardenedTest(MismatchedECSInAnswerTest):
- _confdir = 'MismatchedECSInAnswerHardened'
+ _confdir = "MismatchedECSInAnswerHardened"
_config_template = """
edns-subnet-harden=yes
edns-subnet-allow-list=mecs-echo.example
forward-zones=mecs-echo.example=%s.21
dont-throttle-netmasks=0.0.0.0/0
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def test1SendECS(self):
- expected = dns.rrset.from_text('m'+ nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query('m' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text("m" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query("m" + nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
def test2NoECS(self):
- expected = dns.rrset.from_text('m' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- query = dns.message.make_query('m' + nameECS, 'TXT')
+ expected = dns.rrset.from_text("m" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ query = dns.message.make_query("m" + nameECS, "TXT")
self.sendECSQuery(query, expected)
def test3RequireNoECS(self):
- expected = dns.rrset.from_text('m' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query('m' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text("m" + nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query("m" + nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
+
class IncomingNoECSTest(ECSTest):
- _confdir = 'IncomingNoECS'
+ _confdir = "IncomingNoECS"
_config_template = """edns-subnet-allow-list=
use-incoming-edns-subnet=yes
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected, scopeZeroResponse=True)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
- query = dns.message.make_query(nameECS, 'TXT')
+ query = dns.message.make_query(nameECS, "TXT")
self.sendECSQuery(query, expected)
def testRequireNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected, scopeZeroResponse=True)
+
class IncomingNoECSHardenedTest(IncomingNoECSTest):
- _confdir = 'IncomingNoECSHardened'
+ _confdir = "IncomingNoECSHardened"
_config_template = """
edns-subnet-harden=yes
edns-subnet-allow-list=
use-incoming-edns-subnet=yes
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
+
class ECSByNameTest(ECSTest):
- _confdir = 'ECSByName'
+ _confdir = "ECSByName"
_config_template = """edns-subnet-allow-list=ecs-echo.example.
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/24")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
# check that a query in a different ECS range is a hit, because we don't use the incoming ECS
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.2", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.checkECSQueryHit(query, expected)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
- query = dns.message.make_query(nameECS, 'TXT')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/24")
+ query = dns.message.make_query(nameECS, "TXT")
self.sendECSQuery(query, expected)
def testRequireNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/24")
# the request for no ECS is ignored because use-incoming-edns-subnet is not set
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
+
class ECSByNameLargerTest(ECSTest):
- _confdir = 'ECSByNameLarger'
+ _confdir = "ECSByNameLarger"
_config_template = """edns-subnet-allow-list=ecs-echo.example.
ecs-ipv4-bits=32
forward-zones=ecs-echo.example=%s.21
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.1/32')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.1/32")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
# check that a query in a different range is a miss
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.2", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.1/32')
- query = dns.message.make_query(nameECS, 'TXT')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.1/32")
+ query = dns.message.make_query(nameECS, "TXT")
self.sendECSQuery(query, expected)
def testRequireNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.1/32')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.1/32")
# the request for no ECS is ignored because use-incoming-edns-subnet is not set
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
+
class ECSByNameLargerHardenedTest(ECSByNameLargerTest):
- _confdir = 'ECSByNameLargerHardened'
+ _confdir = "ECSByNameLargerHardened"
_config_template = """
edns-subnet-harden=yes
forward-zones=ecs-echo.example=%s.21
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
+
class ECSByNameSmallerTest(ECSTest):
- _confdir = 'ECSByNameSmaller'
+ _confdir = "ECSByNameSmaller"
_config_template = """edns-subnet-allow-list=ecs-echo.example.
ecs-ipv4-bits=16
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/16')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/16")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/16')
- query = dns.message.make_query(nameECS, 'TXT')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/16")
+ query = dns.message.make_query(nameECS, "TXT")
self.sendECSQuery(query, expected)
def testRequireNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/16')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/16")
# the request for no ECS is ignored because use-incoming-edns-subnet is not set
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
+
class ECSByNameSmallerHardenedTest(ECSByNameSmallerTest):
- _confdir = 'ECSByNameSmallerHardened'
+ _confdir = "ECSByNameSmallerHardened"
_config_template = """
edns-subnet-harden=yes
edns-subnet-allow-list=ecs-echo.example.
ecs-ipv4-bits=16
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
+
class IncomingECSByNameTest(ECSTest):
- _confdir = 'IncomingECSByName'
+ _confdir = "IncomingECSByName"
_config_template = """edns-subnet-allow-list=ecs-echo.example.
use-incoming-edns-subnet=yes
ecs-scope-zero-address=2001:db8::42
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.0/24')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "192.0.2.0/24")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected, ttlECS)
# check that a query in the same ECS range is a hit
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.2", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.checkECSQueryHit(query, expected)
# check that a query in a different ECS range is a miss
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.1.2.0/24')
- ecso = clientsubnetoption.ClientSubnetOption('192.1.2.2', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "192.1.2.0/24")
+ ecso = clientsubnetoption.ClientSubnetOption("192.1.2.2", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
- query = dns.message.make_query(nameECS, 'TXT')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/24")
+ query = dns.message.make_query(nameECS, "TXT")
self.sendECSQuery(query, expected, ttlECS)
def testRequireNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', "2001:db8::42/128")
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "2001:db8::42/128")
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected, ttlECS)
+
class IncomingECSByNameHardenedTest(IncomingECSByNameTest):
- _confdir = 'IncomingECSByNameHardened'
+ _confdir = "IncomingECSByNameHardened"
_config_template = """
edns-subnet-harden=yes
ecs-scope-zero-address=2001:db8::42
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
+
class IncomingECSByNameLargerTest(ECSTest):
- _confdir = 'IncomingECSByNameLarger'
+ _confdir = "IncomingECSByNameLarger"
_config_template = """edns-subnet-allow-list=ecs-echo.example.
use-incoming-edns-subnet=yes
ecs-scope-zero-address=192.168.0.1
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.1/32')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "192.0.2.1/32")
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected, ttlECS)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.1/32')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.1/32")
- query = dns.message.make_query(nameECS, 'TXT')
+ query = dns.message.make_query(nameECS, "TXT")
self.sendECSQuery(query, expected, ttlECS)
def testRequireNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.168.0.1/32')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "192.168.0.1/32")
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected, ttlECS)
+
class IncomingECSByNameLargerHardenedTest(IncomingECSByNameLargerTest):
- _confdir = 'IncomingECSByNameLargerHardened'
+ _confdir = "IncomingECSByNameLargerHardened"
_config_template = """
edns-subnet-harden=yes
ecs-scope-zero-address=192.168.0.1
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
+
class IncomingECSByNameSmallerTest(ECSTest):
- _confdir = 'IncomingECSByNameSmaller'
+ _confdir = "IncomingECSByNameSmaller"
_config_template = """edns-subnet-allow-list=ecs-echo.example.
use-incoming-edns-subnet=yes
ecs-scope-zero-address=192.168.0.1
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.0.0/16')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "192.0.0.0/16")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/16')
- query = dns.message.make_query(nameECS, 'TXT')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/16")
+ query = dns.message.make_query(nameECS, "TXT")
self.sendECSQuery(query, expected)
def testRequireNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.168.0.1/32')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "192.168.0.1/32")
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected, ttlECS)
+
class IncomingECSByNameSmallerHardenedTest(IncomingECSByNameSmallerTest):
- _confdir = 'IncomingECSByNameSmallerHardened'
+ _confdir = "IncomingECSByNameSmallerHardened"
_config_template = """
edns-subnet-harden=yes
ecs-scope-zero-address=192.168.0.1
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
+
@unittest.skipIf(not have_ipv6(), "No IPv6")
class IncomingECSByNameV6Test(ECSTest):
- _confdir = 'IncomingECSByNameV6'
+ _confdir = "IncomingECSByNameV6"
_config_template = """edns-subnet-allow-list=ecs-echo.example.
use-incoming-edns-subnet=yes
"""
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '2001:db8::1/128')
- ecso = clientsubnetoption.ClientSubnetOption('2001:db8::1', 128)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "2001:db8::1/128")
+ ecso = clientsubnetoption.ClientSubnetOption("2001:db8::1", 128)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected, ttlECS)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/24")
- query = dns.message.make_query(nameECS, 'TXT')
+ query = dns.message.make_query(nameECS, "TXT")
self.sendUDPQuery(query)
self.sendECSQuery(query, expected, ttlECS)
def testRequireNoECS(self):
# we should get ::1/128 because ecs-scope-zero-addr is unset and query-local-address is set to ::1
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', "::1/128")
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "::1/128")
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected, ttlECS)
+
class ECSNameMismatchTest(ECSTest):
- _confdir = 'ECSNameMismatch'
+ _confdir = "ECSNameMismatch"
_config_template = """edns-subnet-allow-list=not-the-right-name.example.
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- query = dns.message.make_query(nameECS, 'TXT')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ query = dns.message.make_query(nameECS, "TXT")
self.sendECSQuery(query, expected)
def testRequireNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
+
@unittest.skipIf(not have_ipv6(), "No IPv6")
class IncomingECSByNameV6HardenedTest(IncomingECSByNameV6Test):
- _confdir = 'IncomingECSByNameV6Hardened'
+ _confdir = "IncomingECSByNameV6Hardened"
_config_template = """
edns-subnet-harden=yes
forward-zones=ecs-echo.example=[::1]:53000
"""
+
class ECSByIPTest(ECSTest):
- _confdir = 'ECSByIP'
+ _confdir = "ECSByIP"
_config_template = """edns-subnet-allow-list=%s.21
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'], os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"], os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/24")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
- query = dns.message.make_query(nameECS, 'TXT')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/24")
+ query = dns.message.make_query(nameECS, "TXT")
self.sendECSQuery(query, expected)
def testRequireNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/24")
# the request for no ECS is ignored because use-incoming-edns-subnet is not set
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
+
class ECSByIPHardenedTest(ECSByIPTest):
- _confdir = 'ECSByIPHardened'
+ _confdir = "ECSByIPHardened"
_config_template = """
edns-subnet-harden=yes
edns-subnet-allow-list=%s.21
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'], os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"], os.environ["PREFIX"])
+
class IncomingECSByIPTest(ECSTest):
- _confdir = 'IncomingECSByIP'
+ _confdir = "IncomingECSByIP"
_config_template = """edns-subnet-allow-list=%s.21
use-incoming-edns-subnet=yes
ecs-scope-zero-address=::1
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
- """ % (os.environ['PREFIX'], os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"], os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.0/24')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "192.0.2.0/24")
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
- query = dns.message.make_query(nameECS, 'TXT')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/24")
+ query = dns.message.make_query(nameECS, "TXT")
self.sendECSQuery(query, expected)
def testRequireNoECS(self):
# we will get ::1 because ecs-scope-zero-addr is set to ::1
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '::1/128')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "::1/128")
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected, ttlECS)
def testSendECSInvalidScope(self):
# test that the recursor does not cache with a more specific scope than the source it sent
- expected = dns.rrset.from_text(nameECSInvalidScope, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.0/24/25')
+ expected = dns.rrset.from_text(nameECSInvalidScope, ttlECS, dns.rdataclass.IN, "TXT", "192.0.2.0/24/25")
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24)
- query = dns.message.make_query(nameECSInvalidScope, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 24)
+ query = dns.message.make_query(nameECSInvalidScope, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
+
class IncomingECSByIPHardenedTest(IncomingECSByIPTest):
- _confdir = 'IncomingECSByIPHardened'
+ _confdir = "IncomingECSByIPHardened"
_config_template = """
edns-subnet-harden=yes
ecs-scope-zero-address=::1
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
- """ % (os.environ['PREFIX'], os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"], os.environ["PREFIX"])
+
class ECSIPMismatchTest(ECSTest):
- _confdir = 'ECSIPMismatch'
+ _confdir = "ECSIPMismatch"
_config_template = """edns-subnet-allow-list=192.0.2.1
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
def testSendECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
def testNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
- query = dns.message.make_query(nameECS, 'TXT')
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
+ query = dns.message.make_query(nameECS, "TXT")
self.sendUDPQuery(query)
self.sendECSQuery(query, expected)
def testRequireNoECS(self):
- expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
+ expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", emptyECSText)
- ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("0.0.0.0", 0)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected)
+
class ECSIPMismatchHardenedTest(ECSIPMismatchTest):
- _confdir = 'ECSIPMismatchHardened'
+ _confdir = "ECSIPMismatchHardened"
_config_template = """
edns-subnet-harden=yes
edns-subnet-allow-list=192.0.2.1
forward-zones=ecs-echo.example=%s.21
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
+
class ECSWithProxyProtocolRecursorTest(ECSTest):
- _confdir = 'ECSWithProxyProtocolRecursor'
+ _confdir = "ECSWithProxyProtocolRecursor"
_config_template = """
ecs-add-for=2001:db8::1/128
edns-subnet-allow-list=ecs-echo.example.
forward-zones=ecs-echo.example=%s.21
proxy-protocol-from=127.0.0.1/32
allow-from=2001:db8::1/128
-""" % (os.environ['PREFIX'])
+""" % (os.environ["PREFIX"])
def testProxyProtocolPlusECS(self):
qname = nameECS
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'TXT', '2001:db8::/56')
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "TXT", "2001:db8::/56")
- query = dns.message.make_query(qname, 'TXT', use_edns=True)
+ query = dns.message.make_query(qname, "TXT", use_edns=True)
for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
sender = getattr(self, method)
- res = sender(query, True, '2001:db8::1', '2001:db8::2', 0, 65535)
+ res = sender(query, True, "2001:db8::1", "2001:db8::2", 0, 65535)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
+
class ECSWithProxyProtocolRecursorHardenedTest(ECSWithProxyProtocolRecursorTest):
- _confdir = 'ECSWithProxyProtocolRecursorHardened'
+ _confdir = "ECSWithProxyProtocolRecursorHardened"
_config_template = """
edns-subnet-harden=yes
ecs-add-for=2001:db8::1/128
forward-zones=ecs-echo.example=%s.21
proxy-protocol-from=127.0.0.1/32
allow-from=2001:db8::1/128
-""" % (os.environ['PREFIX'])
+""" % (os.environ["PREFIX"])
-class TooLargeToAddZeroScopeTest(RecursorTest):
- _confdir = 'TooLargeToAddZeroScope'
+class TooLargeToAddZeroScopeTest(RecursorTest):
+ _confdir = "TooLargeToAddZeroScope"
_config_template = """
use-incoming-edns-subnet=yes
dnssec=validate
end
return false
end
- """ % ('A'*447)
+ """ % ("A" * 447)
_roothints = None
-
@classmethod
def generateRecursorConfig(cls, confdir):
super(TooLargeToAddZeroScopeTest, cls).generateRecursorConfig(confdir)
def testTooLarge(self):
- qname = 'toolarge.ecs.'
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24)
- query = dns.message.make_query(qname, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ qname = "toolarge.ecs."
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 24)
+ query = dns.message.make_query(qname, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
# should not have an ECS Option since the packet is too large already
res = self.sendUDPQuery(query, timeout=5.0)
self.assertEqual(res.options[0].otype, 8)
self.assertEqual(res.options[0].scope, 0)
+
class UDPECSResponder(DatagramProtocol):
@staticmethod
def ipToStr(option):
if option.family == clientsubnetoption.FAMILY_IPV4:
- ip = socket.inet_ntop(socket.AF_INET, struct.pack('!L', option.ip))
+ ip = socket.inet_ntop(socket.AF_INET, struct.pack("!L", option.ip))
elif option.family == clientsubnetoption.FAMILY_IPV6:
- ip = socket.inet_ntop(socket.AF_INET6,
- struct.pack('!QQ',
- option.ip >> 64,
- option.ip & (2 ** 64 - 1)))
+ ip = socket.inet_ntop(socket.AF_INET6, struct.pack("!QQ", option.ip >> 64, option.ip & (2**64 - 1)))
return ip
def question(self, datagram, tcp=False):
response.flags |= dns.flags.AA
ecso = None
- if (request.question[0].name == dns.name.from_text(nameECS) or request.question[0].name == dns.name.from_text(nameECSInvalidScope)) and request.question[0].rdtype == dns.rdatatype.TXT:
-
+ if (
+ request.question[0].name == dns.name.from_text(nameECS)
+ or request.question[0].name == dns.name.from_text(nameECSInvalidScope)
+ ) and request.question[0].rdtype == dns.rdatatype.TXT:
text = emptyECSText
for option in request.options:
- if option.otype == clientsubnetoption.ASSIGNED_OPTION_CODE and isinstance(option, clientsubnetoption.ClientSubnetOption):
- text = self.ipToStr(option) + '/' + str(option.mask)
+ if option.otype == clientsubnetoption.ASSIGNED_OPTION_CODE and isinstance(
+ option, clientsubnetoption.ClientSubnetOption
+ ):
+ text = self.ipToStr(option) + "/" + str(option.mask)
# Send a scope more specific than the received source for nameECSInvalidScope
if request.question[0].name == dns.name.from_text(nameECSInvalidScope):
else:
ecso = clientsubnetoption.ClientSubnetOption(self.ipToStr(option), option.mask, option.mask)
- answer = dns.rrset.from_text(request.question[0].name, ttlECS, dns.rdataclass.IN, 'TXT', text)
+ answer = dns.rrset.from_text(request.question[0].name, ttlECS, dns.rdataclass.IN, "TXT", text)
response.answer.append(answer)
elif request.question[0].name == dns.name.from_text(nameECS) and request.question[0].rdtype == dns.rdatatype.NS:
- answer = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'NS', 'ns1.ecs-echo.example.')
+ answer = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "NS", "ns1.ecs-echo.example.")
response.answer.append(answer)
- additional = dns.rrset.from_text('ns1.ecs-echo.example.', 15, dns.rdataclass.IN, 'A', os.environ['PREFIX'] + '.21')
+ additional = dns.rrset.from_text(
+ "ns1.ecs-echo.example.", 15, dns.rdataclass.IN, "A", os.environ["PREFIX"] + ".21"
+ )
response.additional.append(additional)
- elif request.question[0].name == dns.name.from_text('x' + nameECS):
- answer = dns.rrset.from_text(request.question[0].name, ttlECS, dns.rdataclass.IN, 'TXT', 'X')
+ elif request.question[0].name == dns.name.from_text("x" + nameECS):
+ answer = dns.rrset.from_text(request.question[0].name, ttlECS, dns.rdataclass.IN, "TXT", "X")
response.answer.append(answer)
- elif request.question[0].name == dns.name.from_text('m' + nameECS):
+ elif request.question[0].name == dns.name.from_text("m" + nameECS):
incomingECS = False
for option in request.options:
- if option.otype == clientsubnetoption.ASSIGNED_OPTION_CODE and isinstance(option, clientsubnetoption.ClientSubnetOption):
+ if option.otype == clientsubnetoption.ASSIGNED_OPTION_CODE and isinstance(
+ option, clientsubnetoption.ClientSubnetOption
+ ):
incomingECS = True
# Send mismatched ECS over UDP
flag = emptyECSText
if not tcp and incomingECS:
ecso = clientsubnetoption.ClientSubnetOption("193.0.2.1", 24, 25)
flag = mismatchedECSText
- answer = dns.rrset.from_text(request.question[0].name, ttlECS, dns.rdataclass.IN, 'TXT', flag)
+ answer = dns.rrset.from_text(request.question[0].name, ttlECS, dns.rdataclass.IN, "TXT", flag)
response.answer.append(answer)
if ecso:
- response.use_edns(options = [ecso])
+ response.use_edns(options=[ecso])
return response.to_wire()
response = self.question(datagram)
self.transport.write(response, address)
+
class TCPECSResponder(Protocol):
def dataReceived(self, data):
handler = UDPECSResponder()
response = handler.question(data[2:], True)
length = len(response)
- header = length.to_bytes(2, 'big')
+ header = length.to_bytes(2, "big")
self.transport.write(header + response)
+
class TCPECSFactory(Factory):
def buildProtocol(self, addr):
return TCPECSResponder()
ednsBufferReactorRunning = False
+
class EDNSTest(RecursorTest):
"""
These tests are designed to check if we respond correctly to EDNS queries
from clients. Note that buffer-size tests go into test_EDNSBufferSize
"""
- _confdir = 'EDNS'
+
+ _confdir = "EDNS"
def testEDNSUnknownOpt(self):
"""
Ensure the recursor does not reply with an unknown option when one is
sent in the query
"""
- unknownOpt = dns.edns.GenericOption(65005, b'1234567890')
- query = dns.message.make_query('version.bind.', 'TXT', 'CH', use_edns=0,
- payload=4096, options=[unknownOpt])
+ unknownOpt = dns.edns.GenericOption(65005, b"1234567890")
+ query = dns.message.make_query("version.bind.", "TXT", "CH", use_edns=0, payload=4096, options=[unknownOpt])
response = self.sendUDPQuery(query)
self.assertRcodeEqual(response, dns.rcode.NOERROR)
self.assertEqual(response.options, ())
"""
if sys.version_info >= (3, 11) and sys.version_info <= (3, 11, 3):
raise unitest.SkipTest("Test skipped, see https://github.com/PowerDNS/pdns/pull/12912")
- query = dns.message.make_query('version.bind.', 'TXT', 'CH', use_edns=5,
- payload=4096)
+ query = dns.message.make_query("version.bind.", "TXT", "CH", use_edns=5, payload=4096)
response = self.sendUDPQuery(query)
self.assertRcodeEqual(response, dns.rcode.BADVERS)
self.assertEqual(response.answer, [])
ednsBufferReactorRunning = False
+
class EDNSBufferTest(RecursorTest):
"""
The tests derived from this one test several truncation related issues.
The qname is $testnum.edns-tests.example.
"""
- _confdir = 'EDNSBuffer'
+
+ _confdir = "EDNSBuffer"
_udpTruncationThreshold = 1680
_ednsOutgoingBufsize = 1680
- _qnameSuffix = '.edns-tests.example.'
+ _qnameSuffix = ".edns-tests.example."
_config_template = """
qname-minimization=no
forward-zones=edns-tests.example=%s.22
udp-truncation-threshold=%d
edns-outgoing-bufsize=%d
- """ % (os.environ['PREFIX'], _udpTruncationThreshold, _ednsOutgoingBufsize)
+ """ % (os.environ["PREFIX"], _udpTruncationThreshold, _ednsOutgoingBufsize)
@classmethod
def startResponders(cls):
global ednsBufferReactorRunning
print("Launching responders..")
- address = cls._PREFIX + '.22'
+ address = cls._PREFIX + ".22"
port = 53
if not ednsBufferReactorRunning:
def getMessage(self, testnum, payload=0):
do_edns = payload > 0
- return dns.message.make_query(testnum + self._qnameSuffix, 'TXT', 'IN',
- use_edns=do_edns, payload=payload)
+ return dns.message.make_query(testnum + self._qnameSuffix, "TXT", "IN", use_edns=do_edns, payload=payload)
def checkResponseContent(self, rawResponse, value, size, txt_final):
"""
self.assertEqual(len(rawResponse), size)
self.assertRcodeEqual(response, dns.rcode.NOERROR)
- self.assertMessageHasFlags(response, ['QR', 'RD', 'RA'])
+ self.assertMessageHasFlags(response, ["QR", "RD", "RA"])
for record in response.answer:
self.assertEqual(record.rdtype, dns.rdatatype.TXT)
for part in record:
for string in part.strings:
- self.assertTrue(len(string) == 255 or
- len(string) == txt_final)
+ self.assertTrue(len(string) == 255 or len(string) == txt_final)
def checkTruncatedResponse(self, message):
- self.assertMessageHasFlags(message, ['QR', 'RD', 'RA', 'TC'])
+ self.assertMessageHasFlags(message, ["QR", "RD", "RA", "TC"])
def checkEDNS(self, message, bufsize=0):
"""
"""
Runs test cases 1, 2, 5, 6, 7, 8
"""
- _confdir = 'EDNSBuffer16801680'
+
+ _confdir = "EDNSBuffer16801680"
def testEdnsBufferTestCase01(self):
- query = self.getMessage('01', 4096)
+ query = self.getMessage("01", 4096)
for _ in range(10):
raw = self.sendUDPQuery(query, decode=False)
- self.checkResponseContent(raw, 'A',
- self._udpTruncationThreshold, 9)
+ self.checkResponseContent(raw, "A", self._udpTruncationThreshold, 9)
message = dns.message.from_wire(raw)
self.checkEDNS(message, 512)
def testEdnsBufferTestCase02(self):
- query = self.getMessage('02', 1679)
+ query = self.getMessage("02", 1679)
for _ in range(10):
message = self.sendUDPQuery(query)
self.checkTruncatedResponse(message)
self.checkEDNS(message, 512)
def testEdnsBufferTestCase05(self):
- query = self.getMessage('05', 1680)
+ query = self.getMessage("05", 1680)
for _ in range(10):
raw = self.sendUDPQuery(query, decode=False)
- self.checkResponseContent(raw, 'E',
- self._udpTruncationThreshold, 9)
+ self.checkResponseContent(raw, "E", self._udpTruncationThreshold, 9)
message = dns.message.from_wire(raw)
self.checkEDNS(message, 512)
def testEdnsBufferTestCase06(self):
- query = self.getMessage('06', 0)
+ query = self.getMessage("06", 0)
for _ in range(10):
raw = self.sendUDPQuery(query, decode=False)
- self.checkResponseContent(raw, 'F', 512, 192)
+ self.checkResponseContent(raw, "F", 512, 192)
message = dns.message.from_wire(raw)
self.checkEDNS(message, 0)
def testEdnsBufferTestCase07(self):
- query = self.getMessage('07', 0)
+ query = self.getMessage("07", 0)
for _ in range(10):
message = self.sendUDPQuery(query)
self.checkTruncatedResponse(message)
self.checkEDNS(message, 0)
def testEdnsBufferTestCase08(self):
- query = self.getMessage('08', 511)
+ query = self.getMessage("08", 511)
for _ in range(10):
raw = self.sendUDPQuery(query, decode=False)
- self.checkResponseContent(raw, 'H', 512, 181)
+ self.checkResponseContent(raw, "H", 512, 181)
message = dns.message.from_wire(raw)
self.checkEDNS(message, 512)
+
class EDNSBuffer16801681Test(EDNSBufferTest):
"""
Runs test case 3
"""
- _confdir = 'EDNSBuffer16801681'
+
+ _confdir = "EDNSBuffer16801681"
_udpTruncationThreshold = 1680
_ednsOutgoingBufsize = 1681
- _qnameSuffix = '.edns-tests.example.'
+ _qnameSuffix = ".edns-tests.example."
_config_template = """
qname-minimization=no
forward-zones=edns-tests.example=%s.22
udp-truncation-threshold=%d
edns-outgoing-bufsize=%d
- """ % (os.environ['PREFIX'], _udpTruncationThreshold, _ednsOutgoingBufsize)
+ """ % (os.environ["PREFIX"], _udpTruncationThreshold, _ednsOutgoingBufsize)
def testEdnsBufferTestCase03(self):
- query = self.getMessage('03', 4096)
+ query = self.getMessage("03", 4096)
for _ in range(10):
message = self.sendUDPQuery(query)
self.checkTruncatedResponse(message)
"""
Runs test case 4
"""
- _confdir = 'EDNSBuffer16801679'
+
+ _confdir = "EDNSBuffer16801679"
_udpTruncationThreshold = 1680
_ednsOutgoingBufsize = 1679
- _qnameSuffix = '.edns-tests.example.'
+ _qnameSuffix = ".edns-tests.example."
_config_template = """
qname-minimization=no
forward-zones=edns-tests.example=%s.22
udp-truncation-threshold=%d
edns-outgoing-bufsize=%d
- """ % (os.environ['PREFIX'], _udpTruncationThreshold, _ednsOutgoingBufsize)
+ """ % (os.environ["PREFIX"], _udpTruncationThreshold, _ednsOutgoingBufsize)
def testEdnsBufferTestCase04(self):
- query = self.getMessage('04', 4096)
+ query = self.getMessage("04", 4096)
for _ in range(10):
raw = self.sendUDPQuery(query, decode=False)
- self.checkResponseContent(raw, 'D',
- self._ednsOutgoingBufsize, 8)
+ self.checkResponseContent(raw, "D", self._ednsOutgoingBufsize, 8)
message = dns.message.from_wire(raw)
self.checkEDNS(message, 512)
# The outgoing packet should be EDNS buffersize bytes
packet_size = request.payload
- testnum = int(str(request.question[0].name).split('.')[0])
+ testnum = int(str(request.question[0].name).split(".")[0])
# Unless we have special tests
if testnum == 6:
# And the TXT size indicator (first byte in the TXT record)
packet_size -= 1
txt_size = min(packet_size, 255)
- answer = dns.rrset.from_text(request.question[0].name,
- 0, dns.rdataclass.IN, 'TXT',
- value*txt_size)
+ answer = dns.rrset.from_text(request.question[0].name, 0, dns.rdataclass.IN, "TXT", value * txt_size)
response.answer.append(answer)
packet_size -= txt_size
- assert(packet_size == 0)
+ assert packet_size == 0
self.transport.write(response.to_wire(max_size=65535), address)
from recursortests import RecursorTest
-class RecursorEDNSPaddingTest(RecursorTest):
- _confdir = 'RecursorEDNSPadding'
+class RecursorEDNSPaddingTest(RecursorTest):
+ _confdir = "RecursorEDNSPadding"
def checkPadding(self, message, numberOfBytes=None):
self.assertEqual(message.edns, 0)
return message
def testQueryWithoutEDNS(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
- query = dns.message.make_query(name, 'A', want_dnssec=False)
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
+ query = dns.message.make_query(name, "A", want_dnssec=False)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkNoEDNS(res)
self.assertRRsetInAnswer(res, expected)
-class PaddingDefaultTest(RecursorEDNSPaddingTest):
- _confdir = 'PaddingDefault'
+class PaddingDefaultTest(RecursorEDNSPaddingTest):
+ _confdir = "PaddingDefault"
def testQueryWithPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
po = paddingoption.PaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[po])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[po])
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkNoPadding(res)
self.assertRRsetInAnswer(res, expected)
def testQueryWithoutPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkNoPadding(res)
self.assertRRsetInAnswer(res, expected)
-class PaddingDefaultNotAllowedTest(RecursorEDNSPaddingTest):
- _confdir = 'PaddingDefaultNotAllowed'
+class PaddingDefaultNotAllowedTest(RecursorEDNSPaddingTest):
+ _confdir = "PaddingDefaultNotAllowed"
_config_template = """edns-padding-from=127.0.0.2
packetcache-ttl=60
"""
def testQueryWithPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
po = paddingoption.PaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[po])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[po])
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkNoPadding(res)
self.assertRRsetInAnswer(res, expected)
def testQueryWithoutPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkNoPadding(res)
self.assertRRsetInAnswer(res, expected)
-class PaddingAlwaysTest(RecursorEDNSPaddingTest):
- _confdir = 'PaddingAlways'
+class PaddingAlwaysTest(RecursorEDNSPaddingTest):
+ _confdir = "PaddingAlways"
_config_template = """edns-padding-from=127.0.0.1
edns-padding-mode=always
edns-padding-tag=7830
"""
def testQueryWithPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
po = paddingoption.PaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[po])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[po])
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkPadding(res)
self.assertRRsetInAnswer(res, expected)
def testQueryWithPaddingButDisabledViaLua(self):
- name = 'host1.secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.2')
+ name = "host1.secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.2")
po = paddingoption.PaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[po])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[po])
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkNoPadding(res)
self.assertRRsetInAnswer(res, expected)
def testQueryWithPaddingButDisabledViaGettagFFI(self):
- name = 'host1.sub.secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.11')
+ name = "host1.sub.secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.11")
po = paddingoption.PaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[po])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[po])
query.flags |= dns.flags.CD
query.flags |= dns.flags.RD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
def testQueryWithoutPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkPadding(res)
self.assertRRsetInAnswer(res, expected)
-class PaddingNotAllowedAlwaysTest(RecursorEDNSPaddingTest):
- _confdir = 'PaddingNotAllowedAlways'
+class PaddingNotAllowedAlwaysTest(RecursorEDNSPaddingTest):
+ _confdir = "PaddingNotAllowedAlways"
_config_template = """edns-padding-from=127.0.0.2
edns-padding-mode=always
edns-padding-tag=7830
"""
def testQueryWithPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
po = paddingoption.PaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[po])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[po])
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkNoPadding(res)
self.assertRRsetInAnswer(res, expected)
def testQueryWithoutPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkNoPadding(res)
self.assertRRsetInAnswer(res, expected)
-class PaddingWhenPaddedTest(RecursorEDNSPaddingTest):
- _confdir = 'PaddingWhenPadded'
+class PaddingWhenPaddedTest(RecursorEDNSPaddingTest):
+ _confdir = "PaddingWhenPadded"
_config_template = """edns-padding-from=127.0.0.1
edns-padding-mode=padded-queries-only
edns-padding-tag=7830
"""
def testQueryWithPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
po = paddingoption.PaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[po])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[po])
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkPadding(res)
self.assertRRsetInAnswer(res, expected)
def testQueryWithoutPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkNoPadding(res)
self.assertRRsetInAnswer(res, expected)
-class PaddingWhenPaddedNotAllowedTest(RecursorEDNSPaddingTest):
- _confdir = 'PaddingWhenPaddedNotAllowed'
+class PaddingWhenPaddedNotAllowedTest(RecursorEDNSPaddingTest):
+ _confdir = "PaddingWhenPaddedNotAllowed"
_config_template = """edns-padding-from=127.0.0.2
edns-padding-mode=padded-queries-only
edns-padding-tag=7830
"""
def testQueryWithPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
po = paddingoption.PaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[po])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[po])
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkNoPadding(res)
self.assertRRsetInAnswer(res, expected)
def testQueryWithoutPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkNoPadding(res)
self.assertRRsetInAnswer(res, expected)
-@unittest.skipIf('SKIP_IPV6_TESTS' in os.environ, 'IPv6 tests are disabled')
-class PaddingAllowedAlwaysSameTagTest(RecursorEDNSPaddingTest):
+@unittest.skipIf("SKIP_IPV6_TESTS" in os.environ, "IPv6 tests are disabled")
+class PaddingAllowedAlwaysSameTagTest(RecursorEDNSPaddingTest):
# we use the default tag (0) for padded responses, which will cause
# the same packet cache entry (with padding ) to be returned to a client
# not allowed by the edns-padding-from list
- _confdir = 'PaddingAllowedAlwaysSameTag'
+ _confdir = "PaddingAllowedAlwaysSameTag"
_config_template = """edns-padding-from=127.0.0.1
edns-padding-mode=always
edns-padding-tag=0
@classmethod
def setUpClass(cls):
- if 'SKIP_IPV6_TESTS' in os.environ:
- raise unittest.SkipTest('IPv6 tests are disabled')
+ if "SKIP_IPV6_TESTS" in os.environ:
+ raise unittest.SkipTest("IPv6 tests are disabled")
super(PaddingAllowedAlwaysSameTagTest, cls).setUpClass()
def testQueryWithPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
po = paddingoption.PaddingOption(64)
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[po])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[po])
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkPadding(res)
self.assertRRsetInAnswer(res, expected)
- res = self.sendUDPQueryTo(query, '::1')
+ res = self.sendUDPQueryTo(query, "::1")
self.checkPadding(res)
self.assertRRsetInAnswer(res, expected)
def testQueryWithoutPadding(self):
- name = 'secure.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.17')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "secure.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.17")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkPadding(res)
self.assertRRsetInAnswer(res, expected)
- res = self.sendUDPQueryTo(query, '::1')
+ res = self.sendUDPQueryTo(query, "::1")
self.checkPadding(res)
self.assertRRsetInAnswer(res, expected)
import shutil
from recursortests import RecursorTest
+
class ExpiredTest(RecursorTest):
"""This regression test starts the authoritative servers with a clock that is
set 15 days into the past. Hence, the recursor must reject the signatures
because they are expired.
"""
- _confdir = 'Expired'
+
+ _confdir = "Expired"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """dnssec=validate"""
- _auth_env = {'LD_PRELOAD':os.environ.get('LIBFAKETIME'),
- 'FAKETIME':'-15d'}
+ _auth_env = {"LD_PRELOAD": os.environ.get("LIBFAKETIME"), "FAKETIME": "-15d"}
@classmethod
def setUpClass(cls):
@classmethod
def tearDownClass(cls):
- confdir = os.path.join('configs', 'auths')
+ confdir = os.path.join("configs", "auths")
print("Specialized auth teardown " + confdir)
# tear down specialized auths, and then start standard ones
super().tearDownClass(True)
print("Starting default auths")
- #confdir = 'configs/auths'
+ # confdir = 'configs/auths'
shutil.rmtree(confdir, True)
os.mkdir(confdir)
# Be careful here, we don't want the overridden secureZone(), so call RecursorTest explicitly
RecursorTest.startAllAuth(confdir)
def testA(self):
- query = dns.message.make_query('host1.secure.example', 'A')
+ query = dns.message.make_query("host1.secure.example", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
+
class ExpiredWithEDETest(RecursorTest):
"""This regression test starts the authoritative servers with a clock that is
set 15 days into the past. Hence, the recursor must reject the signatures
because they are expired.
"""
- _confdir = 'ExpiredWithEDE'
+
+ _confdir = "ExpiredWithEDE"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
extended-resolution-errors=yes
"""
- _auth_env = {'LD_PRELOAD':os.environ.get('LIBFAKETIME'),
- 'FAKETIME':'-15d'}
+ _auth_env = {"LD_PRELOAD": os.environ.get("LIBFAKETIME"), "FAKETIME": "-15d"}
+
@classmethod
def setUpClass(cls):
cls.setUpClassSpecialAuths()
@classmethod
def tearDownClass(cls):
- confdir = os.path.join('configs', 'auths')
+ confdir = os.path.join("configs", "auths")
print("Specialized auth teardown " + confdir)
# tear down specialized auths, and then start standard ones
super().tearDownClass(True)
print("Starting default auths")
- #confdir = 'configs/auths'
+ # confdir = 'configs/auths'
shutil.rmtree(confdir, True)
os.mkdir(confdir)
# Be careful here, we don't want the overridden secureZone(), so call RecursorTest explicitly
RecursorTest.startAllAuth(confdir)
def testA(self):
- qname = 'host1.secure.example'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ qname = "host1.secure.example"
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(7, b''))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(7, b""))
from recursortests import RecursorTest
-class ExtendedErrorsTest(RecursorTest):
- _confdir = 'ExtendedErrors'
+class ExtendedErrorsTest(RecursorTest):
+ _confdir = "ExtendedErrors"
_config_template = """
dnssec=validate
extended-resolution-errors=yes
ffi.C.pdns_ffi_param_set_extended_error_extra(obj, #extra, extra)
end
end
- """ % ('A'*427)
+ """ % ("A" * 427)
_roothints = None
@classmethod
def generateRecursorConfig(cls, confdir):
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
*.rpz.extended.zone.rpz. 60 IN CNAME .
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(ExtendedErrorsTest, cls).generateRecursorConfig(confdir)
@pytest.mark.external
def testNotIncepted(self):
- qname = 'signotincepted.bad-dnssec.wb.sidnlabs.nl.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ qname = "signotincepted.bad-dnssec.wb.sidnlabs.nl."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(8, b''))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(8, b""))
@pytest.mark.external
def testExpired(self):
- qname = 'sigexpired.bad-dnssec.wb.sidnlabs.nl.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ qname = "sigexpired.bad-dnssec.wb.sidnlabs.nl."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(7, b''))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(7, b""))
@pytest.mark.external
def testAllExpired(self):
- qname = 'servfail.nl.'
- query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ qname = "servfail.nl."
+ query = dns.message.make_query(qname, "AAAA", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(6, b''))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(6, b""))
@pytest.mark.external
def testBogus(self):
- qname = 'bogussig.ok.bad-dnssec.wb.sidnlabs.nl.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ qname = "bogussig.ok.bad-dnssec.wb.sidnlabs.nl."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(6, b''))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(6, b""))
@pytest.mark.external
def testMissingRRSIG(self):
- qname = 'brokendnssec.net.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ qname = "brokendnssec.net."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b''))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b""))
def testFromLua(self):
- qname = 'fromlua.extended.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ qname = "fromlua.extended."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b'Extra text from Lua!'))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b"Extra text from Lua!"))
def testFromLuaFFI(self):
- qname = 'fromluaffi.extended.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ qname = "fromluaffi.extended."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b'Extra text from Lua FFI!'))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b"Extra text from Lua FFI!"))
def testRPZ(self):
- qname = 'sub.rpz.extended.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ qname = "sub.rpz.extended."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(15, b'Blocked by RPZ!'))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(15, b"Blocked by RPZ!"))
def testTooLarge(self):
- qname = 'toolarge.extended.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True, payload=512)
+ qname = "toolarge.extended."
+ query = dns.message.make_query(qname, "A", want_dnssec=True, payload=512)
# should not have the Extended Option since the packet is too large already
res = self.sendUDPQuery(query, timeout=5.0)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b'Extra text from Lua!'))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b"Extra text from Lua!"))
-class NoExtendedErrorsTest(RecursorTest):
- _confdir = 'NoExtendedErrors'
+class NoExtendedErrorsTest(RecursorTest):
+ _confdir = "NoExtendedErrors"
_config_template = """
dnssec=validate
extended-resolution-errors=no
@pytest.mark.external
def testNotIncepted(self):
- qname = 'signotincepted.bad-dnssec.wb.sidnlabs.nl.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ qname = "signotincepted.bad-dnssec.wb.sidnlabs.nl."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
from recursortests import RecursorTest
-class FWCatzServer(object):
+class FWCatzServer(object):
def __init__(self, port):
self._currentSerial = 0
self._targetSerial = 1
self._serverPort = port
- listener = threading.Thread(name='FWCatz Listener', target=self._listener, args=[])
+ listener = threading.Thread(name="FWCatz Listener", target=self._listener, args=[])
listener.daemon = True
listener.start()
return False
if newSerial != self._currentSerial + 1:
- raise AssertionError("Asking the FWCatz server to serve serial %d, already serving %d" % (newSerial, self._currentSerial))
+ raise AssertionError(
+ "Asking the FWCatz server to serve serial %d, already serving %d" % (newSerial, self._currentSerial)
+ )
self._targetSerial = newSerial
return True
if message.question[0].rdtype == dns.rdatatype.AXFR:
if self._currentSerial != 0:
- print('Received an AXFR query but IXFR expected because the current serial is %d' % (self._currentSerial))
+ print(
+ "Received an AXFR query but IXFR expected because the current serial is %d" % (self._currentSerial)
+ )
return (None, self._currentSerial)
newSerial = self._targetSerial
records = [
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('version.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.TXT, '2'),
- dns.rrset.from_text('a.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'a.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("version.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.TXT, "2"),
+ dns.rrset.from_text("a.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "a."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif message.question[0].rdtype == dns.rdatatype.IXFR:
oldSerial = message.authority[0][0].serial
# special case for the 9th update, which might get skipped
if oldSerial != self._currentSerial and self._currentSerial != 9:
- print('Received an IXFR query with an unexpected serial %d, expected %d' % (oldSerial, self._currentSerial))
+ print(
+ "Received an IXFR query with an unexpected serial %d, expected %d"
+ % (oldSerial, self._currentSerial)
+ )
return (None, self._currentSerial)
newSerial = self._targetSerial
if newSerial == 2:
records = [
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % oldSerial),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % oldSerial,
+ ),
# no deletion
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('b.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'b.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("b.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "b."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 3:
records = [
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('a.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'a.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text("a.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "a."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
# no addition
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 4:
records = [
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('b.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'b.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('c.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'c.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text("b.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "b."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("c.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "c."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 5:
# this one is a bit special, we are answering with a full AXFR
records = [
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('version.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.TXT, '2'),
- dns.rrset.from_text('d.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'd.'),
- dns.rrset.from_text('e.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'e.'),
- dns.rrset.from_text('f.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'f.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("version.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.TXT, "2"),
+ dns.rrset.from_text("d.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "d."),
+ dns.rrset.from_text("e.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "e."),
+ dns.rrset.from_text("f.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "f."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 6:
# back to IXFR
records = [
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('d.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'd.'),
- dns.rrset.from_text('e.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'e.'),
- dns.rrset.from_text('f.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'f.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('e.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'ee.'),
- dns.rrset.from_text('group.e.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.TXT, 'GROUP'),
- dns.rrset.from_text('f.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'ff.'),
- dns.rrset.from_text('g.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'gg.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text("d.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "d."),
+ dns.rrset.from_text("e.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "e."),
+ dns.rrset.from_text("f.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "f."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("e.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "ee."),
+ dns.rrset.from_text(
+ "group.e.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.TXT, "GROUP"
+ ),
+ dns.rrset.from_text("f.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "ff."),
+ dns.rrset.from_text("g.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "gg."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 7:
records = [
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('e.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'ee.'),
- dns.rrset.from_text('group.e.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.TXT, 'GROUP'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('e.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'e.'),
- dns.rrset.from_text('f.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'f.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text("e.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "ee."),
+ dns.rrset.from_text(
+ "group.e.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.TXT, "GROUP"
+ ),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("e.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "e."),
+ dns.rrset.from_text("f.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "f."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 8:
# this one is a bit special too, we are answering with a full AXFR and the new zone is empty
records = [
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('version.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.TXT, '2'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("version.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.TXT, "2"),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 9:
# IXFR inserting a duplicate, we should not crash and skip it
records = [
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('version.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.TXT, '2'),
- dns.rrset.from_text('dup1.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'dup.'),
- dns.rrset.from_text('dup2.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'dup.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("version.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.TXT, "2"),
+ dns.rrset.from_text("dup1.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "dup."),
+ dns.rrset.from_text("dup2.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "dup."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 10:
# full AXFR to make sure we are removing the duplicate, adding a record, to check that the update was correctly applied
records = [
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('version.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.TXT, '2'),
- dns.rrset.from_text('f.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'f.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("version.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.TXT, "2"),
+ dns.rrset.from_text("f.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "f."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 11:
# IXFR with two deltas, the first one adding a 'g' and the second one removing 'f'
records = [
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % (newSerial + 1)),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('g.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'g.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('f.zones.forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.PTR, 'f.'),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % (newSerial + 1)),
- dns.rrset.from_text('forward.catz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1' % (newSerial + 1)),
- ]
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % (newSerial + 1),
+ ),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("g.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "g."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("f.zones.forward.catz.", 60, dns.rdataclass.IN, dns.rdatatype.PTR, "f."),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % (newSerial + 1),
+ ),
+ dns.rrset.from_text(
+ "forward.catz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.forward.catz. hostmaster.forward.catz. %d 3600 3600 3600 1" % (newSerial + 1),
+ ),
+ ]
# this one has two updates in one
newSerial = newSerial + 1
self._targetSerial = self._targetSerial + 1
message = dns.message.from_wire(data)
if len(message.question) != 1:
- print('Invalid FWCatz query, qdcount is %d' % (len(message.question)))
+ print("Invalid FWCatz query, qdcount is %d" % (len(message.question)))
break
if not message.question[0].rdtype in [dns.rdatatype.AXFR, dns.rdatatype.IXFR]:
- print('Invalid FWCatz query, qtype is %d' % (message.question.rdtype))
+ print("Invalid FWCatz query, qtype is %d" % (message.question.rdtype))
break
(serial, answer) = self._getAnswer(message)
if not answer:
- print('Unable to get a response for %s %d' % (message.question[0].name, message.question[0].rdtype))
+ print("Unable to get a response for %s %d" % (message.question[0].name, message.question[0].rdtype))
break
wire = answer.to_wire()
while True:
try:
(conn, _) = sock.accept()
- thread = threading.Thread(name='FWCatz Connection Handler',
- target=self._connectionHandler,
- args=[conn])
+ thread = threading.Thread(name="FWCatz Connection Handler", target=self._connectionHandler, args=[conn])
thread.daemon = True
thread.start()
except socket.error as e:
- print('Error in FWCatz socket: %s' % str(e))
+ print("Error in FWCatz socket: %s" % str(e))
sock.close()
+
fwCatzServerPort = 4252
fwCatzServer = FWCatzServer(fwCatzServerPort)
+
class FWCatzXFRRecursorTest(RecursorTest):
"""
This test makes sure that we correctly update FW cat zones via AXFR then IXFR
"""
global fwCatzServerPort
- _confdir = 'FWCatzXFRRecursor'
+ _confdir = "FWCatzXFRRecursor"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
logging:
loglevel: 7
super(FWCatzXFRRecursorTest, cls).generateRecursorYamlConfig(confdir, False)
def sendNotify(self):
- notify = dns.message.make_query('forward.catz', 'SOA', want_dnssec=False)
- notify.set_opcode(4) # notify
+ notify = dns.message.make_query("forward.catz", "SOA", want_dnssec=False)
+ notify.set_opcode(4) # notify
res = self.sendUDPQuery(notify)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(res.opcode(), 4)
- self.assertEqual(res.question[0].to_text(), 'forward.catz. IN SOA')
+ self.assertEqual(res.question[0].to_text(), "forward.catz. IN SOA")
def checkForwards(self, expected):
attempts = 0
ex = None
while attempts < tries:
try:
- with open('configs/' + self._confdir + '/catzone.forward.catz.') as file:
- reality = yaml.safe_load(file);
+ with open("configs/" + self._confdir + "/catzone.forward.catz.") as file:
+ reality = yaml.safe_load(file)
if expected == reality:
return
except Exception as e:
time.sleep(0.01)
if ex is not None:
raise ex
- raise AssertionError('expected content not found')
+ raise AssertionError("expected content not found")
def waitUntilCorrectSerialIsLoaded(self, serial, timeout=5):
global fwCatzServer
attempts = attempts + 1
time.sleep(1)
- raise AssertionError("Waited %d seconds for the serial to be updated to %d but the serial is still %d" % (timeout, serial, currentSerial))
+ raise AssertionError(
+ "Waited %d seconds for the serial to be updated to %d but the serial is still %d"
+ % (timeout, serial, currentSerial)
+ )
def testFWCatz(self):
# Fresh catz does not need a notify
self.waitForTCPSocket("127.0.0.1", self._wsPort)
# first zone
self.waitUntilCorrectSerialIsLoaded(1)
- self.checkForwards({'forward_zones': [
- {'zone': 'a.', 'forwarders': ['1.2.3.4']}
- ]})
+ self.checkForwards({"forward_zones": [{"zone": "a.", "forwarders": ["1.2.3.4"]}]})
# second zone
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(2)
- self.checkForwards({'forward_zones': [
- {'zone': 'a.', 'forwarders': ['1.2.3.4']},
- {'zone': 'b.', 'forwarders': ['1.2.3.4']}
- ]})
+ self.checkForwards(
+ {"forward_zones": [{"zone": "a.", "forwarders": ["1.2.3.4"]}, {"zone": "b.", "forwarders": ["1.2.3.4"]}]}
+ )
# third zone
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(3)
- self.checkForwards({'forward_zones': [
- {'zone': 'b.', 'forwarders': ['1.2.3.4']}
- ]})
+ self.checkForwards({"forward_zones": [{"zone": "b.", "forwarders": ["1.2.3.4"]}]})
# fourth zone
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(4)
- self.checkForwards({'forward_zones': [
- {'zone': 'c.', 'forwarders': ['1.2.3.4']}
- ]})
+ self.checkForwards({"forward_zones": [{"zone": "c.", "forwarders": ["1.2.3.4"]}]})
# fifth zone, we should get a full AXFR this time
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(5)
- self.checkForwards({'forward_zones': [
- {'zone': 'd.', 'forwarders': ['1.2.3.4']},
- {'zone': 'e.', 'forwarders': ['1.2.3.4']},
- {'zone': 'f.', 'forwarders': ['1.2.3.4']}
- ]})
+ self.checkForwards(
+ {
+ "forward_zones": [
+ {"zone": "d.", "forwarders": ["1.2.3.4"]},
+ {"zone": "e.", "forwarders": ["1.2.3.4"]},
+ {"zone": "f.", "forwarders": ["1.2.3.4"]},
+ ]
+ }
+ )
# sixth zone
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(6)
- self.checkForwards({'forward_zones': [
- {'zone': 'ee.', 'forwarders': ['4.5.6.7'], 'notify_allowed': True, 'recurse': True},
- {'zone': 'ff.', 'forwarders': ['1.2.3.4']},
- {'zone': 'gg.', 'forwarders': ['1.2.3.4']}
- ]})
+ self.checkForwards(
+ {
+ "forward_zones": [
+ {"zone": "ee.", "forwarders": ["4.5.6.7"], "notify_allowed": True, "recurse": True},
+ {"zone": "ff.", "forwarders": ["1.2.3.4"]},
+ {"zone": "gg.", "forwarders": ["1.2.3.4"]},
+ ]
+ }
+ )
# seventh zone
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(7)
- self.checkForwards({'forward_zones': [
- {'zone': 'e.', 'forwarders': ['1.2.3.4']},
- {'zone': 'ff.', 'forwarders': ['1.2.3.4']},
- {'zone': 'f.', 'forwarders': ['1.2.3.4']},
- {'zone': 'gg.', 'forwarders': ['1.2.3.4']}
- ]})
+ self.checkForwards(
+ {
+ "forward_zones": [
+ {"zone": "e.", "forwarders": ["1.2.3.4"]},
+ {"zone": "ff.", "forwarders": ["1.2.3.4"]},
+ {"zone": "f.", "forwarders": ["1.2.3.4"]},
+ {"zone": "gg.", "forwarders": ["1.2.3.4"]},
+ ]
+ }
+ )
# eighth zone, all entries should be gone
self.sendNotify()
time.sleep(3)
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(12)
- self.checkForwards({'forward_zones': [
- {'zone': 'g.', 'forwarders': ['1.2.3.4']}
- ]})
-
-
+ self.checkForwards({"forward_zones": [{"zone": "g.", "forwarders": ["1.2.3.4"]}]})
class FlagsTest(RecursorTest):
- _confdir = 'Flags'
+ _confdir = "Flags"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """dnssec=%s"""
- _config_params = ['_dnssec_setting']
+ _config_params = ["_dnssec_setting"]
_dnssec_setting = None
_recursors = {}
- _dnssec_setting_ports = {'off': 5300,
- 'process-no-validate': 5301,
- 'process': 5302,
- 'validate': 5303}
+ _dnssec_setting_ports = {"off": 5300, "process-no-validate": 5301, "process": 5302, "validate": 5303}
@classmethod
def setUp(cls):
for setting in cls._dnssec_setting_ports:
- confdir = os.path.join('configs', cls._confdir, setting)
+ confdir = os.path.join("configs", cls._confdir, setting)
cls.wipeRecursorCache(confdir)
@classmethod
def setUpClass(cls):
cls.setUpSockets()
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
cls.createConfigDir(confdir)
# Moved to fixture
- #cls.generateAllAuthConfig(confdir)
- #cls.startAllAuth(confdir)
+ # cls.generateAllAuthConfig(confdir)
+ # cls.startAllAuth(confdir)
for dnssec_setting, port in cls._dnssec_setting_ports.items():
cls._dnssec_setting = dnssec_setting
cls._sock = {}
for dnssec_setting, port in cls._dnssec_setting_ports.items():
print("Setting up UDP socket..")
- cls._sock[dnssec_setting] = socket.socket(socket.AF_INET,
- socket.SOCK_DGRAM)
+ cls._sock[dnssec_setting] = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
cls._sock[dnssec_setting].settimeout(2.0)
cls._sock[dnssec_setting].connect(("127.0.0.1", port))
@classmethod
def tearDownClass(cls):
- #cls.tearDownAuth() moved to fixture
+ # cls.tearDownAuth() moved to fixture
for subdir, recursor in cls._recursors.items():
cls._recursor = recursor
cls.tearDownRecursor(subdir)
- def getQueryForSecure(self, flags='', ednsflags=''):
- return self.createQuery('ns1.example.', 'A', flags, ednsflags)
+ def getQueryForSecure(self, flags="", ednsflags=""):
+ return self.createQuery("ns1.example.", "A", flags, ednsflags)
##
# -AD -CD -DO
##
def testOff_Secure_None(self):
msg = self.getQueryForSecure()
- res = self.sendUDPQuery(msg, 'off')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ res = self.sendUDPQuery(msg, "off")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testProcessNoValidate_Secure_None(self):
msg = self.getQueryForSecure()
- res = self.sendUDPQuery(msg, 'process-no-validate')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ res = self.sendUDPQuery(msg, "process-no-validate")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testProcess_Secure_None(self):
msg = self.getQueryForSecure()
- res = self.sendUDPQuery(msg, 'process')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ res = self.sendUDPQuery(msg, "process")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testValidate_Secure_None(self):
msg = self.getQueryForSecure()
- res = self.sendUDPQuery(msg, 'validate')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ res = self.sendUDPQuery(msg, "validate")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
##
# +AD -CD -DO
##
def testOff_Secure_AD(self):
- msg = self.getQueryForSecure('AD')
- res = self.sendUDPQuery(msg, 'off')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ msg = self.getQueryForSecure("AD")
+ res = self.sendUDPQuery(msg, "off")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testProcessNoValidate_Secure_AD(self):
- msg = self.getQueryForSecure('AD')
- res = self.sendUDPQuery(msg, 'process-no-validate')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ msg = self.getQueryForSecure("AD")
+ res = self.sendUDPQuery(msg, "process-no-validate")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testProcess_Secure_AD(self):
- msg = self.getQueryForSecure('AD')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForSecure("AD")
+ res = self.sendUDPQuery(msg, "process")
self.assertMessageIsAuthenticated(res)
- self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["AD", "QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testValidate_Secure_AD(self):
- msg = self.getQueryForSecure('AD')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForSecure("AD")
+ res = self.sendUDPQuery(msg, "validate")
self.assertMessageIsAuthenticated(res)
- self.assertMessageHasFlags(res, ['AD', 'RD', 'RA', 'QR'])
+ self.assertMessageHasFlags(res, ["AD", "RD", "RA", "QR"])
self.assertNoRRSIGsInAnswer(res)
##
# +AD -CD +DO
##
def testOff_Secure_ADDO(self):
- msg = self.getQueryForSecure('AD', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForSecure("AD", "DO")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testProcessNoValidate_Secure_ADDO(self):
- msg = self.getQueryForSecure('AD', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForSecure("AD", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testProcess_Secure_ADDO(self):
- msg = self.getQueryForSecure('AD', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForSecure("AD", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "process")
self.assertMessageIsAuthenticated(res)
- self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["AD", "QR", "RA", "RD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testValidate_Secure_ADDO(self):
- msg = self.getQueryForSecure('AD', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForSecure("AD", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "validate")
self.assertMessageIsAuthenticated(res)
- self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["AD", "QR", "RA", "RD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
##
# +AD +CD +DO
##
def testOff_Secure_ADDOCD(self):
- msg = self.getQueryForSecure('AD CD', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForSecure("AD CD", "DO")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
def testProcessNoValidate_Secure_ADDOCD(self):
- msg = self.getQueryForSecure('AD CD', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForSecure("AD CD", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['CD', 'QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["CD", "QR", "RA", "RD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testProcess_Secure_ADDOCD(self):
- msg = self.getQueryForSecure('AD CD', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForSecure("AD CD", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "process")
self.assertMessageIsAuthenticated(res)
- self.assertMessageHasFlags(res, ['AD', 'CD', 'QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["AD", "CD", "QR", "RA", "RD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testValidate_Secure_ADDOCD(self):
- msg = self.getQueryForSecure('AD CD', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForSecure("AD CD", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "validate")
self.assertMessageIsAuthenticated(res)
- self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["AD", "QR", "RA", "RD", "CD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
##
# -AD -CD +DO
##
def testOff_Secure_DO(self):
- msg = self.getQueryForSecure('', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForSecure("", "DO")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testProcessNoValidate_Secure_DO(self):
- msg = self.getQueryForSecure('', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForSecure("", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testProcess_Secure_DO(self):
- msg = self.getQueryForSecure('', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForSecure("", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "process")
- self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["AD", "QR", "RA", "RD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testValidate_Secure_DO(self):
- msg = self.getQueryForSecure('', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForSecure("", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "validate")
- self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["AD", "QR", "RA", "RD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
##
# -AD +CD +DO
##
def testOff_Secure_DOCD(self):
- msg = self.getQueryForSecure('CD', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForSecure("CD", "DO")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testProcessNoValidate_Secure_DOCD(self):
- msg = self.getQueryForSecure('CD', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForSecure("CD", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testProcess_Secure_DOCD(self):
- msg = self.getQueryForSecure('CD', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForSecure("CD", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "process")
- self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["AD", "QR", "RA", "RD", "CD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testValidate_Secure_DOCD(self):
- msg = self.getQueryForSecure('CD', 'DO')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForSecure("CD", "DO")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "validate")
- self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["AD", "QR", "RA", "RD", "CD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
##
# -AD +CD -DO
##
def testOff_Secure_CD(self):
- msg = self.getQueryForSecure('CD')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForSecure("CD")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertRRsetInAnswer(res, expected)
self.assertNoRRSIGsInAnswer(res)
def testProcessNoValidate_Secure_CD(self):
- msg = self.getQueryForSecure('CD')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForSecure("CD")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"])
self.assertRRsetInAnswer(res, expected)
self.assertNoRRSIGsInAnswer(res)
def testProcess_Secure_CD(self):
- msg = self.getQueryForSecure('CD')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForSecure("CD")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "process")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"])
self.assertRRsetInAnswer(res, expected)
self.assertNoRRSIGsInAnswer(res)
def testValidate_Secure_CD(self):
- msg = self.getQueryForSecure('CD')
- expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForSecure("CD")
+ expected = dns.rrset.from_text(
+ "ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX)
+ )
+ res = self.sendUDPQuery(msg, "validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"])
self.assertRRsetInAnswer(res, expected)
self.assertNoRRSIGsInAnswer(res)
-
### Bogus
- def getQueryForBogus(self, flags='', ednsflags=''):
- return self.createQuery('ted.bogus.example.', 'A', flags, ednsflags)
+ def getQueryForBogus(self, flags="", ednsflags=""):
+ return self.createQuery("ted.bogus.example.", "A", flags, ednsflags)
##
# -AD -CD -DO
##
def testOff_Bogus_None(self):
msg = self.getQueryForBogus()
- res = self.sendUDPQuery(msg, 'off')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ res = self.sendUDPQuery(msg, "off")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcessNoValidate_Bogus_None(self):
msg = self.getQueryForBogus()
- res = self.sendUDPQuery(msg, 'process-no-validate')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ res = self.sendUDPQuery(msg, "process-no-validate")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcess_Bogus_None(self):
msg = self.getQueryForBogus()
- res = self.sendUDPQuery(msg, 'process')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ res = self.sendUDPQuery(msg, "process")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testValidate_Bogus_None(self):
msg = self.getQueryForBogus()
- res = self.sendUDPQuery(msg, 'validate')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ res = self.sendUDPQuery(msg, "validate")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertAnswerEmpty(res)
# +AD -CD -DO
##
def testOff_Bogus_AD(self):
- msg = self.getQueryForBogus('AD')
- res = self.sendUDPQuery(msg, 'off')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ msg = self.getQueryForBogus("AD")
+ res = self.sendUDPQuery(msg, "off")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcessNoValidate_Bogus_AD(self):
- msg = self.getQueryForBogus('AD')
- res = self.sendUDPQuery(msg, 'process-no-validate')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ msg = self.getQueryForBogus("AD")
+ res = self.sendUDPQuery(msg, "process-no-validate")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcess_Bogus_AD(self):
- msg = self.getQueryForBogus('AD')
- res = self.sendUDPQuery(msg, 'process')
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ msg = self.getQueryForBogus("AD")
+ res = self.sendUDPQuery(msg, "process")
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertAnswerEmpty(res)
def testValidate_Bogus_AD(self):
- msg = self.getQueryForBogus('AD')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForBogus("AD")
+ res = self.sendUDPQuery(msg, "validate")
- self.assertMessageHasFlags(res, ['RD', 'RA', 'QR'])
+ self.assertMessageHasFlags(res, ["RD", "RA", "QR"])
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertAnswerEmpty(res)
# +AD -CD +DO
##
def testOff_Bogus_ADDO(self):
- msg = self.getQueryForBogus('AD', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForBogus("AD", "DO")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcessNoValidate_Bogus_ADDO(self):
- msg = self.getQueryForBogus('AD', 'DO')
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForBogus("AD", "DO")
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcess_Bogus_ADDO(self):
- msg = self.getQueryForBogus('AD', 'DO')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForBogus("AD", "DO")
+ res = self.sendUDPQuery(msg, "process")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertAnswerEmpty(res)
def testValidate_Bogus_ADDO(self):
- msg = self.getQueryForBogus('AD', 'DO')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForBogus("AD", "DO")
+ res = self.sendUDPQuery(msg, "validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertAnswerEmpty(res)
+
##
# +AD +CD +DO
##
def testOff_Bogus_ADDOCD(self):
- msg = self.getQueryForBogus('AD CD', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForBogus("AD CD", "DO")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcessNoValidate_Bogus_ADDOCD(self):
- msg = self.getQueryForBogus('AD CD', 'DO')
- expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForBogus("AD CD", "DO")
+ expected = dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "process-no-validate")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['CD', 'QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["CD", "QR", "RA", "RD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testProcess_Bogus_ADDOCD(self):
- msg = self.getQueryForBogus('AD CD', 'DO')
- expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForBogus("AD CD", "DO")
+ expected = dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "process")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['CD', 'QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["CD", "QR", "RA", "RD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testValidate_Bogus_ADDOCD(self):
- msg = self.getQueryForBogus('AD CD', 'DO')
- expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForBogus("AD CD", "DO")
+ expected = dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "validate")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
##
# -AD -CD +DO
##
def testOff_Bogus_DO(self):
- msg = self.getQueryForBogus('', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForBogus("", "DO")
+ res = self.sendUDPQuery(msg, "off")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testProcessNoValidate_Bogus_DO(self):
- msg = self.getQueryForBogus('', 'DO')
- expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForBogus("", "DO")
+ expected = dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "process-no-validate")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testProcess_Bogus_DO(self):
- msg = self.getQueryForBogus('', 'DO')
- dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForBogus("", "DO")
+ dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "process")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertAnswerEmpty(res)
def testValidate_Bogus_DO(self):
- msg = self.getQueryForBogus('', 'DO')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForBogus("", "DO")
+ res = self.sendUDPQuery(msg, "validate")
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertAnswerEmpty(res)
##
# -AD +CD +DO
##
def testOff_Bogus_DOCD(self):
- msg = self.getQueryForBogus('CD', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForBogus("CD", "DO")
+ res = self.sendUDPQuery(msg, "off")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testProcessNoValidate_Bogus_DOCD(self):
- msg = self.getQueryForBogus('CD', 'DO')
- expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForBogus("CD", "DO")
+ expected = dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "process-no-validate")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testProcess_Bogus_DOCD(self):
- msg = self.getQueryForBogus('CD', 'DO')
- expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForBogus("CD", "DO")
+ expected = dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "process")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
def testValidate_Bogus_DOCD(self):
- msg = self.getQueryForBogus('CD', 'DO')
- expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForBogus("CD", "DO")
+ expected = dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "validate")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"], ["DO"])
self.assertMatchingRRSIGInAnswer(res, expected)
##
# -AD +CD -DO
##
def testOff_Bogus_CD(self):
- msg = self.getQueryForBogus('CD')
- expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForBogus("CD")
+ expected = dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "off")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertRRsetInAnswer(res, expected)
self.assertNoRRSIGsInAnswer(res)
def testProcessNoValidate_Bogus_CD(self):
- msg = self.getQueryForBogus('CD')
- expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForBogus("CD")
+ expected = dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "process-no-validate")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"])
self.assertRRsetInAnswer(res, expected)
self.assertNoRRSIGsInAnswer(res)
def testProcess_Bogus_CD(self):
- msg = self.getQueryForBogus('CD')
- expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForBogus("CD")
+ expected = dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "process")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"])
self.assertRRsetInAnswer(res, expected)
self.assertNoRRSIGsInAnswer(res)
def testValidate_Bogus_CD(self):
- msg = self.getQueryForBogus('CD')
- expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForBogus("CD")
+ expected = dns.rrset.from_text("ted.bogus.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1")
+ res = self.sendUDPQuery(msg, "validate")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"])
self.assertRRsetInAnswer(res, expected)
self.assertNoRRSIGsInAnswer(res)
-
## Insecure
- def getQueryForInsecure(self, flags='', ednsflags=''):
- return self.createQuery('node1.insecure.example.', 'A', flags, ednsflags)
+ def getQueryForInsecure(self, flags="", ednsflags=""):
+ return self.createQuery("node1.insecure.example.", "A", flags, ednsflags)
##
# -AD -CD -DO
##
def testOff_Insecure_None(self):
msg = self.getQueryForInsecure()
- res = self.sendUDPQuery(msg, 'off')
+ res = self.sendUDPQuery(msg, "off")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testProcessNoValidate_Insecure_None(self):
msg = self.getQueryForInsecure()
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ res = self.sendUDPQuery(msg, "process-no-validate")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testProcess_Insecure_None(self):
msg = self.getQueryForInsecure()
- res = self.sendUDPQuery(msg, 'process')
+ res = self.sendUDPQuery(msg, "process")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
def testValidate_Insecure_None(self):
msg = self.getQueryForInsecure()
- res = self.sendUDPQuery(msg, 'validate')
+ res = self.sendUDPQuery(msg, "validate")
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
##
# +AD -CD -DO
##
def testOff_Insecure_AD(self):
- msg = self.getQueryForInsecure('AD')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForInsecure("AD")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcessNoValidate_Insecure_AD(self):
- msg = self.getQueryForInsecure('AD')
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForInsecure("AD")
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcess_Insecure_AD(self):
- msg = self.getQueryForInsecure('AD')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForInsecure("AD")
+ res = self.sendUDPQuery(msg, "process")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testValidate_Insecure_AD(self):
- msg = self.getQueryForInsecure('AD')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForInsecure("AD")
+ res = self.sendUDPQuery(msg, "validate")
- self.assertMessageHasFlags(res, ['RD', 'RA', 'QR'])
+ self.assertMessageHasFlags(res, ["RD", "RA", "QR"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
# +AD -CD +DO
##
def testOff_Insecure_ADDO(self):
- msg = self.getQueryForInsecure('AD', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForInsecure("AD", "DO")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcessNoValidate_Insecure_ADDO(self):
- msg = self.getQueryForInsecure('AD', 'DO')
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForInsecure("AD", "DO")
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcess_Insecure_ADDO(self):
- msg = self.getQueryForInsecure('AD', 'DO')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForInsecure("AD", "DO")
+ res = self.sendUDPQuery(msg, "process")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testValidate_Insecure_ADDO(self):
- msg = self.getQueryForInsecure('AD', 'DO')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForInsecure("AD", "DO")
+ res = self.sendUDPQuery(msg, "validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
# +AD +CD +DO
##
def testOff_Insecure_ADDOCD(self):
- msg = self.getQueryForInsecure('AD CD', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForInsecure("AD CD", "DO")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcessNoValidate_Insecure_ADDOCD(self):
- msg = self.getQueryForInsecure('AD CD', 'DO')
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForInsecure("AD CD", "DO")
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['CD', 'QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["CD", "QR", "RA", "RD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcess_Insecure_ADDOCD(self):
- msg = self.getQueryForInsecure('AD CD', 'DO')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForInsecure("AD CD", "DO")
+ res = self.sendUDPQuery(msg, "process")
- self.assertMessageHasFlags(res, ['CD', 'QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["CD", "QR", "RA", "RD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testValidate_Insecure_ADDOCD(self):
- msg = self.getQueryForInsecure('AD CD', 'DO')
- dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForInsecure("AD CD", "DO")
+ dns.rrset.from_text("ns1.example.", 0, dns.rdataclass.IN, "A", "{prefix}.10".format(prefix=self._PREFIX))
+ res = self.sendUDPQuery(msg, "validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
# -AD -CD +DO
##
def testOff_Insecure_DO(self):
- msg = self.getQueryForInsecure('', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForInsecure("", "DO")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcessNoValidate_Insecure_DO(self):
- msg = self.getQueryForInsecure('', 'DO')
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForInsecure("", "DO")
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcess_Insecure_DO(self):
- msg = self.getQueryForInsecure('', 'DO')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForInsecure("", "DO")
+ res = self.sendUDPQuery(msg, "process")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testValidate_Insecure_DO(self):
- msg = self.getQueryForInsecure('', 'DO')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForInsecure("", "DO")
+ res = self.sendUDPQuery(msg, "validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
# -AD +CD +DO
##
def testOff_Insecure_DOCD(self):
- msg = self.getQueryForInsecure('CD', 'DO')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForInsecure("CD", "DO")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcessNoValidate_Insecure_DOCD(self):
- msg = self.getQueryForInsecure('CD', 'DO')
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForInsecure("CD", "DO")
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcess_Insecure_DOCD(self):
- msg = self.getQueryForInsecure('CD', 'DO')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForInsecure("CD", "DO")
+ res = self.sendUDPQuery(msg, "process")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testValidate_Insecure_DOCD(self):
- msg = self.getQueryForInsecure('CD', 'DO')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForInsecure("CD", "DO")
+ res = self.sendUDPQuery(msg, "validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"], ["DO"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
# -AD +CD -DO
##
def testOff_Insecure_CD(self):
- msg = self.getQueryForInsecure('CD')
- res = self.sendUDPQuery(msg, 'off')
+ msg = self.getQueryForInsecure("CD")
+ res = self.sendUDPQuery(msg, "off")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcessNoValidate_Insecure_CD(self):
- msg = self.getQueryForInsecure('CD')
- res = self.sendUDPQuery(msg, 'process-no-validate')
+ msg = self.getQueryForInsecure("CD")
+ res = self.sendUDPQuery(msg, "process-no-validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testProcess_Insecure_CD(self):
- msg = self.getQueryForInsecure('CD')
- res = self.sendUDPQuery(msg, 'process')
+ msg = self.getQueryForInsecure("CD")
+ res = self.sendUDPQuery(msg, "process")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testValidate_Insecure_CD(self):
- msg = self.getQueryForInsecure('CD')
- res = self.sendUDPQuery(msg, 'validate')
+ msg = self.getQueryForInsecure("CD")
+ res = self.sendUDPQuery(msg, "validate")
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"])
self.assertNoRRSIGsInAnswer(res)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
import subprocess
from recursortests import RecursorTest
+
class SimpleForwardOverDoTTest(RecursorTest):
"""
This is forwarding to DoT servers in a very basic way and is dependent on the forwards working for DoT
"""
- _confdir = 'SimpleForwardOverDoT'
+ _confdir = "SimpleForwardOverDoT"
_config_template = """
dnssec:
validation: validate
super(SimpleForwardOverDoTTest, cls).generateRecursorYamlConfig(confdir, False)
def reloadConfig(self, config):
- confdir = os.path.join('configs', SimpleForwardOverDoTTest._confdir)
- SimpleForwardOverDoTTest._config_template = config
- SimpleForwardOverDoTTest.generateRecursorYamlConfig(confdir, False)
- SimpleForwardOverDoTTest.recControl(confdir, 'reload-yaml')
+ confdir = os.path.join("configs", SimpleForwardOverDoTTest._confdir)
+ SimpleForwardOverDoTTest._config_template = config
+ SimpleForwardOverDoTTest.generateRecursorYamlConfig(confdir, False)
+ SimpleForwardOverDoTTest.recControl(confdir, "reload-yaml")
@pytest.mark.external
def testBasic(self):
- confdir = 'configs/' + self._confdir
+ confdir = "configs/" + self._confdir
self.reloadConfig(self._config_template)
- self.recControl(confdir, 'reload-zones')
- expected = dns.rrset.from_text('dns.google.', 0, dns.rdataclass.IN, 'A', '8.8.8.8', '8.8.4.4')
- query = dns.message.make_query('dns.google', 'A', want_dnssec=True)
+ self.recControl(confdir, "reload-zones")
+ expected = dns.rrset.from_text("dns.google.", 0, dns.rdataclass.IN, "A", "8.8.8.8", "8.8.4.4")
+ query = dns.message.make_query("dns.google", "A", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
self.assertMatchingRRSIGInAnswer(res, expected)
- ret = self.recControl(confdir, 'get', 'dot-outqueries')
- self.assertNotEqual(ret, 'UNKNOWN\n')
- self.assertNotEqual(ret, '0\n')
+ ret = self.recControl(confdir, "get", "dot-outqueries")
+ self.assertNotEqual(ret, "UNKNOWN\n")
+ self.assertNotEqual(ret, "0\n")
- ret = self.recControl(confdir, 'get', 'tcp-outqueries')
- self.assertEqual(ret, '0\n')
+ ret = self.recControl(confdir, "get", "tcp-outqueries")
+ self.assertEqual(ret, "0\n")
_config_template_test2 = """
dnssec:
@pytest.mark.external
def testWithVerify(self):
- confdir = 'configs/' + self._confdir
+ confdir = "configs/" + self._confdir
self.reloadConfig(self._config_template_test2)
- self.recControl(confdir, 'reload-zones')
- expected = dns.rrset.from_text('dns.google.', 0, dns.rdataclass.IN, 'A', '8.8.8.8', '8.8.4.4')
- query = dns.message.make_query('dns.google', 'A', want_dnssec=True)
+ self.recControl(confdir, "reload-zones")
+ expected = dns.rrset.from_text("dns.google.", 0, dns.rdataclass.IN, "A", "8.8.8.8", "8.8.4.4")
+ query = dns.message.make_query("dns.google", "A", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
self.assertMatchingRRSIGInAnswer(res, expected)
- ret = self.recControl(confdir, 'get', 'dot-outqueries')
- self.assertNotEqual(ret, 'UNKNOWN\n')
- self.assertNotEqual(ret, '0\n')
+ ret = self.recControl(confdir, "get", "dot-outqueries")
+ self.assertNotEqual(ret, "UNKNOWN\n")
+ self.assertNotEqual(ret, "0\n")
- ret = self.recControl(confdir, 'get', 'tcp-outqueries')
- self.assertEqual(ret, '0\n')
+ ret = self.recControl(confdir, "get", "tcp-outqueries")
+ self.assertEqual(ret, "0\n")
_config_template_test3 = """
dnssec:
@pytest.mark.external
def testCertFailed(self):
- confdir = 'configs/' + self._confdir
+ confdir = "configs/" + self._confdir
self.reloadConfig(self._config_template_test3)
- self.recControl(confdir, 'reload-zones')
- expected = dns.rrset.from_text('dns.google.', 0, dns.rdataclass.IN, 'A', '8.8.8.8', '8.8.4.4')
- query = dns.message.make_query('dns.google', 'A', want_dnssec=True)
+ self.recControl(confdir, "reload-zones")
+ expected = dns.rrset.from_text("dns.google.", 0, dns.rdataclass.IN, "A", "8.8.8.8", "8.8.4.4")
+ query = dns.message.make_query("dns.google", "A", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
+
class InteropTest(RecursorTest):
- _confdir = 'Interop'
+ _confdir = "Interop"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """dnssec=validate
packetcache-ttl=0 # explicitly disable packetcache
forward-zones=undelegated.secure.example=%s.12
forward-zones+=undelegated.insecure.example=%s.12
- """ % (os.environ['PREFIX'], os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"], os.environ["PREFIX"])
def testFORMERR(self):
"""
#3841, when we encounter a server that does not understands OPT records
(or something else), we don't retry without EDNS in dnssec=validate mode
"""
- expected = dns.rrset.from_text('host1.insecure-formerr.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ expected = dns.rrset.from_text("host1.insecure-formerr.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
- query = dns.message.make_query('cname-to-formerr.secure.example.', 'A')
+ query = dns.message.make_query("cname-to-formerr.secure.example.", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
"""
#4158, When chasing down for DS/DNSKEY and we find a CNAME, skip a level
"""
- expected = dns.rrset.from_text('node1.insecure.sub2.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.18')
+ expected = dns.rrset.from_text("node1.insecure.sub2.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.18")
- query = dns.message.make_query('node1.insecure.sub2.secure.example.', 'A')
+ query = dns.message.make_query("node1.insecure.sub2.secure.example.", "A")
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], [])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], [])
self.assertRRsetInAnswer(res, expected)
def testUndelegatedForwardedZoneExisting(self):
#4369. Ensure we SERVFAIL when forwarding to undelegated zones for a name that exists
"""
- query = dns.message.make_query('node1.undelegated.secure.example.', 'A')
+ query = dns.message.make_query("node1.undelegated.secure.example.", "A")
query.flags |= dns.flags.AD
# twice, so we hit the record cache
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], [])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], [])
def testUndelegatedForwardedZoneNXDOMAIN(self):
"""
#4369. Ensure we SERVFAIL when forwarding to undelegated zones for a name that does not exist
"""
- query = dns.message.make_query('node2.undelegated.secure.example.', 'A')
+ query = dns.message.make_query("node2.undelegated.secure.example.", "A")
query.flags |= dns.flags.AD
# twice, so we hit the negative record cache
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], [])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], [])
def testUndelegatedForwardedInsecureZoneExisting(self):
"""
#4369. Ensure we answer when forwarding to an undelegated zone in an insecure zone for a name that exists
"""
- expected = dns.rrset.from_text('node1.undelegated.insecure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.22')
- query = dns.message.make_query('node1.undelegated.insecure.example.', 'A')
+ expected = dns.rrset.from_text("node1.undelegated.insecure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.22")
+ query = dns.message.make_query("node1.undelegated.insecure.example.", "A")
query.flags |= dns.flags.AD
# twice, so we hit the record cache
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], [])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], [])
self.assertRRsetInAnswer(res, expected)
def testUndelegatedForwardedInsecureZoneNXDOMAIN(self):
#4369. Ensure we answer when forwarding to an undelegated zone in an insecure zone for a name that does not exist
"""
- query = dns.message.make_query('node2.undelegated.insecure.example.', 'A')
+ query = dns.message.make_query("node2.undelegated.insecure.example.", "A")
query.flags |= dns.flags.AD
# twice, so we hit the negative record cache
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], [])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], [])
def testBothSecureCNAMEAtApex(self):
"""
#4466: a CNAME at the apex of a secure domain to another secure domain made us use the wrong DNSKEY to validate
"""
- query = dns.message.make_query('cname-secure.example.', 'A')
+ query = dns.message.make_query("cname-secure.example.", "A")
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
- expectedCNAME = dns.rrset.from_text('cname-secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'secure.example.')
- expectedA = dns.rrset.from_text('secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.17')
+ expectedCNAME = dns.rrset.from_text("cname-secure.example.", 0, dns.rdataclass.IN, "CNAME", "secure.example.")
+ expectedA = dns.rrset.from_text("secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.17")
self.assertRRsetInAnswer(res, expectedA)
self.assertRRsetInAnswer(res, expectedCNAME)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'AD'], [])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA", "AD"], [])
def testNonApexDNSKEY(self):
"""
a DNSKEY not at the apex of a zone should not be treated as a DNSKEY in validation
"""
- query = dns.message.make_query('non-apex-dnskey.secure.example.', 'DNSKEY')
+ query = dns.message.make_query("non-apex-dnskey.secure.example.", "DNSKEY")
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
print(res)
- expectedDNSKEY = dns.rrset.from_text('non-apex-dnskey.secure.example.', 0, dns.rdataclass.IN, 'DNSKEY', '257 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==')
+ expectedDNSKEY = dns.rrset.from_text(
+ "non-apex-dnskey.secure.example.",
+ 0,
+ dns.rdataclass.IN,
+ "DNSKEY",
+ "257 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==",
+ )
self.assertRRsetInAnswer(res, expectedDNSKEY)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'AD'], [])
-
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA", "AD"], [])
@classmethod
def startResponders(cls):
global interopReactorRunning
print("Launching responders..")
- address = cls._PREFIX + '.2'
+ address = cls._PREFIX + ".2"
port = 53
if not interopReactorRunning:
cls.startReactor()
+
interopReactorRunning = False
+
class InteropProcessTest(RecursorTest):
- _confdir = 'InteropProcess'
+ _confdir = "InteropProcess"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """dnssec=process
packetcache-ttl=0 # explicitly disable packetcache
forward-zones=undelegated.secure.example=%s.12
forward-zones+=undelegated.insecure.example=%s.12
- """ % (os.environ['PREFIX'], os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"], os.environ["PREFIX"])
@classmethod
def startResponders(cls):
global interopReactorRunning
print("Launching responders..")
- address = cls._PREFIX + '.2'
+ address = cls._PREFIX + ".2"
port = 53
if not interopReactorRunning:
"""
# send the query with +CD so the record ends up cached with Indeterminate validation state
- query = dns.message.make_query('non-apex-dnskey2.secure.example.', 'DNSKEY')
+ query = dns.message.make_query("non-apex-dnskey2.secure.example.", "DNSKEY")
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
print(res)
- expectedDNSKEY = dns.rrset.from_text('non-apex-dnskey2.secure.example.', 0, dns.rdataclass.IN, 'DNSKEY', '256 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==')
+ expectedDNSKEY = dns.rrset.from_text(
+ "non-apex-dnskey2.secure.example.",
+ 0,
+ dns.rdataclass.IN,
+ "DNSKEY",
+ "256 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==",
+ )
self.assertRRsetInAnswer(res, expectedDNSKEY)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'CD'], [])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA", "CD"], [])
# now ask again with +AD so it has to be validated from cache
- query = dns.message.make_query('non-apex-dnskey2.secure.example.', 'DNSKEY')
+ query = dns.message.make_query("non-apex-dnskey2.secure.example.", "DNSKEY")
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
print(res)
- expectedDNSKEY = dns.rrset.from_text('non-apex-dnskey2.secure.example.', 0, dns.rdataclass.IN, 'DNSKEY', '256 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==')
+ expectedDNSKEY = dns.rrset.from_text(
+ "non-apex-dnskey2.secure.example.",
+ 0,
+ dns.rdataclass.IN,
+ "DNSKEY",
+ "256 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==",
+ )
self.assertRRsetInAnswer(res, expectedDNSKEY)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'AD'], [])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA", "AD"], [])
def testNonApexDNSKEYANYQuery(self):
"""
"""
# send the query with +CD so the record ends up cached with Indeterminate validation state
- query = dns.message.make_query('non-apex-dnskey3.secure.example.', 'DNSKEY')
+ query = dns.message.make_query("non-apex-dnskey3.secure.example.", "DNSKEY")
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
print(res)
- expectedDNSKEY = dns.rrset.from_text('non-apex-dnskey3.secure.example.', 0, dns.rdataclass.IN, 'DNSKEY', '256 3 13 DT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==')
+ expectedDNSKEY = dns.rrset.from_text(
+ "non-apex-dnskey3.secure.example.",
+ 0,
+ dns.rdataclass.IN,
+ "DNSKEY",
+ "256 3 13 DT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==",
+ )
self.assertRRsetInAnswer(res, expectedDNSKEY)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'CD'], [])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA", "CD"], [])
# now ask again with +AD so it has to be validated from cache
- query = dns.message.make_query('non-apex-dnskey3.secure.example.', 'ANY')
+ query = dns.message.make_query("non-apex-dnskey3.secure.example.", "ANY")
query.flags |= dns.flags.AD
res = self.sendTCPQuery(query)
print(res)
- expectedDNSKEY = dns.rrset.from_text('non-apex-dnskey3.secure.example.', 0, dns.rdataclass.IN, 'DNSKEY', '256 3 13 DT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==')
+ expectedDNSKEY = dns.rrset.from_text(
+ "non-apex-dnskey3.secure.example.",
+ 0,
+ dns.rdataclass.IN,
+ "DNSKEY",
+ "256 3 13 DT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==",
+ )
self.assertRRsetInAnswer(res, expectedDNSKEY)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RD', 'RA', 'AD'], [])
+ self.assertMessageHasFlags(res, ["QR", "RD", "RA", "AD"], [])
+
class UDPResponder(DatagramProtocol):
def datagramReceived(self, datagram, address):
response.additional = []
else:
- if request.question[0].name == dns.name.from_text('host1.insecure-formerr.example.') and request.question[0].rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('host1.insecure-formerr.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ if (
+ request.question[0].name == dns.name.from_text("host1.insecure-formerr.example.")
+ and request.question[0].rdtype == dns.rdatatype.A
+ ):
+ answer = dns.rrset.from_text("host1.insecure-formerr.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
response.answer.append(answer)
- elif request.question[0].name == dns.name.from_text('insecure-formerr.example.') and request.question[0].rdtype == dns.rdatatype.NS:
- answer = dns.rrset.from_text('insecure-formerr.example.', 15, dns.rdataclass.IN, 'NS', 'ns1.insecure-formerr.example.')
+ elif (
+ request.question[0].name == dns.name.from_text("insecure-formerr.example.")
+ and request.question[0].rdtype == dns.rdatatype.NS
+ ):
+ answer = dns.rrset.from_text(
+ "insecure-formerr.example.", 15, dns.rdataclass.IN, "NS", "ns1.insecure-formerr.example."
+ )
response.answer.append(answer)
- additional = dns.rrset.from_text('ns1.insecure-formerr.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.2')
+ additional = dns.rrset.from_text(
+ "ns1.insecure-formerr.example.", 15, dns.rdataclass.IN, "A", "127.0.0.2"
+ )
response.additional.append(additional)
self.transport.write(response.to_wire(), address)
from recursortests import RecursorTest
+
class KeepOpenTCPTest(RecursorTest):
- _confdir = 'KeepOpenTCP'
+ _confdir = "KeepOpenTCP"
_auth_zones = RecursorTest._default_auth_zones
- _config_template = """dnssec=validate
+ _config_template = (
+ """dnssec=validate
packetcache-ttl=10
packetcache-servfail-ttl=10
-auth-zones=authzone.example=configs/%s/authzone.zone""" % _confdir
+auth-zones=authzone.example=configs/%s/authzone.zone"""
+ % _confdir
+ )
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'authzone.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN authzone.example.
+ authzonepath = os.path.join(confdir, "authzone.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN authzone.example.
@ 3600 IN SOA {soa}
@ 3600 IN A 192.0.2.88
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(KeepOpenTCPTest, cls).generateRecursorConfig(confdir)
def sendTCPQueryKeepOpen(cls, sock, query, timeout=2.0):
def testNoTrailingData(self):
count = 10
sock = [None] * count
- expected = dns.rrset.from_text('ns.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.9'.format(prefix=self._PREFIX))
- query = dns.message.make_query('ns.secure.example', 'A', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "ns.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.9".format(prefix=self._PREFIX)
+ )
+ query = dns.message.make_query("ns.secure.example", "A", want_dnssec=True)
query.flags |= dns.flags.AD
for i in range(count):
sock[i] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("ok")
for i in range(count):
sock[i].close()
-
from recursortests import RecursorTest
+
class LockedCacheTest(RecursorTest):
"""
Test that a locked cached entry is *not* updated by the same additional encountered in a second query
"""
- _confdir = 'LockedCache'
+
+ _confdir = "LockedCache"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
"""
def getCacheTTL(self):
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'dump-cache',
- '-']
+ rec_controlCmd = [os.environ["RECCONTROL"], "--config-dir=%s" % "configs/" + self._confdir, "dump-cache", "-"]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT, text=True)
for i in ret.splitlines():
- pieces = i.split(' ')
+ pieces = i.split(" ")
print(pieces)
- if pieces[0] == 'mx1.secure.example.' and pieces[4] == 'A':
+ if pieces[0] == "mx1.secure.example." and pieces[4] == "A":
return pieces[2]
- raise AssertionError("Cache Line not found");
-
+ raise AssertionError("Cache Line not found")
except subprocess.CalledProcessError as e:
print(e.output)
raise
def testMX(self):
- expected1 = dns.rrset.from_text('secure.example.', 0, dns.rdataclass.IN, 'MX', '10 mx1.secure.example.', '20 mx2.secure.example.')
- expected2 = dns.rrset.from_text('sub.secure.example.', 0, dns.rdataclass.IN, 'MX', '10 mx1.secure.example.', '20 mx2.secure.example.')
- query1 = dns.message.make_query('secure.example', 'MX', want_dnssec=True)
+ expected1 = dns.rrset.from_text(
+ "secure.example.", 0, dns.rdataclass.IN, "MX", "10 mx1.secure.example.", "20 mx2.secure.example."
+ )
+ expected2 = dns.rrset.from_text(
+ "sub.secure.example.", 0, dns.rdataclass.IN, "MX", "10 mx1.secure.example.", "20 mx2.secure.example."
+ )
+ query1 = dns.message.make_query("secure.example", "MX", want_dnssec=True)
query1.flags |= dns.flags.AD
- query2 = dns.message.make_query('sub.secure.example', 'MX', want_dnssec=True)
+ query2 = dns.message.make_query("sub.secure.example", "MX", want_dnssec=True)
query2.flags |= dns.flags.AD
res = self.sendUDPQuery(query1)
ttl2 = self.getCacheTTL()
self.assertGreater(ttl1, ttl2)
+
class NotLockedCacheTest(RecursorTest):
"""
Test that a not locked cached entry *is* updated by the same additional encountered in a second query
"""
- _confdir = 'NotLockedCache'
+
+ _confdir = "NotLockedCache"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
"""
def getCacheTTL(self):
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'dump-cache',
- '-']
+ rec_controlCmd = [os.environ["RECCONTROL"], "--config-dir=%s" % "configs/" + self._confdir, "dump-cache", "-"]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT, text=True)
for i in ret.splitlines():
- pieces = i.split(' ')
+ pieces = i.split(" ")
print(pieces)
- if pieces[0] == 'mx1.secure.example.' and pieces[4] == 'A':
+ if pieces[0] == "mx1.secure.example." and pieces[4] == "A":
return int(pieces[2])
return -1
raise
def testMX(self):
- expected1 = dns.rrset.from_text('secure.example.', 0, dns.rdataclass.IN, 'MX', '10 mx1.secure.example.', '20 mx2.secure.example.')
- expected2 = dns.rrset.from_text('sub.secure.example.', 0, dns.rdataclass.IN, 'MX', '10 mx1.secure.example.', '20 mx2.secure.example.')
- query1 = dns.message.make_query('secure.example', 'MX', want_dnssec=True)
+ expected1 = dns.rrset.from_text(
+ "secure.example.", 0, dns.rdataclass.IN, "MX", "10 mx1.secure.example.", "20 mx2.secure.example."
+ )
+ expected2 = dns.rrset.from_text(
+ "sub.secure.example.", 0, dns.rdataclass.IN, "MX", "10 mx1.secure.example.", "20 mx2.secure.example."
+ )
+ query1 = dns.message.make_query("secure.example", "MX", want_dnssec=True)
query1.flags |= dns.flags.AD
- query2 = dns.message.make_query('sub.secure.example', 'MX', want_dnssec=True)
+ query2 = dns.message.make_query("sub.secure.example", "MX", want_dnssec=True)
query2.flags |= dns.flags.AD
res = self.sendUDPQuery(query1)
from recursortests import RecursorTest
+
class GettagRecursorTest(RecursorTest):
- _confdir = 'GettagRecursor'
+ _confdir = "GettagRecursor"
_config_template = """
log-common-errors=yes
gettag-needs-edns-options=yes
"""
def testA(self):
- name = 'gettag.lua.'
+ name = "gettag.lua."
expected = [
- dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1'),
- dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [ name, 'gettag-qtype-1'])
- ]
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.1"),
+ dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, "TXT", [name, "gettag-qtype-1"]),
+ ]
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertResponseMatches(query, expected, res)
def testTCPA(self):
- name = 'gettag-tcpa.lua.'
+ name = "gettag-tcpa.lua."
expected = [
- dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1'),
- dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [ name, 'gettag-qtype-1', 'gettag-tcp'])
- ]
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.1"),
+ dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, "TXT", [name, "gettag-qtype-1", "gettag-tcp"]),
+ ]
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendTCPQuery(query)
self.assertResponseMatches(query, expected, res)
def testAAAA(self):
- name = 'gettag-aaaa.lua.'
+ name = "gettag-aaaa.lua."
expected = [
- dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'AAAA', '2001:db8::1'),
- dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [ name, 'gettag-qtype-28'])
- ]
- query = dns.message.make_query(name, 'AAAA', want_dnssec=True)
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "AAAA", "2001:db8::1"),
+ dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, "TXT", [name, "gettag-qtype-28"]),
+ ]
+ query = dns.message.make_query(name, "AAAA", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertResponseMatches(query, expected, res)
def testTcpAAAA(self):
- name = 'gettag-tcpaaaa.lua.'
+ name = "gettag-tcpaaaa.lua."
expected = [
- dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'AAAA', '2001:db8::1'),
- dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [ name, 'gettag-qtype-28', 'gettag-tcp'])
- ]
- query = dns.message.make_query(name, 'AAAA', want_dnssec=True)
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "AAAA", "2001:db8::1"),
+ dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, "TXT", [name, "gettag-qtype-28", "gettag-tcp"]),
+ ]
+ query = dns.message.make_query(name, "AAAA", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendTCPQuery(query)
self.assertResponseMatches(query, expected, res)
def testSubnet(self):
- name = 'gettag-subnet.lua.'
- subnet = '192.0.2.255'
+ name = "gettag-subnet.lua."
+ subnet = "192.0.2.255"
subnetMask = 32
ecso = clientsubnetoption.ClientSubnetOption(subnet, subnetMask)
expected = [
- dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1'),
- dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [name, 'gettag-qtype-1', 'edns-subnet-' + subnet + '/' + str(subnetMask),
- 'ednsoption-8-count-1', 'ednsoption-8-total-len-8']),
- ]
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[ecso])
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.1"),
+ dns.rrset.from_text_list(
+ name,
+ 0,
+ dns.rdataclass.IN,
+ "TXT",
+ [
+ name,
+ "gettag-qtype-1",
+ "edns-subnet-" + subnet + "/" + str(subnetMask),
+ "ednsoption-8-count-1",
+ "ednsoption-8-total-len-8",
+ ],
+ ),
+ ]
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[ecso])
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertResponseMatches(query, expected, res)
def testEDNSOptions(self):
- name = 'gettag-ednsoptions.lua.'
- subnet = '192.0.2.255'
+ name = "gettag-ednsoptions.lua."
+ subnet = "192.0.2.255"
subnetMask = 32
ecso = clientsubnetoption.ClientSubnetOption(subnet, subnetMask)
- eco1 = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- eco2 = cookiesoption.CookiesOption(b'deadc0de', b'deadc0de')
+ eco1 = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ eco2 = cookiesoption.CookiesOption(b"deadc0de", b"deadc0de")
expected = [
- dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1'),
- dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [name, 'gettag-qtype-1', 'edns-subnet-' + subnet + '/' + str(subnetMask),
- 'ednsoption-10-count-2', 'ednsoption-10-total-len-32',
- 'ednsoption-8-count-1', 'ednsoption-8-total-len-8'
- ]),
- ]
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[eco1,ecso,eco2])
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.1"),
+ dns.rrset.from_text_list(
+ name,
+ 0,
+ dns.rdataclass.IN,
+ "TXT",
+ [
+ name,
+ "gettag-qtype-1",
+ "edns-subnet-" + subnet + "/" + str(subnetMask),
+ "ednsoption-10-count-2",
+ "ednsoption-10-total-len-32",
+ "ednsoption-8-count-1",
+ "ednsoption-8-total-len-8",
+ ],
+ ),
+ ]
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[eco1, ecso, eco2])
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertResponseMatches(query, expected, res)
+
class GettagRecursorDistributesQueriesTest(GettagRecursorTest):
- _confdir = 'GettagRecursorDistributesQueries'
+ _confdir = "GettagRecursorDistributesQueries"
_config_template = """
log-common-errors=yes
gettag-needs-edns-options=yes
threads=2
"""
+
hooksReactorRunning = False
-class UDPHooksResponder(DatagramProtocol):
+class UDPHooksResponder(DatagramProtocol):
def datagramReceived(self, datagram, address):
request = dns.message.from_wire(datagram)
response = dns.message.make_response(request)
response.flags |= dns.flags.AA
- if request.question[0].name == dns.name.from_text('nxdomain.luahooks.example.'):
- soa = dns.rrset.from_text('luahooks.example.', 86400, dns.rdataclass.IN, 'SOA', 'ns.luahooks.example. hostmaster.luahooks.example. 1 3600 3600 3600 1')
+ if request.question[0].name == dns.name.from_text("nxdomain.luahooks.example."):
+ soa = dns.rrset.from_text(
+ "luahooks.example.",
+ 86400,
+ dns.rdataclass.IN,
+ "SOA",
+ "ns.luahooks.example. hostmaster.luahooks.example. 1 3600 3600 3600 1",
+ )
response.authority.append(soa)
response.set_rcode(dns.rcode.NXDOMAIN)
- elif request.question[0].name == dns.name.from_text('nodata.luahooks.example.'):
- soa = dns.rrset.from_text('luahooks.example.', 86400, dns.rdataclass.IN, 'SOA', 'ns.luahooks.example. hostmaster.luahooks.example. 1 3600 3600 3600 1')
+ elif request.question[0].name == dns.name.from_text("nodata.luahooks.example."):
+ soa = dns.rrset.from_text(
+ "luahooks.example.",
+ 86400,
+ dns.rdataclass.IN,
+ "SOA",
+ "ns.luahooks.example. hostmaster.luahooks.example. 1 3600 3600 3600 1",
+ )
response.authority.append(soa)
- elif request.question[0].name == dns.name.from_text('postresolve.luahooks.example.'):
- answer = dns.rrset.from_text('postresolve.luahooks.example.', 3600, dns.rdataclass.IN, 'A', '192.0.2.1')
+ elif request.question[0].name == dns.name.from_text("postresolve.luahooks.example."):
+ answer = dns.rrset.from_text("postresolve.luahooks.example.", 3600, dns.rdataclass.IN, "A", "192.0.2.1")
response.answer.append(answer)
- elif request.question[0].name == dns.name.from_text('preresolve.luahooks.example.'):
- answer = dns.rrset.from_text('preresolve.luahooks.example.', 3600, dns.rdataclass.IN, 'A', '192.0.2.2')
+ elif request.question[0].name == dns.name.from_text("preresolve.luahooks.example."):
+ answer = dns.rrset.from_text("preresolve.luahooks.example.", 3600, dns.rdataclass.IN, "A", "192.0.2.2")
response.answer.append(answer)
self.transport.write(response.to_wire(), address)
+
class LuaHooksRecursorTest(RecursorTest):
- _confdir = 'LuaHooksRecursor'
+ _confdir = "LuaHooksRecursor"
_config_template = """
forward-zones=luahooks.example=%s.23
log-common-errors=yes
quiet=no
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
_lua_dns_script_file = """
allowedips = newNMG()
return false
end
- """ % (os.environ['PREFIX'], os.environ['PREFIX'], os.environ['PREFIX'], os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"], os.environ["PREFIX"], os.environ["PREFIX"], os.environ["PREFIX"])
@classmethod
def startResponders(cls):
global hooksReactorRunning
print("Launching responders..")
- address = cls._PREFIX + '.23'
+ address = cls._PREFIX + ".23"
port = 53
if not hooksReactorRunning:
cls.startReactor()
def testNoData(self):
- expected = dns.rrset.from_text('nodata.luahooks.example.', 3600, dns.rdataclass.IN, 'AAAA', '2001:DB8::1')
- query = dns.message.make_query('nodata.luahooks.example.', 'AAAA', 'IN')
+ expected = dns.rrset.from_text("nodata.luahooks.example.", 3600, dns.rdataclass.IN, "AAAA", "2001:DB8::1")
+ query = dns.message.make_query("nodata.luahooks.example.", "AAAA", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertRRsetInAnswer(res, expected)
def testVanillaNXD(self):
- #expected = dns.rrset.from_text('nxdomain.luahooks.example.', 3600, dns.rdataclass.IN, 'A', '192.0.2.1')
- query = dns.message.make_query('nxdomain.luahooks.example.', 'AAAA', 'IN')
+ # expected = dns.rrset.from_text('nxdomain.luahooks.example.', 3600, dns.rdataclass.IN, 'A', '192.0.2.1')
+ query = dns.message.make_query("nxdomain.luahooks.example.", "AAAA", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
def testHookedNXD(self):
- expected = dns.rrset.from_text('nxdomain.luahooks.example.', 3600, dns.rdataclass.IN, 'A', '192.0.2.1')
- query = dns.message.make_query('nxdomain.luahooks.example.', 'A', 'IN')
+ expected = dns.rrset.from_text("nxdomain.luahooks.example.", 3600, dns.rdataclass.IN, "A", "192.0.2.1")
+ query = dns.message.make_query("nxdomain.luahooks.example.", "A", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertRRsetInAnswer(res, expected)
def testPostResolve(self):
- expected = dns.rrset.from_text('postresolve.luahooks.example.', 1, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query('postresolve.luahooks.example.', 'A', 'IN')
+ expected = dns.rrset.from_text("postresolve.luahooks.example.", 1, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query("postresolve.luahooks.example.", "A", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(res.answer[0].ttl, 1)
def testPreResolve(self):
- expected = dns.rrset.from_text('preresolve.luahooks.example.', 1, dns.rdataclass.IN, 'A', '192.0.2.2')
- query = dns.message.make_query('preresolve.luahooks.example.', 'A', 'IN')
+ expected = dns.rrset.from_text("preresolve.luahooks.example.", 1, dns.rdataclass.IN, "A", "192.0.2.2")
+ query = dns.message.make_query("preresolve.luahooks.example.", "A", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
self.assertRRsetInAnswer(res, expected)
def testIPFilterHeader(self):
- query = dns.message.make_query('ipfilter.luahooks.example.', 'A', 'IN')
+ query = dns.message.make_query("ipfilter.luahooks.example.", "A", "IN")
query.flags |= dns.flags.AD
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(res, None)
def testPreOutInterceptedQuery(self):
- query = dns.message.make_query('preout.luahooks.example.', 'A', 'IN')
+ query = dns.message.make_query("preout.luahooks.example.", "A", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
def testPreOutNotInterceptedQuery(self):
- query = dns.message.make_query('preout.luahooks.example.', 'AAAA', 'IN')
+ query = dns.message.make_query("preout.luahooks.example.", "AAAA", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testPreOutInterceptedToTCPQuery(self):
- query = dns.message.make_query('preout.luahooks.example.', 'TXT', 'IN')
+ query = dns.message.make_query("preout.luahooks.example.", "TXT", "IN")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
- self.assertRcodeEqual(res, dns.rcode.SERVFAIL) # crude, responder does not do TCP
+ self.assertRcodeEqual(res, dns.rcode.SERVFAIL) # crude, responder does not do TCP
+
class LuaHooksRecursorDistributesTest(LuaHooksRecursorTest):
- _confdir = 'LuaHooksRecursorDistributes'
+ _confdir = "LuaHooksRecursorDistributes"
_config_template = """
forward-zones=luahooks.example=%s.23
log-common-errors=yes
pdns-distributes-queries=yes
threads=2
quiet=no
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
+
class LuaDNS64Test(RecursorTest):
"""Tests the dq.followupAction("getFakeAAAARecords")"""
- _confdir = 'LuaDNS64'
+ _confdir = "LuaDNS64"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
"""
"""
def testAtoAAAA(self):
- expected = [
- dns.rrset.from_text('ns.secure.example.', 15, dns.rdataclass.IN, 'AAAA', '2001:db8:64::7f00:9')
- ]
- query = dns.message.make_query('ns.secure.example', 'AAAA')
+ expected = [dns.rrset.from_text("ns.secure.example.", 15, dns.rdataclass.IN, "AAAA", "2001:db8:64::7f00:9")]
+ query = dns.message.make_query("ns.secure.example", "AAAA")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
def testAtoCNAMEtoAAAA(self):
expected = [
- dns.rrset.from_text('cname-to-insecure.secure.example.', 3600, dns.rdataclass.IN, 'CNAME', 'node1.insecure.example.'),
- dns.rrset.from_text('node1.insecure.example.', 3600, dns.rdataclass.IN, 'AAAA', '2001:db8:64::c000:206')
+ dns.rrset.from_text(
+ "cname-to-insecure.secure.example.", 3600, dns.rdataclass.IN, "CNAME", "node1.insecure.example."
+ ),
+ dns.rrset.from_text("node1.insecure.example.", 3600, dns.rdataclass.IN, "AAAA", "2001:db8:64::c000:206"),
]
- query = dns.message.make_query('cname-to-insecure.secure.example.', 'AAAA')
+ query = dns.message.make_query("cname-to-insecure.secure.example.", "AAAA")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(len(res.authority), 0)
self.assertResponseMatches(query, expected, res)
+
class GettagFFIDNS64Test(RecursorTest):
"""Tests the interaction between gettag_ffi, RPZ and DNS64:
- - gettag_ffi will intercept the query and return a NODATA
- - the RPZ zone will match the name, but the only type is A
- - DNS64 should kick in, generating an AAAA
+ - gettag_ffi will intercept the query and return a NODATA
+ - the RPZ zone will match the name, but the only type is A
+ - DNS64 should kick in, generating an AAAA
"""
- _confdir = 'GettagFFIDNS64'
+ _confdir = "GettagFFIDNS64"
_config_template = """
dns64-prefix=64:ff9b::/96
"""
@classmethod
def generateRecursorConfig(cls, confdir):
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
dns64.test.powerdns.com.zone.rpz. 60 IN A 192.0.2.42
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(GettagFFIDNS64Test, cls).generateRecursorConfig(confdir)
def testAtoAAAA(self):
- expected = [
- dns.rrset.from_text('dns64.test.powerdns.com.', 15, dns.rdataclass.IN, 'AAAA', '64:ff9b::c000:22a')
- ]
- query = dns.message.make_query('dns64.test.powerdns.com.', 'AAAA')
+ expected = [dns.rrset.from_text("dns64.test.powerdns.com.", 15, dns.rdataclass.IN, "AAAA", "64:ff9b::c000:22a")]
+ query = dns.message.make_query("dns64.test.powerdns.com.", "AAAA")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(len(res.authority), 0)
self.assertResponseMatches(query, expected, res)
+
class PDNSRandomTest(RecursorTest):
"""Tests if pdnsrandom works"""
- _confdir = 'PDNSRandom'
+ _confdir = "PDNSRandom"
_config_template = """
"""
_lua_dns_script_file = """
"""
def testRandom(self):
- query = dns.message.make_query('whatever.example.', 'TXT')
+ query = dns.message.make_query("whatever.example.", "TXT")
ans = set()
class PDNSFeaturesTest(RecursorTest):
"""Tests if pdns_features works"""
- _confdir = 'PDNSFeatures'
+ _confdir = "PDNSFeatures"
_config_template = """
"""
_lua_dns_script_file = """
"""
def testFeatures(self):
- query = dns.message.make_query('whatever.example.', 'TXT')
+ query = dns.message.make_query("whatever.example.", "TXT")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
+
class PDNSGeneratingAnswerFromGettagTest(RecursorTest):
"""Tests that we can generate answers from gettag"""
- _confdir = 'PDNSGeneratingAnswerFromGettag'
+ _confdir = "PDNSGeneratingAnswerFromGettag"
_config_template = """
"""
_lua_dns_script_file = """
def testGettag(self):
expectedAnswerRecords = [
- dns.rrset.from_text('gettag-answers.powerdns.com.', 60, dns.rdataclass.IN, 'A', '192.0.2.1'),
+ dns.rrset.from_text("gettag-answers.powerdns.com.", 60, dns.rdataclass.IN, "A", "192.0.2.1"),
]
expectedAdditionalRecords = [
- dns.rrset.from_text('not-powerdns.com.', 60, dns.rdataclass.IN, 'A', '192.0.2.2'),
+ dns.rrset.from_text("not-powerdns.com.", 60, dns.rdataclass.IN, "A", "192.0.2.2"),
]
- query = dns.message.make_query('gettag-answers.powerdns.com.', 'A')
+ query = dns.message.make_query("gettag-answers.powerdns.com.", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(res.answer, expectedAnswerRecords)
self.assertEqual(res.additional, expectedAdditionalRecords)
+
class PDNSValidationStatesTest(RecursorTest):
"""Tests that we have access to the validation states from Lua"""
- _confdir = 'PDNSValidationStates'
+ _confdir = "PDNSValidationStates"
_config_template = """
dnssec=validate
"""
"""
def testValidationBogus(self):
- query = dns.message.make_query('brokendnssec.net.', 'A')
+ query = dns.message.make_query("brokendnssec.net.", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.assertEqual(len(res.answer), 0)
self.assertEqual(len(res.authority), 0)
+
class PolicyEventFilterOnFollowUpTest(RecursorTest):
- """Tests the interaction between RPZ and followup queries (dns64, followCNAME)
- """
+ """Tests the interaction between RPZ and followup queries (dns64, followCNAME)"""
- _confdir = 'PolicyEventFilterOnFollowUp'
+ _confdir = "PolicyEventFilterOnFollowUp"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
"""
@classmethod
def generateRecursorConfig(cls, confdir):
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
secure.example.zone.rpz. 60 IN A 192.0.2.42
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(PolicyEventFilterOnFollowUpTest, cls).generateRecursorConfig(confdir)
def testA(self):
expected = [
- dns.rrset.from_text('policyeventfilter-followup.test.powerdns.com.', 15, dns.rdataclass.IN, 'CNAME', 'secure.example.'),
- dns.rrset.from_text('secure.example.', 15, dns.rdataclass.IN, 'A', '192.0.2.17')
+ dns.rrset.from_text(
+ "policyeventfilter-followup.test.powerdns.com.", 15, dns.rdataclass.IN, "CNAME", "secure.example."
+ ),
+ dns.rrset.from_text("secure.example.", 15, dns.rdataclass.IN, "A", "192.0.2.17"),
]
- query = dns.message.make_query('policyeventfilter-followup.test.powerdns.com.', 'A')
+ query = dns.message.make_query("policyeventfilter-followup.test.powerdns.com.", "A")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(len(res.authority), 0)
self.assertResponseMatches(query, expected, res)
+
class PolicyEventFilterOnFollowUpWithNativeDNS64Test(RecursorTest):
- """Tests the interaction between followup queries and native dns64
- """
+ """Tests the interaction between followup queries and native dns64"""
- _confdir = 'PolicyEventFilterOnFollowUpWithNativeDNS64'
+ _confdir = "PolicyEventFilterOnFollowUpWithNativeDNS64"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
dns64-prefix=1234::/96
def testAAAA(self):
expected = [
- dns.rrset.from_text('mx1.secure.example.', 15, dns.rdataclass.IN, 'CNAME', 'cname.secure.example.'),
- dns.rrset.from_text('cname.secure.example.', 15, dns.rdataclass.IN, 'CNAME', ' host1.secure.example.'),
- dns.rrset.from_text('host1.secure.example.', 15, dns.rdataclass.IN, 'AAAA', '1234::c000:202')
+ dns.rrset.from_text("mx1.secure.example.", 15, dns.rdataclass.IN, "CNAME", "cname.secure.example."),
+ dns.rrset.from_text("cname.secure.example.", 15, dns.rdataclass.IN, "CNAME", " host1.secure.example."),
+ dns.rrset.from_text("host1.secure.example.", 15, dns.rdataclass.IN, "AAAA", "1234::c000:202"),
]
- query = dns.message.make_query('mx1.secure.example', 'AAAA')
+ query = dns.message.make_query("mx1.secure.example", "AAAA")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertEqual(len(res.authority), 0)
self.assertResponseMatches(query, expected, res)
+
class LuaPostResolveFFITest(RecursorTest):
"""Tests postresolve_ffi interface"""
- _confdir = 'LuaPostResolveFFI'
+ _confdir = "LuaPostResolveFFI"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
"""
"""
def testNOACTION(self):
- """ postresolve_ffi: test that we can do a NOACTION for a name and type combo"""
- query = dns.message.make_query('example', 'SOA')
+ """postresolve_ffi: test that we can do a NOACTION for a name and type combo"""
+ query = dns.message.make_query("example", "SOA")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(len(res.answer), 1)
def testDROP(self):
- """ postresolve_ffi: test that we can do a DROP for a name and type combo"""
- query = dns.message.make_query('example', 'TXT')
+ """postresolve_ffi: test that we can do a DROP for a name and type combo"""
+ query = dns.message.make_query("example", "TXT")
res = self.sendUDPQuery(query)
self.assertEqual(res, None)
def testNXDOMAIN(self):
- """ postresolve_ffi: test that we can return a NXDOMAIN for a name and type combo"""
- query = dns.message.make_query('ns1.example', 'A')
+ """postresolve_ffi: test that we can return a NXDOMAIN for a name and type combo"""
+ query = dns.message.make_query("ns1.example", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertEqual(len(res.answer), 0)
def testNODATA(self):
- """ postresolve_ffi: test that we can return a NODATA for a name and type combo"""
- query = dns.message.make_query('ns1.example', 'AAAA')
+ """postresolve_ffi: test that we can return a NODATA for a name and type combo"""
+ query = dns.message.make_query("ns1.example", "AAAA")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(len(res.answer), 0)
def testTRUNCATE(self):
- """ postresolve_ffi: test that we can return a truncated for a name and type combo"""
- query = dns.message.make_query('ns2.example', 'A')
+ """postresolve_ffi: test that we can return a truncated for a name and type combo"""
+ query = dns.message.make_query("ns2.example", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(len(res.answer), 0)
- self.assertMessageHasFlags(res, ['QR', 'TC', 'RD', 'RA'])
-
+ self.assertMessageHasFlags(res, ["QR", "TC", "RD", "RA"])
def testModifyA(self):
"""postresolve_ffi: test that we can modify A answers"""
expectedAnswerRecords = [
- dns.rrset.from_text('postresolve_ffi.example.', 60, dns.rdataclass.IN, 'A', '0.1.2.3', '1.2.3.5'),
+ dns.rrset.from_text("postresolve_ffi.example.", 60, dns.rdataclass.IN, "A", "0.1.2.3", "1.2.3.5"),
]
expectedAdditionalRecords = [
- dns.rrset.from_text('add.postresolve_ffi.example.', 60, dns.rdataclass.IN, 'A', '4.5.6.7'),
+ dns.rrset.from_text("add.postresolve_ffi.example.", 60, dns.rdataclass.IN, "A", "4.5.6.7"),
]
- query = dns.message.make_query('postresolve_ffi.example', 'A')
+ query = dns.message.make_query("postresolve_ffi.example", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(len(res.answer), 1)
def testModifyAAAA(self):
"""postresolve_ffi: test that we can modify AAAA answers"""
expectedAnswerRecords = [
- dns.rrset.from_text('postresolve_ffi.example.', 60, dns.rdataclass.IN, 'AAAA', '1:203:405:607:809:a0b:c0d:e0f', '::2'),
+ dns.rrset.from_text(
+ "postresolve_ffi.example.", 60, dns.rdataclass.IN, "AAAA", "1:203:405:607:809:a0b:c0d:e0f", "::2"
+ ),
]
- query = dns.message.make_query('postresolve_ffi.example', 'AAAA')
+ query = dns.message.make_query("postresolve_ffi.example", "AAAA")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(len(res.answer), 1)
malformedReactorRunning = False
+
class MalformedTest(RecursorTest):
- _confdir = 'Malformed'
+ _confdir = "Malformed"
_config_template = """
recursor:
forward_zones:
common_errors: true
outgoing:
dont_throttle_netmasks: ['127.0.0.27']
-""" % (os.environ['PREFIX'])
+""" % (os.environ["PREFIX"])
@classmethod
def generateRecursorConfig(cls, confdir):
cls.startResponders()
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
cls.createConfigDir(confdir)
cls.generateRecursorConfig(confdir)
global malformedReactorRunning
print("Launching responders..")
- address1 = cls._PREFIX + '.27'
+ address1 = cls._PREFIX + ".27"
port = 53
if not malformedReactorRunning:
cls.startReactor()
def getCache(self, name):
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'dump-cache',
- '-']
+ rec_controlCmd = [os.environ["RECCONTROL"], "--config-dir=%s" % "configs/" + self._confdir, "dump-cache", "-"]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT, text=True)
for i in ret.splitlines():
- pieces = i.split(' ')
- #print(pieces)
+ pieces = i.split(" ")
+ # print(pieces)
if pieces[0] == name:
return pieces
return []
def testOKAnswer(self):
# Case: rec gets a proper answer
- query = dns.message.make_query('proper.malformed.example.', 'A')
- expected = dns.rrset.from_text('proper.malformed.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ query = dns.message.make_query("proper.malformed.example.", "A")
+ expected = dns.rrset.from_text("proper.malformed.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
def testQR0Answer(self):
# Case: rec gets a QR=0 answer
- query = dns.message.make_query('qr0.malformed.example.', 'A')
+ query = dns.message.make_query("qr0.malformed.example.", "A")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
def testQR0PoisonAnswer(self):
# Case: rec gets a QR=0 answer
- query = dns.message.make_query('qr0poison.malformed.example.', 'A')
+ query = dns.message.make_query("qr0poison.malformed.example.", "A")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
def testHeaderOnlyAnswer(self):
# Case: rec gets a header-only answer
- query = dns.message.make_query('headeronly.malformed.example.', 'A')
+ query = dns.message.make_query("headeronly.malformed.example.", "A")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
-class UDPResponder(DatagramProtocol):
+class UDPResponder(DatagramProtocol):
def question(self, datagram, tcp=False):
request = dns.message.from_wire(datagram)
question = request.question[0]
# Case: send proper answer back
- if question.name == dns.name.from_text('proper.malformed.example.') and question.rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('proper.malformed.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ if question.name == dns.name.from_text("proper.malformed.example.") and question.rdtype == dns.rdatatype.A:
+ answer = dns.rrset.from_text("proper.malformed.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
response.answer.append(answer)
# Case: send qr=0 answer back
- elif question.name == dns.name.from_text('qr0.malformed.example.') and question.rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('qr0.malformed.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ elif question.name == dns.name.from_text("qr0.malformed.example.") and question.rdtype == dns.rdatatype.A:
+ answer = dns.rrset.from_text("qr0.malformed.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
response.answer.append(answer)
response.flags &= ~dns.flags.QR
# Case: send qr=0 poison answer back
- elif question.name == dns.name.from_text('qr0poison.malformed.example.') and question.rdtype == dns.rdatatype.A:
- answer = dns.rrset.from_text('www.poision.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ elif question.name == dns.name.from_text("qr0poison.malformed.example.") and question.rdtype == dns.rdatatype.A:
+ answer = dns.rrset.from_text("www.poision.example.", 15, dns.rdataclass.IN, "A", "127.0.0.1")
response.answer.append(answer)
response.flags &= ~dns.flags.QR
# Case: send header only back
- elif dns.name.from_text('headeronly.malformed.example.') and question.rdtype == dns.rdatatype.A:
+ elif dns.name.from_text("headeronly.malformed.example.") and question.rdtype == dns.rdatatype.A:
response.question = []
response.use_edns(False)
else:
response = self.question(datagram)
self.transport.write(response, address)
+
class TCPResponder(Protocol):
def dataReceived(self, data):
handler = UDPResponder()
response = handler.question(data[2:], True)
length = len(response)
- header = length.to_bytes(2, 'big')
+ header = length.to_bytes(2, "big")
self.transport.write(header + response)
+
class TCPFactory(Factory):
def buildProtocol(self, addr):
return TCPResponder()
import dns
from recursortests import RecursorTest
+
class NTATest(RecursorTest):
- _confdir = 'NTA'
+ _confdir = "NTA"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """dnssec=validate"""
"""Ensure a direct query to a bogus name with an NTA is Insecure"""
msg = dns.message.make_query("ted.bogus.example.", dns.rdatatype.A)
- msg.flags = dns.flags.from_text('AD RD')
- msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text('DO'))
+ msg.flags = dns.flags.from_text("AD RD")
+ msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text("DO"))
res = self.sendUDPQuery(msg)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testCNAMENTA(self):
"""Ensure a CNAME from a secure zone to a bogus one with an NTA is Insecure"""
msg = dns.message.make_query("cname-to-bogus.secure.example.", dns.rdatatype.A)
- msg.flags = dns.flags.from_text('AD RD')
- msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text('DO'))
+ msg.flags = dns.flags.from_text("AD RD")
+ msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text("DO"))
res = self.sendUDPQuery(msg)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
def testSecureWithNTAandDS(self):
"""#4391: when there is a TA *and* NTA configured for a name, the result must be insecure"""
msg = dns.message.make_query("node1.secure.optout.example.", dns.rdatatype.A)
- msg.flags = dns.flags.from_text('AD RD')
- msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text('DO'))
+ msg.flags = dns.flags.from_text("AD RD")
+ msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text("DO"))
res = self.sendUDPQuery(msg)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
import time
from recursortests import RecursorTest
+
class NamedForwardTest(RecursorTest):
"""
This is forwarding test using a name as target
"""
- _confdir = 'NamedForward'
+ _confdir = "NamedForward"
_config_template = """
dnssec=validate
forward-zones-recurse=.=dns.quad9.net;dns.google;one.one.one.one
"""
def testA(self):
- expected = dns.rrset.from_text('dns.google.', 0, dns.rdataclass.IN, 'A', '8.8.8.8', '8.8.4.4')
- query = dns.message.make_query('dns.google', 'A', want_dnssec=True)
+ expected = dns.rrset.from_text("dns.google.", 0, dns.rdataclass.IN, "A", "8.8.8.8", "8.8.4.4")
+ query = dns.message.make_query("dns.google", "A", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
self.assertMatchingRRSIGInAnswer(res, expected)
-@unittest.skipUnless('ENABLE_SUDO_TESTS' in os.environ, "sudo is not available")
+
+@unittest.skipUnless("ENABLE_SUDO_TESTS" in os.environ, "sudo is not available")
class NamedForwardWithChangeTest(RecursorTest):
"""
This is forwarding test using a name as target and a changing resolve
"""
- _confdir = 'NamedForwardWithChange'
+ _confdir = "NamedForwardWithChange"
_config_template = """
dnssec=off
forward-zones-recurse=.=namedforwardtest
@classmethod
def generateRecursorConfig(cls, confdir):
- subprocess.run(['sudo', 'sed', '-i', '-e', '/namedforwardtest/d', '/etc/hosts'])
- subprocess.run(['sudo', 'sh', '-c', 'echo ' + cls._PREFIX + '.10 namedforwardtest >> /etc/hosts'])
+ subprocess.run(["sudo", "sed", "-i", "-e", "/namedforwardtest/d", "/etc/hosts"])
+ subprocess.run(["sudo", "sh", "-c", "echo " + cls._PREFIX + ".10 namedforwardtest >> /etc/hosts"])
super(NamedForwardWithChangeTest, cls).generateRecursorConfig(confdir)
def testExampleNSQuery(self):
- query = dns.message.make_query('example', 'NS', want_dnssec=False)
+ query = dns.message.make_query("example", "NS", want_dnssec=False)
- expectedNS = dns.rrset.from_text('example.', 0, 'IN', 'NS', 'ns1.example.', 'ns2.example.')
+ expectedNS = dns.rrset.from_text("example.", 0, "IN", "NS", "ns1.example.", "ns2.example.")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expectedNS)
- subprocess.run(['sudo', 'sed', '-i', '-e', '/namedforwardtest/d', '/etc/hosts'])
- subprocess.run(['sudo', 'sh', '-c', 'echo ' + self._PREFIX + '.12 namedforwardtest >> /etc/hosts'])
+ subprocess.run(["sudo", "sed", "-i", "-e", "/namedforwardtest/d", "/etc/hosts"])
+ subprocess.run(["sudo", "sh", "-c", "echo " + self._PREFIX + ".12 namedforwardtest >> /etc/hosts"])
# the change should get picked up by the system resolver update thread and the reload flushes the caches
time.sleep(2)
res = self.sendUDPQuery(query)
- subprocess.run(['sudo', 'sed', '-i', '-e', '/namedforwardtest/d', '/etc/hosts'])
+ subprocess.run(["sudo", "sed", "-i", "-e", "/namedforwardtest/d", "/etc/hosts"])
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
class NoDSTest(RecursorTest):
- _confdir = 'NoDS'
+ _confdir = "NoDS"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """dnssec=validate"""
"""#4430 When the root DS is removed, the result must be Insecure"""
msg = dns.message.make_query("ted.bogus.example.", dns.rdatatype.A)
- msg.flags = dns.flags.from_text('AD RD')
- msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text('DO'))
+ msg.flags = dns.flags.from_text("AD RD")
+ msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text("DO"))
res = self.sendUDPQuery(msg)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
import dns
from recursortests import RecursorTest
+
class NoDSYAMLTest(RecursorTest):
- _confdir = 'NoDSYAML'
+ _confdir = "NoDSYAML"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
validation: validate
trustanchors: [{name: .}]
"""
+
@classmethod
def generateRecursorConfig(cls, confdir):
super(NoDSYAMLTest, cls).generateRecursorYamlConfig(confdir, False)
"""#4430 When the root DS is removed, the result must be Insecure"""
msg = dns.message.make_query("ted.bogus.example.", dns.rdatatype.A)
- msg.flags = dns.flags.from_text('AD RD')
- msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text('DO'))
+ msg.flags = dns.flags.from_text("AD RD")
+ msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text("DO"))
res = self.sendUDPQuery(msg)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], ["DO"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
set 15 days into the future. Hence, the recursor must reject the signatures
because they are not yet valid.
"""
- _confdir = 'NotYetValid'
+
+ _confdir = "NotYetValid"
_config_template = """dnssec=validate"""
- _auth_env = {'LD_PRELOAD':os.environ.get('LIBFAKETIME'),
- 'FAKETIME':'+15d'}
+ _auth_env = {"LD_PRELOAD": os.environ.get("LIBFAKETIME"), "FAKETIME": "+15d"}
@classmethod
def setUpClass(cls):
@classmethod
def tearDownClass(cls):
- confdir = os.path.join('configs', 'auths')
+ confdir = os.path.join("configs", "auths")
print("Specialized auth teardown " + confdir)
# tear down specialized auths, and then start standard ones
super().tearDownClass(True)
print("Starting default auths")
- #confdir = 'configs/auths'
+ # confdir = 'configs/auths'
shutil.rmtree(confdir, True)
os.mkdir(confdir)
# Be careful here, we don't want the overridden secureZone(), so call RecursorTest explicitly
RecursorTest.startAllAuth(confdir)
def testA(self):
- query = dns.message.make_query('host1.secure.example', 'A')
+ query = dns.message.make_query("host1.secure.example", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
from recursortests import RecursorTest
-class NotifyTest(RecursorTest):
- _confdir = 'Notify'
+class NotifyTest(RecursorTest):
+ _confdir = "Notify"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
packetcache:
disable: true
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
b 3600 IN A 192.0.2.42
d 3600 IN A 192.0.2.42
e 3600 IN A 192.0.2.42
f 3600 IN CNAME f ; CNAME loop: dirty trick to get a ServFail in an authzone
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(NotifyTest, cls).generateRecursorYamlConfig(confdir)
def checkRecordCacheMetrics(self, expectedHits, expectedMisses):
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/api/v1/servers/localhost/statistics"
r = requests.get(url, headers=headers, timeout=self._wsTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
foundHits = False
foundMisses = True
for entry in content:
- if entry['name'] == 'cache-hits':
+ if entry["name"] == "cache-hits":
foundHits = True
- self.assertEqual(int(entry['value']), expectedHits)
- elif entry['name'] == 'cache-misses':
+ self.assertEqual(int(entry["value"]), expectedHits)
+ elif entry["name"] == "cache-misses":
foundMisses = True
- self.assertEqual(int(entry['value']), expectedMisses)
+ self.assertEqual(int(entry["value"]), expectedMisses)
self.assertTrue(foundHits)
self.assertTrue(foundMisses)
def testNotify(self):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
# first query
- qname = 'a.example.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+ qname = "a.example."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.42")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertRRsetInAnswer(res, expected)
self.checkRecordCacheMetrics(3, 1)
- notify = dns.message.make_query('example', 'SOA', want_dnssec=False)
- notify.set_opcode(4) # notify
+ notify = dns.message.make_query("example", "SOA", want_dnssec=False)
+ notify.set_opcode(4) # notify
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(notify)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(res.opcode(), 4)
print(res)
- self.assertEqual(res.question[0].to_text(), 'example. IN SOA')
+ self.assertEqual(res.question[0].to_text(), "example. IN SOA")
self.checkRecordCacheMetrics(3, 1)
self.checkRecordCacheMetrics(4, 2)
-class NotifyNameNotAllowedTest(RecursorTest):
- _confdir = 'NotifyNameNotAllowed'
+class NotifyNameNotAllowedTest(RecursorTest):
+ _confdir = "NotifyNameNotAllowed"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
packetcache:
disable: true
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
b 3600 IN A 192.0.2.42
d 3600 IN A 192.0.2.42
e 3600 IN A 192.0.2.42
f 3600 IN CNAME f ; CNAME loop: dirty trick to get a ServFail in an authzone
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(NotifyNameNotAllowedTest, cls).generateRecursorYamlConfig(confdir)
def checkRecordCacheMetrics(self, expectedHits, expectedMisses):
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/api/v1/servers/localhost/statistics"
r = requests.get(url, headers=headers, timeout=self._wsTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
foundHits = False
foundMisses = True
for entry in content:
- if entry['name'] == 'cache-hits':
+ if entry["name"] == "cache-hits":
foundHits = True
- self.assertEqual(int(entry['value']), expectedHits)
- elif entry['name'] == 'cache-misses':
+ self.assertEqual(int(entry["value"]), expectedHits)
+ elif entry["name"] == "cache-misses":
foundMisses = True
- self.assertEqual(int(entry['value']), expectedMisses)
+ self.assertEqual(int(entry["value"]), expectedMisses)
self.assertTrue(foundHits)
self.assertTrue(foundMisses)
def testNotify(self):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
# first query
- qname = 'a.example.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+ qname = "a.example."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.42")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertRRsetInAnswer(res, expected)
self.checkRecordCacheMetrics(3, 1)
- notify = dns.message.make_query('example', 'SOA', want_dnssec=False)
- notify.set_opcode(4) # notify
+ notify = dns.message.make_query("example", "SOA", want_dnssec=False)
+ notify.set_opcode(4) # notify
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(notify)
- self.assertEqual(res, None);
+ self.assertEqual(res, None)
self.checkRecordCacheMetrics(3, 1)
self.checkRecordCacheMetrics(5, 1)
-class NotifyNetNotAllowedTest(RecursorTest):
- _confdir = 'NotifyNetNotAllowed'
+class NotifyNetNotAllowedTest(RecursorTest):
+ _confdir = "NotifyNetNotAllowed"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
packetcache:
disable: true
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
b 3600 IN A 192.0.2.42
d 3600 IN A 192.0.2.42
e 3600 IN A 192.0.2.42
f 3600 IN CNAME f ; CNAME loop: dirty trick to get a ServFail in an authzone
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(NotifyNetNotAllowedTest, cls).generateRecursorYamlConfig(confdir)
def checkRecordCacheMetrics(self, expectedHits, expectedMisses):
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/api/v1/servers/localhost/statistics"
r = requests.get(url, headers=headers, timeout=self._wsTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
foundHits = False
foundMisses = True
for entry in content:
- if entry['name'] == 'cache-hits':
+ if entry["name"] == "cache-hits":
foundHits = True
- self.assertEqual(int(entry['value']), expectedHits)
- elif entry['name'] == 'cache-misses':
+ self.assertEqual(int(entry["value"]), expectedHits)
+ elif entry["name"] == "cache-misses":
foundMisses = True
- self.assertEqual(int(entry['value']), expectedMisses)
+ self.assertEqual(int(entry["value"]), expectedMisses)
self.assertTrue(foundHits)
self.assertTrue(foundMisses)
def testNotify(self):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
# first query
- qname = 'a.example.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+ qname = "a.example."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.42")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertRRsetInAnswer(res, expected)
self.checkRecordCacheMetrics(3, 1)
- notify = dns.message.make_query('example', 'SOA', want_dnssec=False)
- notify.set_opcode(4) # notify
+ notify = dns.message.make_query("example", "SOA", want_dnssec=False)
+ notify.set_opcode(4) # notify
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(notify)
- self.assertEqual(res, None);
+ self.assertEqual(res, None)
self.checkRecordCacheMetrics(3, 1)
import time
from recursortests import RecursorTest
+
class OOOTCPTest(RecursorTest):
- _confdir = 'OOOTCP'
+ _confdir = "OOOTCP"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """dnssec=validate
def testOOOVeryBasic(self):
expected = {}
queries = []
- for zone in ['5.delay1.example.', '0.delay2.example.']:
- expected[zone] = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, 'TXT', 'a')
- query = dns.message.make_query(zone, 'TXT', want_dnssec=True)
+ for zone in ["5.delay1.example.", "0.delay2.example."]:
+ expected[zone] = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, "TXT", "a")
+ query = dns.message.make_query(zone, "TXT", want_dnssec=True)
query.flags |= dns.flags.AD
queries.append(query)
self.assertEqual(len(ress), len(expected))
i = 0
- for exp in [expected['0.delay2.example.'], expected['5.delay1.example.']]:
- print('ress0')
+ for exp in [expected["0.delay2.example."], expected["5.delay1.example."]]:
+ print("ress0")
print(ress[i].answer[0].to_text())
- print('exp')
+ print("exp")
print(exp.to_text())
self.assertMessageIsAuthenticated(ress[i])
self.assertRRsetInAnswer(ress[i], exp)
def testOOOTimeout(self):
queries = []
- for zone in ['25.delay1.example.', '1.delay2.example.']:
- query = dns.message.make_query(zone, 'TXT', want_dnssec=True)
+ for zone in ["25.delay1.example.", "1.delay2.example."]:
+ query = dns.message.make_query(zone, "TXT", want_dnssec=True)
query.flags |= dns.flags.AD
queries.append(query)
ress = self.sendTCPQueries(queries)
self.assertEqual(len(ress), 2)
- exp = dns.rrset.from_text('1.delay2.example.', 0, dns.rdataclass.IN, 'TXT', 'a')
+ exp = dns.rrset.from_text("1.delay2.example.", 0, dns.rdataclass.IN, "TXT", "a")
self.assertRRsetInAnswer(ress[0], exp)
self.assertMatchingRRSIGInAnswer(ress[0], exp)
self.assertRcodeEqual(ress[1], dns.rcode.SERVFAIL)
# Let the auth timeout happen to not disturb other tests
# this can happen if the auth is single-threaded
time.sleep(1)
-
from recursortests import RecursorTest
-class PacketCacheTest(RecursorTest):
- _confdir = 'PacketCache'
+class PacketCacheTest(RecursorTest):
+ _confdir = "PacketCache"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
packetcache-ttl=10
packetcache-negative-ttl=8
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
b 3600 IN A 192.0.2.42
d 3600 IN A 192.0.2.42
e 3600 IN A 192.0.2.42
f 3600 IN CNAME f ; CNAME loop: dirty trick to get a ServFail in an authzone
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(PacketCacheTest, cls).generateRecursorConfig(confdir)
def checkPacketCacheMetrics(self, expectedHits, expectedMisses):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/api/v1/servers/localhost/statistics"
r = requests.get(url, headers=headers, timeout=self._wsTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
foundHits = False
foundMisses = True
for entry in content:
- if entry['name'] == 'packetcache-hits':
+ if entry["name"] == "packetcache-hits":
foundHits = True
- self.assertEqual(int(entry['value']), expectedHits)
- elif entry['name'] == 'packetcache-misses':
+ self.assertEqual(int(entry["value"]), expectedHits)
+ elif entry["name"] == "packetcache-misses":
foundMisses = True
- self.assertEqual(int(entry['value']), expectedMisses)
+ self.assertEqual(int(entry["value"]), expectedMisses)
self.assertTrue(foundHits)
self.assertTrue(foundMisses)
def testPacketCache(self):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
# first query, no cookie
- qname = 'a.example.'
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+ qname = "a.example."
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.42")
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertRRsetInAnswer(res, expected)
self.checkPacketCacheMetrics(2, 2)
- eco1 = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
- eco2 = cookiesoption.CookiesOption(b'deadc0de', b'deadc0de')
- ecso1 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
+ eco1 = cookiesoption.CookiesOption(b"deadbeef", b"deadbeef")
+ eco2 = cookiesoption.CookiesOption(b"deadc0de", b"deadc0de")
+ ecso1 = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ ecso2 = clientsubnetoption.ClientSubnetOption("192.0.2.2", 32)
# we add a cookie, should not match anymore
- query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1])
+ query = dns.message.make_query(qname, "A", want_dnssec=True, options=[eco1])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
self.checkPacketCacheMetrics(2, 3)
# same cookie, should match
- query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1])
+ query = dns.message.make_query(qname, "A", want_dnssec=True, options=[eco1])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
self.checkPacketCacheMetrics(3, 3)
# different cookie, should still match
- query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco2])
+ query = dns.message.make_query(qname, "A", want_dnssec=True, options=[eco2])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
self.checkPacketCacheMetrics(4, 3)
# first cookie but with an ECS option, should not match
- query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1, ecso1])
+ query = dns.message.make_query(qname, "A", want_dnssec=True, options=[eco1, ecso1])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
self.checkPacketCacheMetrics(4, 4)
# different cookie but same ECS option, should match
- query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco2, ecso1])
+ query = dns.message.make_query(qname, "A", want_dnssec=True, options=[eco2, ecso1])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
# first cookie but different ECS option, should still match (we ignore EDNS Client Subnet
# in the recursor's packet cache, but ECS-specific responses are not cached
- query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1, ecso2])
+ query = dns.message.make_query(qname, "A", want_dnssec=True, options=[eco1, ecso2])
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
self.checkPacketCacheMetrics(6, 4)
# NXDomain should get negative packetcache TTL (8)
- query = dns.message.make_query('nxdomain.example.', 'A', want_dnssec=True)
+ query = dns.message.make_query("nxdomain.example.", "A", want_dnssec=True)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.checkPacketCacheMetrics(6, 5)
# NoData should get negative packetcache TTL (8)
- query = dns.message.make_query('a.example.', 'AAAA', want_dnssec=True)
+ query = dns.message.make_query("a.example.", "AAAA", want_dnssec=True)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.checkPacketCacheMetrics(6, 6)
# ServFail should get ServFail TTL (5)
- query = dns.message.make_query('f.example.', 'A', want_dnssec=True)
+ query = dns.message.make_query("f.example.", "A", want_dnssec=True)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
self.checkPacketCacheMetrics(6, 7)
# We peek into the cache to check TTLs and allow TTLs to be one lower than inserted since the clock might have ticked
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'dump-cache', '-']
+ rec_controlCmd = [os.environ["RECCONTROL"], "--config-dir=%s" % "configs/" + self._confdir, "dump-cache", "-"]
try:
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
self.assertTrue((b"a.example. 10 A ; tag 0 udp\n" in ret) or (b"a.example. 9 A ; tag 0 udp\n" in ret))
- self.assertTrue((b"nxdomain.example. 8 A ; tag 0 udp\n" in ret) or (b"nxdomain.example. 7 A ; tag 0 udp\n" in ret))
- self.assertTrue((b"a.example. 8 AAAA ; tag 0 udp\n" in ret) or (b"a.example. 7 AAAA ; tag 0 udp\n" in ret))
+ self.assertTrue(
+ (b"nxdomain.example. 8 A ; tag 0 udp\n" in ret) or (b"nxdomain.example. 7 A ; tag 0 udp\n" in ret)
+ )
+ self.assertTrue(
+ (b"a.example. 8 AAAA ; tag 0 udp\n" in ret) or (b"a.example. 7 AAAA ; tag 0 udp\n" in ret)
+ )
self.assertTrue((b"f.example. 5 A ; tag 0 udp\n" in ret) or (b"f.example. 4 A ; tag 0 udp\n" in ret))
except subprocess.CalledProcessError as e:
print(e.output)
raise
-
import pytest
from recursortests import RecursorTest
+
class RecPrometheusTest(RecursorTest):
-
def checkPrometheusContentBasic(self, content):
for line in content.splitlines():
- if line.startswith('# HELP'):
- tokens = line.split(' ')
+ if line.startswith("# HELP"):
+ tokens = line.split(" ")
self.assertGreaterEqual(len(tokens), 4)
- elif line.startswith('# TYPE'):
- tokens = line.split(' ')
+ elif line.startswith("# TYPE"):
+ tokens = line.split(" ")
self.assertEqual(len(tokens), 4)
- self.assertIn(tokens[3], ['counter', 'gauge', 'histogram'])
- elif not line.startswith('#'):
- tokens = line.split(' ')
+ self.assertIn(tokens[3], ["counter", "gauge", "histogram"])
+ elif not line.startswith("#"):
+ tokens = line.split(" ")
self.assertEqual(len(tokens), 2)
- if not line.startswith('pdns_recursor_'):
- raise AssertionError('Expecting prometheus metric to be prefixed by \'pdns_recursor_\', got: "%s"' % (line))
+ if not line.startswith("pdns_recursor_"):
+ raise AssertionError(
+ "Expecting prometheus metric to be prefixed by 'pdns_recursor_', got: \"%s\"" % (line)
+ )
def checkPrometheusContentPromtool(self, content):
output = None
try:
- testcmd = ['promtool', 'check', 'metrics']
- process = subprocess.Popen(testcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
+ testcmd = ["promtool", "check", "metrics"]
+ process = subprocess.Popen(
+ testcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True
+ )
output = process.communicate(input=content)
except subprocess.CalledProcessError as exc:
- raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, process.output))
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, process.output))
# promtool may return 3 because of the "_total" suffix warnings
if not process.returncode in [0, 3]:
- raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, output))
+ raise AssertionError("%s failed (%d): %s" % (testcmd, process.returncode, output))
for line in output[0].splitlines():
- if line.endswith(b"should have \"_total\" suffix"):
+ if line.endswith(b'should have "_total" suffix'):
continue
- raise AssertionError('%s returned an unexpected output. Faulty line is "%s", complete content is "%s"' % (testcmd, line, output))
+ raise AssertionError(
+ '%s returned an unexpected output. Faulty line is "%s", complete content is "%s"'
+ % (testcmd, line, output)
+ )
+
class BasicPrometheusTest(RecPrometheusTest):
- _confdir = 'BasicPrometheus'
+ _confdir = "BasicPrometheus"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_lua_dns_script_file = """
getMetric('metric_just_a_name')
def testPrometheus(self):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
- url = 'http://user:' + self._wsPassword + '@127.0.0.1:' + str(self._wsPort) + '/metrics'
+ url = "http://user:" + self._wsPassword + "@127.0.0.1:" + str(self._wsPort) + "/metrics"
r = requests.get(url, timeout=self._wsTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.checkPrometheusContentBasic(r.text)
self.checkPrometheusContentPromtool(r.content)
+
class HttpsPrometheusTest(RecPrometheusTest):
- _confdir = 'HttpsPrometheus'
+ _confdir = "HttpsPrometheus"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
webservice:
def testPrometheus(self):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
- url = 'https://user:' + self._wsPassword + '@127.0.0.1:' + str(self._wsPort) + '/metrics'
- r = requests.get(url, timeout=self._wsTimeout, verify='ca.pem')
+ url = "https://user:" + self._wsPassword + "@127.0.0.1:" + str(self._wsPort) + "/metrics"
+ r = requests.get(url, timeout=self._wsTimeout, verify="ca.pem")
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.checkPrometheusContentBasic(r.text)
self.checkPrometheusContentPromtool(r.content)
-@pytest.mark.skipif('pkcs12' not in RecursorTest.recFeatures(), reason='pkcs12 feature not available')
+
+@pytest.mark.skipif("pkcs12" not in RecursorTest.recFeatures(), reason="pkcs12 feature not available")
class HttpsPKCS12PrometheusTest(RecPrometheusTest):
- _confdir = 'HttpsPKCS12Prometheus'
+ _confdir = "HttpsPKCS12Prometheus"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
webservice:
def testPrometheus(self):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
- url = 'https://user:' + self._wsPassword + '@127.0.0.1:' + str(self._wsPort) + '/metrics'
- r = requests.get(url, timeout=self._wsTimeout, verify='ca.pem')
+ url = "https://user:" + self._wsPassword + "@127.0.0.1:" + str(self._wsPort) + "/metrics"
+ r = requests.get(url, timeout=self._wsTimeout, verify="ca.pem")
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.checkPrometheusContentBasic(r.text)
from recursortests import RecursorTest
+
def ProtobufConnectionHandler(queue, conn):
data = None
while True:
conn.close()
+
def ProtobufListener(queue, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
while True:
try:
(conn, _) = sock.accept()
- thread = threading.Thread(name='Connection Handler',
- target=ProtobufConnectionHandler,
- args=[queue, conn])
+ thread = threading.Thread(
+ name="Connection Handler", target=ProtobufConnectionHandler, args=[queue, conn]
+ )
thread.daemon = True
thread.start()
except socket.error as e:
- print('Error in protobuf socket: %s' % str(e))
+ print("Error in protobuf socket: %s" % str(e))
finally:
sock.close()
class ProtobufServerParams:
- def __init__(self, port):
- self.queue = Queue()
- self.port = port
+ def __init__(self, port):
+ self.queue = Queue()
+ self.port = port
+
protobufServersParameters = [ProtobufServerParams(4243), ProtobufServerParams(4244)]
protobufListeners = []
for param in protobufServersParameters:
- listener = threading.Thread(name='Protobuf Listener', target=ProtobufListener, args=[param.queue, param.port])
- listener.daemon = True
- listener.start()
- protobufListeners.append(listener)
+ listener = threading.Thread(name="Protobuf Listener", target=ProtobufListener, args=[param.queue, param.port])
+ listener.daemon = True
+ listener.start()
+ protobufListeners.append(listener)
-class TestRecursorProtobuf(RecursorTest):
+class TestRecursorProtobuf(RecursorTest):
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
def getFirstProtobufMessage(self, retries=10, waitTime=0.1):
msg = None
- #print("in getFirstProtobufMessage")
+ # print("in getFirstProtobufMessage")
for param in protobufServersParameters:
- failed = 0
-
- while param.queue.empty():
- if failed >= retries:
- break
- failed = failed + 1
- #print(str(failed) + '...')
- time.sleep(waitTime)
-
- #print(str(failed) + ' ' + str(param.queue.empty()))
- self.assertFalse(param.queue.empty())
- data = param.queue.get(False)
- self.assertTrue(data)
- oldmsg = msg
- msg = dnsmessage_pb2.PBDNSMessage()
- msg.ParseFromString(data)
- if oldmsg is not None:
- self.assertEqual(msg, oldmsg)
+ failed = 0
+
+ while param.queue.empty():
+ if failed >= retries:
+ break
+ failed = failed + 1
+ # print(str(failed) + '...')
+ time.sleep(waitTime)
+
+ # print(str(failed) + ' ' + str(param.queue.empty()))
+ self.assertFalse(param.queue.empty())
+ data = param.queue.get(False)
+ self.assertTrue(data)
+ oldmsg = msg
+ msg = dnsmessage_pb2.PBDNSMessage()
+ msg.ParseFromString(data)
+ if oldmsg is not None:
+ self.assertEqual(msg, oldmsg)
return msg
def emptyProtoBufQueue(self):
def checkNoRemainingMessage(self):
for param in protobufServersParameters:
- self.assertTrue(param.queue.empty())
+ self.assertTrue(param.queue.empty())
- def checkProtobufBase(self, msg, protocol, query, initiator, normalQueryResponse=True, expectedECS=None, receivedSize=None):
+ def checkProtobufBase(
+ self, msg, protocol, query, initiator, normalQueryResponse=True, expectedECS=None, receivedSize=None
+ ):
self.assertTrue(msg)
- self.assertTrue(msg.HasField('timeSec'))
- self.assertTrue(msg.HasField('socketFamily'))
+ self.assertTrue(msg.HasField("timeSec"))
+ self.assertTrue(msg.HasField("socketFamily"))
self.assertEqual(msg.socketFamily, dnsmessage_pb2.PBDNSMessage.INET)
- self.assertTrue(msg.HasField('from'))
- fromvalue = getattr(msg, 'from')
+ self.assertTrue(msg.HasField("from"))
+ fromvalue = getattr(msg, "from")
self.assertEqual(socket.inet_ntop(socket.AF_INET, fromvalue), initiator)
- self.assertTrue(msg.HasField('socketProtocol'))
+ self.assertTrue(msg.HasField("socketProtocol"))
self.assertEqual(msg.socketProtocol, protocol)
- self.assertTrue(msg.HasField('messageId'))
- self.assertTrue(msg.HasField('serverIdentity'))
- self.assertTrue(msg.HasField('id'))
+ self.assertTrue(msg.HasField("messageId"))
+ self.assertTrue(msg.HasField("serverIdentity"))
+ self.assertTrue(msg.HasField("id"))
self.assertEqual(msg.id, query.id)
- self.assertTrue(msg.HasField('inBytes'))
+ self.assertTrue(msg.HasField("inBytes"))
if normalQueryResponse:
# compare inBytes with length of query/response
# Note that for responses, the size we received might differ
else:
self.assertEqual(msg.inBytes, len(query.to_wire()))
if expectedECS is not None:
- self.assertTrue(msg.HasField('originalRequestorSubnet'))
+ self.assertTrue(msg.HasField("originalRequestorSubnet"))
# v4 only for now
self.assertEqual(len(msg.originalRequestorSubnet), 4)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.originalRequestorSubnet), '127.0.0.1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.originalRequestorSubnet), "127.0.0.1")
def checkOutgoingProtobufBase(self, msg, protocol, query, initiator, length=None, expectedECS=None):
self.assertTrue(msg)
- self.assertTrue(msg.HasField('timeSec'))
- self.assertTrue(msg.HasField('socketFamily'))
+ self.assertTrue(msg.HasField("timeSec"))
+ self.assertTrue(msg.HasField("socketFamily"))
self.assertEqual(msg.socketFamily, dnsmessage_pb2.PBDNSMessage.INET)
- self.assertTrue(msg.HasField('socketProtocol'))
+ self.assertTrue(msg.HasField("socketProtocol"))
self.assertEqual(msg.socketProtocol, protocol)
- self.assertTrue(msg.HasField('messageId'))
- self.assertTrue(msg.HasField('serverIdentity'))
- self.assertTrue(msg.HasField('id'))
+ self.assertTrue(msg.HasField("messageId"))
+ self.assertTrue(msg.HasField("serverIdentity"))
+ self.assertTrue(msg.HasField("id"))
self.assertNotEqual(msg.id, query.id)
- self.assertTrue(msg.HasField('inBytes'))
+ self.assertTrue(msg.HasField("inBytes"))
if length is not None:
- self.assertEqual(msg.inBytes, length)
+ self.assertEqual(msg.inBytes, length)
else:
- # compare inBytes with length of query/response
- self.assertEqual(msg.inBytes, len(query.to_wire()))
+ # compare inBytes with length of query/response
+ self.assertEqual(msg.inBytes, len(query.to_wire()))
if expectedECS is not None:
- self.assertTrue(msg.HasField('originalRequestorSubnet'))
+ self.assertTrue(msg.HasField("originalRequestorSubnet"))
# v4 only for now
self.assertEqual(len(msg.originalRequestorSubnet), 4)
self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.originalRequestorSubnet), expectedECS)
- def checkProtobufQuery(self, msg, protocol, query, qclass, qtype, qname, initiator='127.0.0.1', to='127.0.0.1'):
+ def checkProtobufQuery(self, msg, protocol, query, qclass, qtype, qname, initiator="127.0.0.1", to="127.0.0.1"):
self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSQueryType)
self.checkProtobufBase(msg, protocol, query, initiator)
# dnsdist doesn't fill the responder field for responses
# because it doesn't keep the information around.
- self.assertTrue(msg.HasField('to'))
+ self.assertTrue(msg.HasField("to"))
self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.to), to)
- self.assertTrue(msg.HasField('workerId'))
- self.assertTrue(msg.HasField('question'))
- self.assertTrue(msg.question.HasField('qClass'))
+ self.assertTrue(msg.HasField("workerId"))
+ self.assertTrue(msg.HasField("question"))
+ self.assertTrue(msg.question.HasField("qClass"))
self.assertEqual(msg.question.qClass, qclass)
- self.assertTrue(msg.question.HasField('qType'))
+ self.assertTrue(msg.question.HasField("qType"))
self.assertEqual(msg.question.qType, qtype)
- self.assertTrue(msg.question.HasField('qName'))
+ self.assertTrue(msg.question.HasField("qName"))
self.assertEqual(msg.question.qName, qname)
# This method takes wire format values to check
def checkProtobufHeaderFlagsAndEDNSVersion(self, msg, flags, ednsVersion):
- self.assertTrue(msg.HasField('headerFlags'))
+ self.assertTrue(msg.HasField("headerFlags"))
self.assertEqual(msg.headerFlags, socket.htons(flags))
- self.assertTrue(msg.HasField('ednsVersion'))
+ self.assertTrue(msg.HasField("ednsVersion"))
self.assertEqual(msg.ednsVersion, socket.htonl(ednsVersion))
- def checkProtobufResponse(self, msg, protocol, response, initiator='127.0.0.1', receivedSize=None, vstate=dnsmessage_pb2.PBDNSMessage.VState.Indeterminate):
+ def checkProtobufResponse(
+ self,
+ msg,
+ protocol,
+ response,
+ initiator="127.0.0.1",
+ receivedSize=None,
+ vstate=dnsmessage_pb2.PBDNSMessage.VState.Indeterminate,
+ ):
self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSResponseType)
self.checkProtobufBase(msg, protocol, response, initiator, receivedSize=receivedSize)
- self.assertTrue(msg.HasField('workerId'))
- self.assertTrue(msg.HasField('packetCacheHit'))
- self.assertTrue(msg.HasField('response'))
- self.assertTrue(msg.response.HasField('queryTimeSec'))
- self.assertTrue(msg.response.HasField('validationState'))
+ self.assertTrue(msg.HasField("workerId"))
+ self.assertTrue(msg.HasField("packetCacheHit"))
+ self.assertTrue(msg.HasField("response"))
+ self.assertTrue(msg.response.HasField("queryTimeSec"))
+ self.assertTrue(msg.response.HasField("validationState"))
self.assertEqual(msg.response.validationState, vstate)
def checkProtobufResponseRecord(self, record, rclass, rtype, rname, rttl, checkTTL=True):
- self.assertTrue(record.HasField('class'))
- self.assertEqual(getattr(record, 'class'), rclass)
- self.assertTrue(record.HasField('type'))
+ self.assertTrue(record.HasField("class"))
+ self.assertEqual(getattr(record, "class"), rclass)
+ self.assertTrue(record.HasField("type"))
self.assertEqual(record.type, rtype)
- self.assertTrue(record.HasField('name'))
+ self.assertTrue(record.HasField("name"))
self.assertEqual(record.name, rname)
- self.assertTrue(record.HasField('ttl'))
+ self.assertTrue(record.HasField("ttl"))
if checkTTL:
self.assertEqual(record.ttl, rttl)
- self.assertTrue(record.HasField('rdata'))
+ self.assertTrue(record.HasField("rdata"))
def checkProtobufPolicy(self, msg, policyType, reason, trigger, hit, kind):
self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSResponseType)
- self.assertTrue(msg.response.HasField('appliedPolicyType'))
- self.assertTrue(msg.response.HasField('appliedPolicy'))
- self.assertTrue(msg.response.HasField('appliedPolicyTrigger'))
- self.assertTrue(msg.response.HasField('appliedPolicyHit'))
- self.assertTrue(msg.response.HasField('appliedPolicyKind'))
+ self.assertTrue(msg.response.HasField("appliedPolicyType"))
+ self.assertTrue(msg.response.HasField("appliedPolicy"))
+ self.assertTrue(msg.response.HasField("appliedPolicyTrigger"))
+ self.assertTrue(msg.response.HasField("appliedPolicyHit"))
+ self.assertTrue(msg.response.HasField("appliedPolicyKind"))
self.assertEqual(msg.response.appliedPolicy, reason)
self.assertEqual(msg.response.appliedPolicyType, policyType)
self.assertEqual(msg.response.appliedPolicyTrigger, trigger)
self.assertEqual(msg.response.appliedPolicyKind, kind)
def checkProtobufTags(self, msg, tags):
- #print(tags)
- #print('---')
- #print(msg.response.tags)
+ # print(tags)
+ # print('---')
+ # print(msg.response.tags)
self.assertEqual(len(msg.response.tags), len(tags))
for tag in msg.response.tags:
self.assertIn(tag, tags)
def checkProtobufMetas(self, msg, metas):
- #print(metas)
- #print('---')
- #print(msg.meta)
+ # print(metas)
+ # print('---')
+ # print(msg.meta)
self.assertEqual(len(msg.meta), len(metas))
for m in msg.meta:
- self.assertTrue(m.HasField('key'))
- self.assertTrue(m.HasField('value'))
+ self.assertTrue(m.HasField("key"))
+ self.assertTrue(m.HasField("value"))
self.assertIn(m.key, metas)
- for i in m.value.intVal :
- self.assertIn(i, metas[m.key]['intVal'])
- for s in m.value.stringVal :
- self.assertIn(s, metas[m.key]['stringVal'])
-
- def checkProtobufOutgoingQuery(self, msg, protocol, query, qclass, qtype, qname, initiator='127.0.0.1', length=None, expectedECS=None):
+ for i in m.value.intVal:
+ self.assertIn(i, metas[m.key]["intVal"])
+ for s in m.value.stringVal:
+ self.assertIn(s, metas[m.key]["stringVal"])
+
+ def checkProtobufOutgoingQuery(
+ self, msg, protocol, query, qclass, qtype, qname, initiator="127.0.0.1", length=None, expectedECS=None
+ ):
self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSOutgoingQueryType)
self.checkOutgoingProtobufBase(msg, protocol, query, initiator, length=length, expectedECS=expectedECS)
- self.assertTrue(msg.HasField('to'))
- self.assertTrue(msg.HasField('question'))
- self.assertTrue(msg.question.HasField('qClass'))
+ self.assertTrue(msg.HasField("to"))
+ self.assertTrue(msg.HasField("question"))
+ self.assertTrue(msg.question.HasField("qClass"))
self.assertEqual(msg.question.qClass, qclass)
- self.assertTrue(msg.question.HasField('qType'))
+ self.assertTrue(msg.question.HasField("qType"))
self.assertEqual(msg.question.qType, qtype)
- self.assertTrue(msg.question.HasField('qName'))
+ self.assertTrue(msg.question.HasField("qName"))
self.assertEqual(msg.question.qName, qname)
- def checkProtobufIncomingResponse(self, msg, protocol, response, initiator='127.0.0.1', length=None):
+ def checkProtobufIncomingResponse(self, msg, protocol, response, initiator="127.0.0.1", length=None):
self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSIncomingResponseType)
self.checkOutgoingProtobufBase(msg, protocol, response, initiator, length=length)
- self.assertTrue(msg.HasField('response'))
- self.assertTrue(msg.response.HasField('rcode'))
- self.assertTrue(msg.response.HasField('queryTimeSec'))
+ self.assertTrue(msg.HasField("response"))
+ self.assertTrue(msg.response.HasField("rcode"))
+ self.assertTrue(msg.response.HasField("queryTimeSec"))
- def checkProtobufIncomingNetworkErrorResponse(self, msg, protocol, response, initiator='127.0.0.1'):
+ def checkProtobufIncomingNetworkErrorResponse(self, msg, protocol, response, initiator="127.0.0.1"):
self.checkProtobufIncomingResponse(msg, protocol, response, initiator, length=0)
self.assertEqual(msg.response.rcode, 65536)
def checkProtobufIdentity(self, msg, requestorId, deviceId, deviceName):
- #print(msg)
- self.assertEqual(requestorId == '', not msg.HasField('requestorId'))
- self.assertEqual(deviceId == b'', not msg.HasField('deviceId'))
- self.assertEqual(deviceName == '', not msg.HasField('deviceName'))
+ # print(msg)
+ self.assertEqual(requestorId == "", not msg.HasField("requestorId"))
+ self.assertEqual(deviceId == b"", not msg.HasField("deviceId"))
+ self.assertEqual(deviceName == "", not msg.HasField("deviceName"))
self.assertEqual(msg.requestorId, requestorId)
self.assertEqual(msg.deviceId, deviceId)
self.assertEqual(msg.deviceName, deviceName)
def checkProtobufEDE(self, msg, ede, edeText):
- #print(msg)
- self.assertEqual(ede == 0, not msg.HasField('ede'))
- self.assertEqual(edeText == '', not msg.HasField('edeText'))
+ # print(msg)
+ self.assertEqual(ede == 0, not msg.HasField("ede"))
+ self.assertEqual(edeText == "", not msg.HasField("edeText"))
self.assertEqual(msg.ede, ede)
self.assertEqual(msg.edeText, edeText)
- def getOpenTelemetryEDNS(self, traceid=b'0123456701234567', spanid=b'01234567', flags=b'\x00'):
- prefix = b'\x00\x00'
- opt = dns.edns.GenericOption(65500, prefix + traceid + spanid + flags)
- return opt
+ def getOpenTelemetryEDNS(self, traceid=b"0123456701234567", spanid=b"01234567", flags=b"\x00"):
+ prefix = b"\x00\x00"
+ opt = dns.edns.GenericOption(65500, prefix + traceid + spanid + flags)
+ return opt
def checkProtobufOT(self, msg, openTelemetryData, openTelemetryTraceID):
- self.assertEqual(openTelemetryData, msg.HasField('openTelemetryData'))
- self.assertEqual(openTelemetryTraceID, msg.HasField('openTelemetryTraceID'))
+ self.assertEqual(openTelemetryData, msg.HasField("openTelemetryData"))
+ self.assertEqual(openTelemetryTraceID, msg.HasField("openTelemetryTraceID"))
def setUp(self):
super(TestRecursorProtobuf, self).setUp()
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
tagged 3600 IN A 192.0.2.84
types 3600 IN SRV 10 20 443 a.example.
cname 3600 IN CNAME a.example.
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(TestRecursorProtobuf, cls).generateRecursorConfig(confdir)
@classmethod
def generateRecursorYamlConfig(cls, confdir, flag):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
aaaa 3600 IN AAAA 2001:DB8::2
types 3600 IN SRV 10 20 443 a.example.
cname 3600 IN CNAME a.example.
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(TestRecursorProtobuf, cls).generateRecursorYamlConfig(confdir, flag)
This test makes sure that we correctly export queries and response over protobuf.
"""
- _confdir = 'ProtobufDefault'
+ _confdir = "ProtobufDefault"
_config_template = """
recursor:
auth_zones:
expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, dnstype, content)
query = dns.message.make_query(name, dnstype, want_dnssec=True, options=edns)
query.flags |= dns.flags.CD
- for method in ('sendUDPQuery', 'sendTCPQuery'):
+ for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# check the protobuf messages corresponding to the query and answer
msg = self.getFirstProtobufMessage()
- if method == 'sendUDPQuery':
+ if method == "sendUDPQuery":
protocol = dnsmessage_pb2.PBDNSMessage.UDP
else:
protocol = dnsmessage_pb2.PBDNSMessage.TCP
self.checkProtobufHeaderFlagsAndEDNSVersion(msg, 0x0110, 0x00008000)
# then the response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, protocol, res, '127.0.0.1')
+ self.checkProtobufResponse(msg, protocol, res, "127.0.0.1")
if content is not None:
self.assertEqual(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
#
# again, for a PC cache hit
#
- for method in ('sendUDPQuery', 'sendTCPQuery'):
+ for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
# check the protobuf messages corresponding to the UDP query and answer
msg = self.getFirstProtobufMessage()
- if method == 'sendUDPQuery':
+ if method == "sendUDPQuery":
protocol = dnsmessage_pb2.PBDNSMessage.UDP
else:
protocol = dnsmessage_pb2.PBDNSMessage.TCP
self.checkProtobufHeaderFlagsAndEDNSVersion(msg, 0x0110, 0x00008000)
# then the response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, protocol, res, '127.0.0.1')
+ self.checkProtobufResponse(msg, protocol, res, "127.0.0.1")
if content is not None:
self.assertEqual(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
self.checkNoRemainingMessage()
def reloadConfig(self, config):
- confdir = os.path.join('configs', ProtobufDefaultTest._confdir)
- ProtobufDefaultTest._config_template = config
- ProtobufDefaultTest.generateRecursorYamlConfig(confdir, False)
- ProtobufDefaultTest.recControl(confdir, 'reload-yaml')
+ confdir = os.path.join("configs", ProtobufDefaultTest._confdir)
+ ProtobufDefaultTest._config_template = config
+ ProtobufDefaultTest.generateRecursorYamlConfig(confdir, False)
+ ProtobufDefaultTest.recControl(confdir, "reload-yaml")
config_default = """
recursor:
def testADefault(self):
self.reloadConfig(self.config_default)
- self.runtest('a.example.', dns.rdatatype.A, '192.0.2.42', True, True)
+ self.runtest("a.example.", dns.rdatatype.A, "192.0.2.42", True, True)
def testCNAMEDefault(self):
self.reloadConfig(self.config_default)
- name = 'cname.example.'
- expectedCNAME = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'CNAME', 'a.example.')
- expectedA = dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "cname.example."
+ expectedCNAME = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "CNAME", "a.example.")
+ expectedA = dns.rrset.from_text("a.example.", 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
raw = self.sendUDPQuery(query, decode=False)
res = dns.message.from_wire(raw)
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
# then the response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '127.0.0.1', receivedSize=len(raw))
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, "127.0.0.1", receivedSize=len(raw))
self.assertEqual(len(msg.response.rrs), 2)
rr = msg.response.rrs[0]
# we don't want to check the TTL for the A record, it has been cached by the previous test
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 15, checkTTL=False)
- self.assertEqual(rr.rdata, b'a.example.')
+ self.assertEqual(rr.rdata, b"a.example.")
rr = msg.response.rrs[1]
- self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, 'a.example.', 15, checkTTL=False)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, "a.example.", 15, checkTTL=False)
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
self.checkProtobufOT(msg, True, True)
self.checkNoRemainingMessage()
-
config_traceid_only = """
recursor:
auth_zones:
def testATraceIDOnly(self):
self.reloadConfig(self.config_traceid_only)
edns = self.getOpenTelemetryEDNS()
- self.runtest('a.example.', dns.rdatatype.A, '192.0.2.42', False, True, edns)
+ self.runtest("a.example.", dns.rdatatype.A, "192.0.2.42", False, True, edns)
def testCNAMETraceIDOnly(self):
self.reloadConfig(self.config_traceid_only)
- name = 'cname.example.'
- expectedCNAME = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'CNAME', 'a.example.')
- expectedA = dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+ name = "cname.example."
+ expectedCNAME = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "CNAME", "a.example.")
+ expectedA = dns.rrset.from_text("a.example.", 0, dns.rdataclass.IN, "A", "192.0.2.42")
edns = self.getOpenTelemetryEDNS()
- query = dns.message.make_query(name, 'A', want_dnssec=True, options=[edns])
+ query = dns.message.make_query(name, "A", want_dnssec=True, options=[edns])
query.flags |= dns.flags.CD
raw = self.sendUDPQuery(query, decode=False)
res = dns.message.from_wire(raw)
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
# then the response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '127.0.0.1', receivedSize=len(raw))
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, "127.0.0.1", receivedSize=len(raw))
self.assertEqual(len(msg.response.rrs), 2)
rr = msg.response.rrs[0]
# we don't want to check the TTL for the A record, it has been cached by the previous test
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 15, checkTTL=False)
- self.assertEqual(rr.rdata, b'a.example.')
+ self.assertEqual(rr.rdata, b"a.example.")
rr = msg.response.rrs[1]
- self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, 'a.example.', 15, checkTTL=False)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, "a.example.", 15, checkTTL=False)
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
self.checkProtobufOT(msg, False, True)
self.checkNoRemainingMessage()
def testAOTAOnly(self):
self.reloadConfig(self.config_otaonly)
- self.runtest('a.example.', dns.rdatatype.A, '192.0.2.42', True, True)
+ self.runtest("a.example.", dns.rdatatype.A, "192.0.2.42", True, True)
def testAAAAOTAOnly(self):
self.reloadConfig(self.config_otaonly)
edns = self.getOpenTelemetryEDNS()
- self.runtest('aaaa.example.', dns.rdatatype.AAAA, '2001:db8::2', False, True, edns, socket.AF_INET6)
+ self.runtest("aaaa.example.", dns.rdatatype.AAAA, "2001:db8::2", False, True, edns, socket.AF_INET6)
config_nameonly = """
recursor:
def testCorrectNameOnly(self):
self.reloadConfig(self.config_nameonly)
edns = self.getOpenTelemetryEDNS()
- self.runtest('a.example.', dns.rdatatype.A, '192.0.2.42', True, True, edns)
+ self.runtest("a.example.", dns.rdatatype.A, "192.0.2.42", True, True, edns)
def testOtherNameOnly(self):
self.reloadConfig(self.config_nameonly)
edns = self.getOpenTelemetryEDNS()
- self.runtest('aaaa.example.', dns.rdatatype.AAAA, '2001:db8::2', False, True, edns, socket.AF_INET6)
+ self.runtest("aaaa.example.", dns.rdatatype.AAAA, "2001:db8::2", False, True, edns, socket.AF_INET6)
config_nameandtypeonly = """
recursor:
def testCorrectNameAndTypeOnly(self):
self.reloadConfig(self.config_nameandtypeonly)
edns = self.getOpenTelemetryEDNS()
- self.runtest('aaaa.example.', dns.rdatatype.AAAA, '2001:db8::2', True, True, edns, socket.AF_INET6)
+ self.runtest("aaaa.example.", dns.rdatatype.AAAA, "2001:db8::2", True, True, edns, socket.AF_INET6)
def testCorrectNameWrongType(self):
self.reloadConfig(self.config_nameandtypeonly)
edns = self.getOpenTelemetryEDNS()
- self.runtest('aaaa.example.', dns.rdatatype.A, None, False, True, edns, None)
+ self.runtest("aaaa.example.", dns.rdatatype.A, None, False, True, edns, None)
config_nameandedns = """
recursor:
def testCorrectNameAndEDNSOnly(self):
self.reloadConfig(self.config_nameandedns)
edns = self.getOpenTelemetryEDNS()
- self.runtest('aaaa.example.', dns.rdatatype.AAAA, '2001:db8::2', True, True, edns, socket.AF_INET6)
+ self.runtest("aaaa.example.", dns.rdatatype.AAAA, "2001:db8::2", True, True, edns, socket.AF_INET6)
def testCorrectNameNoEDNS(self):
self.reloadConfig(self.config_nameandedns)
- self.runtest('aaaa.example.', dns.rdatatype.A, None, False, False, None, None)
+ self.runtest("aaaa.example.", dns.rdatatype.A, None, False, False, None, None)
config_nonetmatch = """
recursor:
def testNoNetMatch(self):
self.reloadConfig(self.config_nonetmatch)
- self.runtest('aaaa.example.', dns.rdatatype.A, None, False, False, None, None)
+ self.runtest("aaaa.example.", dns.rdatatype.A, None, False, False, None, None)
+
class ProtobufProxyMappingTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export queries and response over protobuf with a proxyMapping
"""
- _confdir = 'ProtobufProxyMapping'
- _config_template = """
+ _confdir = "ProtobufProxyMapping"
+ _config_template = (
+ """
auth-zones=example=configs/%s/example.zone
allow-from=3.4.5.0/24
devonly-regression-test-mode
- """ % _confdir
+ """
+ % _confdir
+ )
_lua_config_file = """
addProxyMapping("127.0.0.1/24", "3.4.5.6:99")
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
def testA(self):
- name = 'a.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "a.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
# then the response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '127.0.0.1')
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, "127.0.0.1")
self.assertEqual(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
self.checkNoRemainingMessage()
+
class ProtobufProxyMappingLogMappedTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export queries and response over protobuf.
"""
- _confdir = 'ProtobufProxyMappingLogMapped'
- _config_template = """
+ _confdir = "ProtobufProxyMappingLogMapped"
+ _config_template = (
+ """
auth-zones=example=configs/%s/example.zone
devonly-regression-test-mode
allow-from=3.4.5.0/0v
- """ % _confdir
+ """
+ % _confdir
+ )
_lua_config_file = """
addProxyMapping("127.0.0.1/24", "3.4.5.6:99")
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
def testA(self):
- name = 'a.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "a.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
# check the protobuf messages corresponding to the UDP query and answer
msg = self.getFirstProtobufMessage()
- self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '3.4.5.6')
+ self.checkProtobufQuery(
+ msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, "3.4.5.6"
+ )
# then the response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '3.4.5.6')
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, "3.4.5.6")
self.assertEqual(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
self.checkNoRemainingMessage()
+
class ProtobufProxyTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export addresses over protobuf when the proxy protocol is used.
"""
- _confdir = 'ProtobufProxy'
- _config_template = """
+ _confdir = "ProtobufProxy"
+ _config_template = (
+ """
auth-zones=example=configs/%s/example.zone
proxy-protocol-from=127.0.0.1/32
allow-from=127.0.0.1,6.6.6.6
devonly-regression-test-mode
-""" % _confdir
+"""
+ % _confdir
+ )
def testA(self):
- name = 'a.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "a.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
- res = self.sendUDPQueryWithProxyProtocol(query, False, '6.6.6.6', '7.7.7.7', 666, 777)
+ res = self.sendUDPQueryWithProxyProtocol(query, False, "6.6.6.6", "7.7.7.7", 666, 777)
self.assertRRsetInAnswer(res, expected)
# check the protobuf messages corresponding to the UDP query and answer
msg = self.getFirstProtobufMessage()
- self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '6.6.6.6', '7.7.7.7')
+ self.checkProtobufQuery(
+ msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, "6.6.6.6", "7.7.7.7"
+ )
# then the response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '6.6.6.6')
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, "6.6.6.6")
self.assertEqual(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
self.checkNoRemainingMessage()
+
class ProtobufProxyWithProxyByTableTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export addresses over protobuf when the proxy protocol and a proxy table mapping is used
"""
- _confdir = 'ProtobufProxyWithProxyByTable'
- _config_template = """
+ _confdir = "ProtobufProxyWithProxyByTable"
+ _config_template = (
+ """
auth-zones=example=configs/%s/example.zone
proxy-protocol-from=127.0.0.1/32
allow-from=3.4.5.6
devonly-regression-test-mode
-""" % _confdir
+"""
+ % _confdir
+ )
_lua_config_file = """
addProxyMapping("6.6.6.6/24", "3.4.5.6:99")
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
def testA(self):
- name = 'a.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "a.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
- res = self.sendUDPQueryWithProxyProtocol(query, False, '6.6.6.6', '7.7.7.7', 666, 777)
+ res = self.sendUDPQueryWithProxyProtocol(query, False, "6.6.6.6", "7.7.7.7", 666, 777)
self.assertRRsetInAnswer(res, expected)
# check the protobuf messages corresponding to the UDP query and answer
msg = self.getFirstProtobufMessage()
- self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '6.6.6.6', '7.7.7.7')
+ self.checkProtobufQuery(
+ msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, "6.6.6.6", "7.7.7.7"
+ )
# then the response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '6.6.6.6')
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, "6.6.6.6")
self.assertEqual(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
self.checkNoRemainingMessage()
+
class ProtobufProxyWithProxyByTableLogMappedTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export addresses over protobuf when the proxy protocol and a proxy table mapping is used
"""
- _confdir = 'ProtobufProxyWithProxyByTableLogMapped'
- _config_template = """
+ _confdir = "ProtobufProxyWithProxyByTableLogMapped"
+ _config_template = (
+ """
auth-zones=example=configs/%s/example.zone
proxy-protocol-from=127.0.0.1/32
allow-from=3.4.5.6
devonly-regression-test-mode
-""" % _confdir
+"""
+ % _confdir
+ )
_lua_config_file = """
addProxyMapping("6.6.6.6/24", "3.4.5.6:99")
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
def testA(self):
- name = 'a.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "a.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
- res = self.sendUDPQueryWithProxyProtocol(query, False, '6.6.6.6', '7.7.7.7', 666, 777)
+ res = self.sendUDPQueryWithProxyProtocol(query, False, "6.6.6.6", "7.7.7.7", 666, 777)
self.assertRRsetInAnswer(res, expected)
# check the protobuf messages corresponding to the UDP query and answer
msg = self.getFirstProtobufMessage()
- self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '3.4.5.6', '7.7.7.7')
+ self.checkProtobufQuery(
+ msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, "3.4.5.6", "7.7.7.7"
+ )
# then the response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '3.4.5.6')
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, "3.4.5.6")
self.assertEqual(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
self.checkNoRemainingMessage()
that the recursor at least connects to the protobuf server.
"""
- _confdir = 'OutgoingProtobufDefault'
+ _confdir = "OutgoingProtobufDefault"
_config_template = """
# Switch off QName Minimization, it generates much more protobuf messages
# (or make the test much more smart!)
def testA(self):
# There is a race in priming (having the . DNSKEY in cache in particular) and this code.
# So make sure we have the . DNSKEY in cache
- query = dns.message.make_query('.', 'A', want_dnssec=True)
+ query = dns.message.make_query(".", "A", want_dnssec=True)
query.flags |= dns.flags.RD
res = self.sendUDPQuery(query)
time.sleep(1)
self.emptyProtoBufQueue()
- name = 'host1.secure.example.'
+ name = "host1.secure.example."
expected = list()
for qname, qtype, proto, responseSize in [
- ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 248),
- ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 221),
- ('example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 219),
- ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 175),
- ('secure.example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 233),
+ ("host1.secure.example.", dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 248),
+ ("host1.secure.example.", dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 221),
+ ("example.", dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 219),
+ ("host1.secure.example.", dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 175),
+ ("secure.example.", dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 233),
]:
if not qname:
expected.append((None, None, None, None, None, None))
continue
query = dns.message.make_query(qname, qtype, use_edns=True, want_dnssec=True)
resp = dns.message.make_response(query)
- expected.append((
- qname, qtype, query, resp, proto, responseSize
- ))
+ expected.append((qname, qtype, query, resp, proto, responseSize))
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.RD
self.sendUDPQuery(query)
self.checkNoRemainingMessage()
+
class OutgoingProtobufWithECSMappingTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export outgoing queries over protobuf.
that the recursor at least connects to the protobuf server.
"""
- _confdir = 'OutgoingProtobufWithECSMapping'
+ _confdir = "OutgoingProtobufWithECSMapping"
_config_template = """
# Switch off QName Minimization, it generates much more protobuf messages
# (or make the test much more smart!)
def testA(self):
# There is a race in priming (having the . DNSKEY in cache in particular) and this code.
# So make sure we have the . DNSKEY in cache
- query = dns.message.make_query('.', 'A', want_dnssec=True)
+ query = dns.message.make_query(".", "A", want_dnssec=True)
query.flags |= dns.flags.RD
self.sendUDPQuery(query)
time.sleep(1)
self.emptyProtoBufQueue()
- name = 'host1.secure.example.'
+ name = "host1.secure.example."
expected = list()
for qname, qtype, proto, responseSize, ecs in [
- ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 248, "1.2.3.0"),
- ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 221, "1.2.3.0"),
- ('example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 219, "1.2.3.0"),
- ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 175, "1.2.3.0"),
- ('secure.example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 233, "1.2.3.0"),
+ ("host1.secure.example.", dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 248, "1.2.3.0"),
+ ("host1.secure.example.", dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 221, "1.2.3.0"),
+ ("example.", dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 219, "1.2.3.0"),
+ ("host1.secure.example.", dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 175, "1.2.3.0"),
+ ("secure.example.", dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 233, "1.2.3.0"),
]:
if not qname:
expected.append((None, None, None, None, None, None, None))
continue
- ecso = clientsubnetoption.ClientSubnetOption('9.10.11.12', 24)
+ ecso = clientsubnetoption.ClientSubnetOption("9.10.11.12", 24)
query = dns.message.make_query(qname, qtype, use_edns=True, want_dnssec=True, options=[ecso], payload=512)
resp = dns.message.make_response(query)
- expected.append((
- qname, qtype, query, resp, proto, responseSize, ecs
- ))
+ expected.append((qname, qtype, query, resp, proto, responseSize, ecs))
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.RD
res = self.sendUDPQuery(query)
self.checkNoRemainingMessage()
# this query should use the unmapped ECS
- name = 'mx1.secure.example.'
+ name = "mx1.secure.example."
expected = list()
for qname, qtype, proto, responseSize, ecs in [
- ('mx1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 173, "127.0.0.1"),
+ ("mx1.secure.example.", dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 173, "127.0.0.1"),
]:
if not qname:
expected.append((None, None, None, None, None, None, None))
continue
- ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 32)
+ ecso = clientsubnetoption.ClientSubnetOption("127.0.0.1", 32)
query = dns.message.make_query(qname, qtype, use_edns=True, want_dnssec=True, options=[ecso], payload=512)
resp = dns.message.make_response(query)
- expected.append((
- qname, qtype, query, resp, proto, responseSize, ecs
- ))
+ expected.append((qname, qtype, query, resp, proto, responseSize, ecs))
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.RD
self.sendUDPQuery(query)
self.checkNoRemainingMessage()
+
class OutgoingProtobufNoQueriesTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export incoming responses but not outgoing queries over protobuf.
that the recursor at least connects to the protobuf server.
"""
- _confdir = 'OutgoingProtobufNoQueries'
+ _confdir = "OutgoingProtobufNoQueries"
_config_template = """
# Switch off QName Minimization, it generates much more protobuf messages
# (or make the test much more smart!)
def testA(self):
# There is a race in priming (having the . DNSKEY in cache in particular) and this code.
# So make sure we have the . DNSKEY in cache
- query = dns.message.make_query('.', 'A', want_dnssec=True)
+ query = dns.message.make_query(".", "A", want_dnssec=True)
query.flags |= dns.flags.RD
res = self.sendUDPQuery(query)
time.sleep(1)
self.emptyProtoBufQueue()
- name = 'host1.secure.example.'
+ name = "host1.secure.example."
expected = list()
# the root DNSKEY has been learned with priming the root NS already
# ('.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 201),
for qname, qtype, proto, size in [
- ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 248),
- ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 221),
- ('example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 219),
- ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 175),
- ('secure.example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 233),
+ ("host1.secure.example.", dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 248),
+ ("host1.secure.example.", dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 221),
+ ("example.", dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 219),
+ ("host1.secure.example.", dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 175),
+ ("secure.example.", dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 233),
]:
if not qname:
expected.append((None, None, None, None, None, None))
continue
query = dns.message.make_query(qname, qtype, use_edns=True, want_dnssec=True)
resp = dns.message.make_response(query)
- expected.append((
- qname, qtype, query, resp, proto, size
- ))
+ expected.append((qname, qtype, query, resp, proto, size))
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.RD
self.sendUDPQuery(query)
self.checkNoRemainingMessage()
+
class ProtobufMasksTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export queries and response over protobuf, respecting the configured initiator masking.
"""
- _confdir = 'ProtobufMasks'
- _config_template = """
+ _confdir = "ProtobufMasks"
+ _config_template = (
+ """
devonly-regression-test-mode
-auth-zones=example=configs/%s/example.zone""" % _confdir
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_protobufMaskV4 = 4
_protobufMaskV6 = 128
_lua_config_file = """
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port, _protobufMaskV4, _protobufMaskV6)
def testA(self):
- name = 'a.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "a.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
# check the protobuf messages corresponding to the UDP query and answer
# but first let the protobuf messages the time to get there
msg = self.getFirstProtobufMessage()
- self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '112.0.0.0')
+ self.checkProtobufQuery(
+ msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, "112.0.0.0"
+ )
# then the response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '112.0.0.0')
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, "112.0.0.0")
self.assertEqual(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
self.checkNoRemainingMessage()
+
class ProtobufQueriesOnlyTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export queries but not responses over protobuf.
"""
- _confdir = 'ProtobufQueriesOnly'
- _config_template = """
+ _confdir = "ProtobufQueriesOnly"
+ _config_template = (
+ """
devonly-regression-test-mode
-auth-zones=example=configs/%s/example.zone""" % _confdir
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=false } )
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
def testA(self):
- name = 'a.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "a.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
# no response
self.checkNoRemainingMessage()
+
class ProtobufResponsesOnlyTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export responses but not queries over protobuf.
"""
- _confdir = 'ProtobufResponsesOnly'
- _config_template = """
-auth-zones=example=configs/%s/example.zone""" % _confdir
+ _confdir = "ProtobufResponsesOnly"
+ _config_template = (
+ """
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=true } )
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
def testA(self):
- name = 'a.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "a.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
# nothing else in the queue
self.checkNoRemainingMessage()
+
class ProtobufTaggedOnlyTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export queries and responses but only if they have been tagged.
"""
- _confdir = 'ProtobufTaggedOnly'
- _config_template = """
+ _confdir = "ProtobufTaggedOnly"
+ _config_template = (
+ """
devonly-regression-test-mode
-auth-zones=example=configs/%s/example.zone""" % _confdir
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true, taggedOnly=true } )
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
- _tags = ['tag1', 'tag2']
- _tag_from_gettag = 'tag-from-gettag'
+ _tags = ["tag1", "tag2"]
+ _tag_from_gettag = "tag-from-gettag"
_lua_dns_script_file = """
function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
if qname:equal('tagged.example.') then
""" % (_tag_from_gettag, _tags[0], _tags[1])
def testA(self):
- name = 'a.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "a.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
for method in ("sendUDPQuery", "sendTCPQuery"):
- sender = getattr(self, method)
- res = sender(query)
- self.assertRRsetInAnswer(res, expected)
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRRsetInAnswer(res, expected)
- # check the protobuf message corresponding to the UDP response
- # the first query and answer are not tagged, so there is nothing in the queue
- #time.sleep(1)
+ # check the protobuf message corresponding to the UDP response
+ # the first query and answer are not tagged, so there is nothing in the queue
+ # time.sleep(1)
- self.checkNoRemainingMessage()
- # Again to check PC case
- sender(query)
- #time.sleep(1)
- self.checkNoRemainingMessage()
+ self.checkNoRemainingMessage()
+ # Again to check PC case
+ sender(query)
+ # time.sleep(1)
+ self.checkNoRemainingMessage()
def testTagged(self):
- name = 'tagged.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "tagged.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.84")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
first = True
for method in ("sendUDPQuery", "sendTCPQuery"):
- messagetype = dnsmessage_pb2.PBDNSMessage.UDP
- if not first:
- messagetype = dnsmessage_pb2.PBDNSMessage.TCP
- sender = getattr(self, method)
- res = sender(query)
- self.assertRRsetInAnswer(res, expected)
-
- # check the protobuf messages corresponding to the query and answer
- msg = self.getFirstProtobufMessage()
- self.checkProtobufQuery(msg, messagetype, query, dns.rdataclass.IN, dns.rdatatype.A, name)
- self.checkProtobufTags(msg, [ self._tag_from_gettag ])
- # then the response
- msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, messagetype, res)
- self.assertEqual(len(msg.response.rrs), 1)
- rr = msg.response.rrs[0]
- # we have max-cache-ttl set to 15, but only check it first iteration
- self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15, checkTTL=first)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
- tags = [ self._tag_from_gettag ] + self._tags
- self.checkProtobufTags(msg, tags)
- self.checkNoRemainingMessage()
-
- # Again to check PC case
- res = sender(query)
- self.assertRRsetInAnswer(res, expected)
-
- # check the protobuf messages corresponding to the query and answer
- msg = self.getFirstProtobufMessage()
- self.checkProtobufQuery(msg, messagetype, query, dns.rdataclass.IN, dns.rdatatype.A, name)
- self.checkProtobufTags(msg, [ self._tag_from_gettag ])
- # then the response
- msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, messagetype, res)
- self.assertEqual(len(msg.response.rrs), 1)
- rr = msg.response.rrs[0]
- # time may have passed, so do not check TTL
- self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15, checkTTL=False)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
- tags = [ self._tag_from_gettag ] + self._tags
- self.checkProtobufTags(msg, tags)
- self.checkNoRemainingMessage()
- first = False
+ messagetype = dnsmessage_pb2.PBDNSMessage.UDP
+ if not first:
+ messagetype = dnsmessage_pb2.PBDNSMessage.TCP
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRRsetInAnswer(res, expected)
+
+ # check the protobuf messages corresponding to the query and answer
+ msg = self.getFirstProtobufMessage()
+ self.checkProtobufQuery(msg, messagetype, query, dns.rdataclass.IN, dns.rdatatype.A, name)
+ self.checkProtobufTags(msg, [self._tag_from_gettag])
+ # then the response
+ msg = self.getFirstProtobufMessage()
+ self.checkProtobufResponse(msg, messagetype, res)
+ self.assertEqual(len(msg.response.rrs), 1)
+ rr = msg.response.rrs[0]
+ # we have max-cache-ttl set to 15, but only check it first iteration
+ self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15, checkTTL=first)
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.84")
+ tags = [self._tag_from_gettag] + self._tags
+ self.checkProtobufTags(msg, tags)
+ self.checkNoRemainingMessage()
+
+ # Again to check PC case
+ res = sender(query)
+ self.assertRRsetInAnswer(res, expected)
+
+ # check the protobuf messages corresponding to the query and answer
+ msg = self.getFirstProtobufMessage()
+ self.checkProtobufQuery(msg, messagetype, query, dns.rdataclass.IN, dns.rdatatype.A, name)
+ self.checkProtobufTags(msg, [self._tag_from_gettag])
+ # then the response
+ msg = self.getFirstProtobufMessage()
+ self.checkProtobufResponse(msg, messagetype, res)
+ self.assertEqual(len(msg.response.rrs), 1)
+ rr = msg.response.rrs[0]
+ # time may have passed, so do not check TTL
+ self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15, checkTTL=False)
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.84")
+ tags = [self._tag_from_gettag] + self._tags
+ self.checkProtobufTags(msg, tags)
+ self.checkNoRemainingMessage()
+ first = False
+
class ProtobufTagCacheBase(TestRecursorProtobuf):
__test__ = False
def testTagged(self):
- name = 'tagged.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "tagged.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.84")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.84")
self.checkNoRemainingMessage()
self.assertEqual(len(msg.response.tags), 1)
ts1 = msg.response.tags[0]
rr = msg.response.rrs[0]
# time may have passed, so do not check TTL
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15, checkTTL=False)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.84")
self.checkNoRemainingMessage()
self.assertEqual(len(msg.response.tags), 1)
ts2 = msg.response.tags[0]
self.assertNotEqual(ts1, ts2)
def testTaggedTCP(self):
- name = 'taggedtcp.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.87')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "taggedtcp.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.87")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendTCPQuery(query)
self.assertRRsetInAnswer(res, expected)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.87')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.87")
self.checkNoRemainingMessage()
self.assertEqual(len(msg.response.tags), 1)
ts1 = msg.response.tags[0]
rr = msg.response.rrs[0]
# time may have passed, so do not check TTL
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15, checkTTL=False)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.87')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.87")
self.checkNoRemainingMessage()
self.assertEqual(len(msg.response.tags), 1)
ts2 = msg.response.tags[0]
self.assertNotEqual(ts1, ts2)
+
class ProtobufTagCacheTest(ProtobufTagCacheBase):
"""
This test makes sure that we correctly cache tags (actually not cache them)
"""
__test__ = True
- _confdir = 'ProtobufTagCache'
- _config_template = """
-auth-zones=example=configs/%s/example.zone""" % _confdir
+ _confdir = "ProtobufTagCache"
+ _config_template = (
+ """
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=true } )
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
end
"""
+
class ProtobufTagCacheFFITest(ProtobufTagCacheBase):
"""
This test makes sure that we correctly cache tags (actually not cache them) for the FFI case
"""
__test__ = True
- _confdir = 'ProtobufTagCacheFFI'
- _config_template = """
+ _confdir = "ProtobufTagCacheFFI"
+ _config_template = (
+ """
devonly-regression-test-mode
-auth-zones=example=configs/%s/example.zone""" % _confdir
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=true } )
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
end
"""
+
class ProtobufSelectedFromLuaTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export queries and responses but only if they have been selected from Lua.
"""
- _confdir = 'ProtobufSelectedFromLua'
- _config_template = """
-auth-zones=example=configs/%s/example.zone""" % _confdir
+ _confdir = "ProtobufSelectedFromLua"
+ _config_template = (
+ """
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=false } )
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
"""
def testA(self):
- name = 'a.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "a.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
self.checkNoRemainingMessage()
def testQuerySelected(self):
- name = 'query-selected.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "query-selected.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.84")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
self.checkNoRemainingMessage()
def testResponseSelected(self):
- name = 'answer-selected.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "answer-selected.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.84")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.84")
self.checkNoRemainingMessage()
+
class ProtobufExportTypesTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export other types than A, AAAA and CNAME over protobuf.
"""
- _confdir = 'ProtobufExportTypes'
- _config_template = """
+ _confdir = "ProtobufExportTypes"
+ _config_template = (
+ """
devonly-regression-test-mode
-auth-zones=example=configs/%s/example.zone""" % _confdir
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { exportTypes={"AAAA", "MX", "SPF", "SRV", "TXT"} } )
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
def testA(self):
- name = 'types.example.'
- expected = [dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84'),
- dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'AAAA', '2001:DB8::1'),
- dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'MX', '10 a.example.'),
- dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'SPF', '"v=spf1 -all"'),
- dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'SRV', '10 20 443 a.example.'),
- dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', '"Lorem ipsum dolor sit amet"'),
- ]
- query = dns.message.make_query(name, 'ANY', want_dnssec=True)
+ name = "types.example."
+ expected = [
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.84"),
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "AAAA", "2001:DB8::1"),
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "MX", "10 a.example."),
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "SPF", '"v=spf1 -all"'),
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "SRV", "10 20 443 a.example."),
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, "TXT", '"Lorem ipsum dolor sit amet"'),
+ ]
+ query = dns.message.make_query(name, "ANY", want_dnssec=True)
query.flags |= dns.flags.CD
raw1 = self.sendUDPQuery(query, decode=False)
res = dns.message.from_wire(raw1)
- self.assertMessageHasFlags(res, ['QR', 'TC', 'RD', 'RA', 'CD'], ['DO'])
+ self.assertMessageHasFlags(res, ["QR", "TC", "RD", "RA", "CD"], ["DO"])
raw2 = self.sendTCPQuery(query, decode=False)
res = dns.message.from_wire(raw2)
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.ANY, name)
# then TC response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '127.0.0.1', receivedSize=len(raw1))
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, "127.0.0.1", receivedSize=len(raw1))
self.assertEqual(len(msg.response.rrs), 0)
# check the protobuf messages corresponding to the TCP query and answer
msg = self.getFirstProtobufMessage()
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.TCP, query, dns.rdataclass.IN, dns.rdatatype.ANY, name)
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, res, '127.0.0.1', receivedSize=len(raw2))
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, res, "127.0.0.1", receivedSize=len(raw2))
self.assertEqual(len(msg.response.rrs), 5)
for rr in msg.response.rrs:
- self.assertIn(rr.type, [dns.rdatatype.AAAA, dns.rdatatype.TXT, dns.rdatatype.MX, dns.rdatatype.SPF, dns.rdatatype.SRV])
+ self.assertIn(
+ rr.type, [dns.rdatatype.AAAA, dns.rdatatype.TXT, dns.rdatatype.MX, dns.rdatatype.SPF, dns.rdatatype.SRV]
+ )
if rr.type == dns.rdatatype.AAAA:
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.AAAA, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET6, rr.rdata), '2001:db8::1')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET6, rr.rdata), "2001:db8::1")
elif rr.type == dns.rdatatype.TXT:
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.TXT, name, 15)
self.assertEqual(rr.rdata, b'"Lorem ipsum dolor sit amet"')
elif rr.type == dns.rdatatype.MX:
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.MX, name, 15)
- self.assertEqual(rr.rdata, b'a.example.')
+ self.assertEqual(rr.rdata, b"a.example.")
elif rr.type == dns.rdatatype.SPF:
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.SPF, name, 15)
self.assertEqual(rr.rdata, b'"v=spf1 -all"')
elif rr.type == dns.rdatatype.SRV:
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.SRV, name, 15)
- self.assertEqual(rr.rdata, b'a.example.')
+ self.assertEqual(rr.rdata, b"a.example.")
self.checkNoRemainingMessage()
+
class ProtobufTaggedExtraFieldsTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export extra fields that may have been set while being tagged.
"""
- _confdir = 'ProtobufTaggedExtraFields'
- _config_template = """
+ _confdir = "ProtobufTaggedExtraFields"
+ _config_template = (
+ """
devonly-regression-test-mode
-auth-zones=example=configs/%s/example.zone""" % _confdir
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
- _requestorId = 'S-000001727'
- _deviceId = 'd1:0a:91:dc:cc:82'
- _deviceName = 'Joe'
+ _requestorId = "S-000001727"
+ _deviceId = "d1:0a:91:dc:cc:82"
+ _deviceName = "Joe"
_lua_dns_script_file = """
function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
if qname:equal('tagged.example.') then
""" % (_requestorId, _deviceId, _deviceName)
def testA(self):
- name = 'a.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "a.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
# check the protobuf messages corresponding to the UDP query and answer
msg = self.getFirstProtobufMessage()
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
- self.checkProtobufIdentity(msg, '', b'', '')
+ self.checkProtobufIdentity(msg, "", b"", "")
# then the response
msg = self.getFirstProtobufMessage()
- self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '127.0.0.1')
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, "127.0.0.1")
self.assertEqual(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
- self.checkProtobufIdentity(msg, '', b'', '')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
+ self.checkProtobufIdentity(msg, "", b"", "")
self.checkNoRemainingMessage()
def testTagged(self):
- name = 'tagged.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "tagged.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.84")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
# check the protobuf messages corresponding to the UDP query and answer
msg = self.getFirstProtobufMessage()
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
- port = ':' + str(msg.fromPort)
- self.checkProtobufIdentity(msg, self._requestorId + port, (self._deviceId + port).encode('ascii'), self._deviceName + port)
+ port = ":" + str(msg.fromPort)
+ self.checkProtobufIdentity(
+ msg, self._requestorId + port, (self._deviceId + port).encode("ascii"), self._deviceName + port
+ )
# then the response
msg = self.getFirstProtobufMessage()
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
- self.checkProtobufIdentity(msg, self._requestorId + port, (self._deviceId + port).encode('ascii'), self._deviceName + port)
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.84")
+ self.checkProtobufIdentity(
+ msg, self._requestorId + port, (self._deviceId + port).encode("ascii"), self._deviceName + port
+ )
self.checkNoRemainingMessage()
# Again, but now the PC is involved
# check the protobuf messages corresponding to the UDP query and answer
# Re-init socket so we get a different port
- self.setUpSockets();
+ self.setUpSockets()
res = self.sendUDPQuery(query)
msg = self.getFirstProtobufMessage()
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
- port2 = ':' + str(msg.fromPort)
+ port2 = ":" + str(msg.fromPort)
self.assertNotEqual(port, port2)
- self.checkProtobufIdentity(msg, self._requestorId + port2, (self._deviceId + port2).encode('ascii'), self._deviceName + port2)
+ self.checkProtobufIdentity(
+ msg, self._requestorId + port2, (self._deviceId + port2).encode("ascii"), self._deviceName + port2
+ )
# then the response
msg = self.getFirstProtobufMessage()
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
- self.checkProtobufIdentity(msg, self._requestorId + port2, (self._deviceId + port2).encode('ascii'), self._deviceName + port2)
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.84")
+ self.checkProtobufIdentity(
+ msg, self._requestorId + port2, (self._deviceId + port2).encode("ascii"), self._deviceName + port2
+ )
self.checkNoRemainingMessage()
+
class ProtobufTaggedExtraFieldsFFITest(ProtobufTaggedExtraFieldsTest):
"""
This test makes sure that we correctly export extra fields that may have been set while being tagged (FFI version).
"""
- _confdir = 'ProtobufTaggedExtraFieldsFFI'
- _config_template = """
+
+ _confdir = "ProtobufTaggedExtraFieldsFFI"
+ _config_template = (
+ """
devonly-regression-test-mode
-auth-zones=example=configs/%s/example.zone""" % _confdir
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
end
return 0
end
- """ % (ProtobufTaggedExtraFieldsTest._requestorId, ProtobufTaggedExtraFieldsTest._deviceId, ProtobufTaggedExtraFieldsTest._deviceName)
+ """ % (
+ ProtobufTaggedExtraFieldsTest._requestorId,
+ ProtobufTaggedExtraFieldsTest._deviceId,
+ ProtobufTaggedExtraFieldsTest._deviceName,
+ )
+
class ProtobufRPZTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export the RPZ applied policy in our protobuf messages
"""
- _confdir = 'ProtobufRPZ'
- _config_template = """
+ _confdir = "ProtobufRPZ"
+ _config_template = (
+ """
devonly-regression-test-mode
-auth-zones=example=configs/%s/example.rpz.zone""" % _confdir
+auth-zones=example=configs/%s/example.rpz.zone"""
+ % _confdir
+ )
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", extendedErrorCode=99, extendedErrorExtra="EDEText"})
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.rpz.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.rpz.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
sub.test 3600 IN A 192.0.2.42
ip 3600 IN A 33.22.11.99
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
*.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
24.0.11.22.33.rpz-ip 60 IN A 1.2.3.4
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(ProtobufRPZTest, cls).generateRecursorConfig(confdir)
def testA(self):
- name = 'sub.test.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "sub.test.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
# then the response
msg = self.getFirstProtobufMessage()
self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
- self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME, 'zone.rpz.', '*.test.example.', 'sub.test.example', dnsmessage_pb2.PBDNSMessage.PolicyKind.NoAction)
- self.checkProtobufEDE(msg, 99, 'EDEText')
+ self.checkProtobufPolicy(
+ msg,
+ dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME,
+ "zone.rpz.",
+ "*.test.example.",
+ "sub.test.example",
+ dnsmessage_pb2.PBDNSMessage.PolicyKind.NoAction,
+ )
+ self.checkProtobufEDE(msg, 99, "EDEText")
self.checkProtobufOT(msg, False, False)
self.assertEqual(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
self.checkNoRemainingMessage()
def testB(self):
- name = 'ip.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '1.2.3.4')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "ip.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "1.2.3.4")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
# then the response
msg = self.getFirstProtobufMessage()
self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
- self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.RESPONSEIP, 'zone.rpz.', '24.0.11.22.33.rpz-ip.', '33.22.11.99', dnsmessage_pb2.PBDNSMessage.PolicyKind.Custom)
- self.checkProtobufEDE(msg, 99, 'EDEText')
+ self.checkProtobufPolicy(
+ msg,
+ dnsmessage_pb2.PBDNSMessage.PolicyType.RESPONSEIP,
+ "zone.rpz.",
+ "24.0.11.22.33.rpz-ip.",
+ "33.22.11.99",
+ dnsmessage_pb2.PBDNSMessage.PolicyKind.Custom,
+ )
+ self.checkProtobufEDE(msg, 99, "EDEText")
self.checkProtobufOT(msg, False, False)
self.assertEqual(len(msg.response.rrs), 1)
self.checkNoRemainingMessage()
+
class ProtobufRPZTagsTest(TestRecursorProtobuf):
"""
This test makes sure that we correctly export the RPZ tags in our protobuf messages
"""
- _confdir = 'ProtobufRPZTags'
- _config_template = """
+ _confdir = "ProtobufRPZTags"
+ _config_template = (
+ """
devonly-regression-test-mode
-auth-zones=example=configs/%s/example.rpz.zone""" % _confdir
- _tags = ['tag1', 'tag2']
- _tags_from_gettag = ['tag1-from-gettag', 'tag2-from-gettag']
- _tags_from_rpz = ['tag1-from-rpz', 'tag2-from-rpz' ]
+auth-zones=example=configs/%s/example.rpz.zone"""
+ % _confdir
+ )
+ _tags = ["tag1", "tag2"]
+ _tags_from_gettag = ["tag1-from-gettag", "tag2-from-gettag"]
+ _tags_from_rpz = ["tag1-from-rpz", "tag2-from-rpz"]
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true, tags={'tag1', 'tag2'} } )
rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", tags={ '%s', '%s'} })
- """ % (protobufServersParameters[0].port, protobufServersParameters[1].port, _confdir, _tags_from_rpz[0], _tags_from_rpz[1])
+ """ % (
+ protobufServersParameters[0].port,
+ protobufServersParameters[1].port,
+ _confdir,
+ _tags_from_rpz[0],
+ _tags_from_rpz[1],
+ )
_lua_dns_script_file = """
function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
return 0, { '%s', '%s' }
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.rpz.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.rpz.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
sub.test 3600 IN A 192.0.2.42
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
*.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(ProtobufRPZTagsTest, cls).generateRecursorConfig(confdir)
def testA(self):
- name = 'sub.test.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "sub.test.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
# then the response
msg = self.getFirstProtobufMessage()
self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
- self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME, 'zone.rpz.', '*.test.example.', 'sub.test.example', dnsmessage_pb2.PBDNSMessage.PolicyKind.NoAction)
+ self.checkProtobufPolicy(
+ msg,
+ dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME,
+ "zone.rpz.",
+ "*.test.example.",
+ "sub.test.example",
+ dnsmessage_pb2.PBDNSMessage.PolicyKind.NoAction,
+ )
self.checkProtobufTags(msg, self._tags + self._tags_from_gettag + self._tags_from_rpz)
self.assertEqual(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.42")
self.checkNoRemainingMessage()
"""
This test makes sure that we can correctly add extra meta fields (FFI version).
"""
- _confdir = 'ProtobufMetaFFI'
- _config_template = """
+
+ _confdir = "ProtobufMetaFFI"
+ _config_template = (
+ """
devonly-regression-test-mode
-auth-zones=example=configs/%s/example.zone""" % _confdir
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_lua_config_file = """
protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
""" % (protobufServersParameters[0].port, protobufServersParameters[1].port)
return 0
end
"""
+
def testMeta(self):
- name = 'meta.example.'
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.85')
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "meta.example."
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.85")
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expected)
# check the protobuf messages corresponding to the UDP query and answer
msg = self.getFirstProtobufMessage()
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
- self.checkProtobufMetas(msg, {'meta-str': { "stringVal" : ["content", "keyword"]}, 'meta-int': {"intVal" : [21, 42]}})
+ self.checkProtobufMetas(
+ msg, {"meta-str": {"stringVal": ["content", "keyword"]}, "meta-int": {"intVal": [21, 42]}}
+ )
# then the response
msg = self.getFirstProtobufMessage()
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
- self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.85')
- self.checkProtobufMetas(msg, {'meta-str': { "stringVal" : ["content", "keyword"]}, 'meta-int': {"intVal" : [21, 42]}})
+ self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), "192.0.2.85")
+ self.checkProtobufMetas(
+ msg, {"meta-str": {"stringVal": ["content", "keyword"]}, "meta-int": {"intVal": [21, 42]}}
+ )
self.checkNoRemainingMessage()
import os
from recursortests import RecursorTest
+
class ProxyByTableTest(RecursorTest):
"""
This test makes sure that we correctly use the proxy-mapped address during the ACL check
"""
- _confdir = 'ProxyByTable'
+
+ _confdir = "ProxyByTable"
_auth_zones = RecursorTest._default_auth_zones
- _config_template = """dnssec=validate
+ _config_template = (
+ """dnssec=validate
auth-zones=authzone.example=configs/%s/authzone.zone
allow-from=3.4.5.0/24
- """ % _confdir
+ """
+ % _confdir
+ )
_lua_config_file = """
addProxyMapping("127.0.0.0/24", "3.4.5.6:99")
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'authzone.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN authzone.example.
+ authzonepath = os.path.join(confdir, "authzone.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN authzone.example.
@ 3600 IN SOA {soa}
@ 3600 IN A 192.0.2.88
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(ProxyByTableTest, cls).generateRecursorConfig(confdir)
-
def testA(self):
- expected = dns.rrset.from_text('ns.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.9'.format(prefix=self._PREFIX))
- query = dns.message.make_query('ns.secure.example', 'A', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "ns.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.9".format(prefix=self._PREFIX)
+ )
+ query = dns.message.make_query("ns.secure.example", "A", want_dnssec=True)
query.flags |= dns.flags.AD
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertMessageIsAuthenticated(res)
self.assertRRsetInAnswer(res, expected)
self.assertMatchingRRSIGInAnswer(res, expected)
-
-
from recursortests import RecursorTest
from proxyprotocol import ProxyProtocol
+
class ProxyProtocolAllowedTest(RecursorTest):
- _confdir = 'ProxyProtocolAllowed'
+ _confdir = "ProxyProtocolAllowed"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_lua_dns_script_file = """
function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp, proxyProtocolValues)
""" % (_wsPort, _wsPassword, _apiKey)
def checkStats(self, expected127001):
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/jsonstat?command=get-remote-ring&name=remotes'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/jsonstat?command=get-remote-ring&name=remotes"
r = requests.get(url, headers=headers, timeout=self._wsTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
# testLocalProxyProtocol test, which actually does not set a source address. If we see a
# higher value than expected, some ProxyProtocol clients were accounted as 127.0.0.1, which
# is not right as all other tests set a source address other than 127.0.0.1
- for entry in content['entries']:
- if entry[1] == '127.0.0.1':
+ for entry in content["entries"]:
+ if entry[1] == "127.0.0.1":
self.assertEqual(entry[0], expected127001)
def testLocalProxyProtocol(self):
- qname = 'local.proxy-protocol.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.255')
+ qname = "local.proxy-protocol.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.255")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
queryPayload = query.to_wire()
ppPayload = ProxyProtocol.getPayload(True, False, False, None, None, None, None, [])
payload = ppPayload + queryPayload
self.checkStats(2)
def testInvalidMagicProxyProtocol(self):
- qname = 'invalid-magic.proxy-protocol.recursor-tests.powerdns.com.'
+ qname = "invalid-magic.proxy-protocol.recursor-tests.powerdns.com."
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
queryPayload = query.to_wire()
ppPayload = ProxyProtocol.getPayload(True, False, False, None, None, None, None, [])
- ppPayload = b'\x00' + ppPayload[1:]
+ ppPayload = b"\x00" + ppPayload[1:]
payload = ppPayload + queryPayload
# UDP
self.assertEqual(res, None)
def testTCPOneByteAtATimeProxyProtocol(self):
- qname = 'tcp-one-byte-at-a-time.proxy-protocol.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+ qname = "tcp-one-byte-at-a-time.proxy-protocol.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.1")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
queryPayload = query.to_wire()
- ppPayload = ProxyProtocol.getPayload(False, True, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ ppPayload = ProxyProtocol.getPayload(
+ False, True, False, "127.0.0.42", "255.255.255.255", 0, 65535, [[0, b"foo"], [255, b"bar"]]
+ )
# TCP
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
for i in range(len(ppPayload)):
- sock.send(ppPayload[i:i+1])
+ sock.send(ppPayload[i : i + 1])
time.sleep(0.01)
value = struct.pack("!H", len(queryPayload))
for i in range(len(value)):
- sock.send(value[i:i+1])
+ sock.send(value[i : i + 1])
time.sleep(0.01)
for i in range(len(queryPayload)):
- sock.send(queryPayload[i:i+1])
+ sock.send(queryPayload[i : i + 1])
time.sleep(0.01)
data = sock.recv(2)
def testTooLargeProxyProtocol(self):
# the total payload (proxy protocol + DNS) is larger than proxy-protocol-maximum-size
# so it should be dropped
- qname = 'too-large.proxy-protocol.recursor-tests.powerdns.com.'
+ qname = "too-large.proxy-protocol.recursor-tests.powerdns.com."
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
queryPayload = query.to_wire()
- ppPayload = ProxyProtocol.getPayload(False, True, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [1, b'A'*512], [ 255, b'bar'] ])
+ ppPayload = ProxyProtocol.getPayload(
+ False, True, False, "127.0.0.42", "255.255.255.255", 0, 65535, [[0, b"foo"], [1, b"A" * 512], [255, b"bar"]]
+ )
payload = ppPayload + queryPayload
# UDP
self.assertEqual(res, None)
def testNoHeaderProxyProtocol(self):
- qname = 'no-header.proxy-protocol.recursor-tests.powerdns.com.'
+ qname = "no-header.proxy-protocol.recursor-tests.powerdns.com."
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
self.assertEqual(res, None)
def testIPv4ProxyProtocol(self):
- qname = 'ipv4.proxy-protocol.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+ qname = "ipv4.proxy-protocol.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.1")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
sender = getattr(self, method)
- res = sender(query, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ res = sender(query, False, "127.0.0.42", "255.255.255.255", 0, 65535, [[0, b"foo"], [255, b"bar"]])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
def testIPv4NoValuesProxyProtocol(self):
- qname = 'ipv4-no-values.proxy-protocol.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.255')
+ qname = "ipv4-no-values.proxy-protocol.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.255")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
sender = getattr(self, method)
- res = sender(query, False, '127.0.0.42', '255.255.255.255', 0, 65535)
+ res = sender(query, False, "127.0.0.42", "255.255.255.255", 0, 65535)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
def testIPv4ProxyProtocolNotAuthorized(self):
- qname = 'ipv4-not-authorized.proxy-protocol.recursor-tests.powerdns.com.'
+ qname = "ipv4-not-authorized.proxy-protocol.recursor-tests.powerdns.com."
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
sender = getattr(self, method)
- res = sender(query, False, '192.0.2.255', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ res = sender(query, False, "192.0.2.255", "255.255.255.255", 0, 65535, [[0, b"foo"], [255, b"bar"]])
self.assertEqual(res, None)
def testIPv6ProxyProtocol(self):
- qname = 'ipv6.proxy-protocol.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+ qname = "ipv6.proxy-protocol.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.1")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
sender = getattr(self, method)
- res = sender(query, True, '::42', '2001:db8::ff', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ res = sender(query, True, "::42", "2001:db8::ff", 0, 65535, [[0, b"foo"], [255, b"bar"]])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
def testIPv6NoValuesProxyProtocol(self):
- qname = 'ipv6-no-values.proxy-protocol.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.255')
+ qname = "ipv6-no-values.proxy-protocol.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.255")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
sender = getattr(self, method)
- res = sender(query, True, '::42', '2001:db8::ff', 0, 65535)
+ res = sender(query, True, "::42", "2001:db8::ff", 0, 65535)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
def testIPv6ProxyProtocolNotAuthorized(self):
- qname = 'ipv6-not-authorized.proxy-protocol.recursor-tests.powerdns.com.'
+ qname = "ipv6-not-authorized.proxy-protocol.recursor-tests.powerdns.com."
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
sender = getattr(self, method)
- res = sender(query, True, '2001:db8::1', '2001:db8::ff', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ res = sender(query, True, "2001:db8::1", "2001:db8::ff", 0, 65535, [[0, b"foo"], [255, b"bar"]])
self.assertEqual(res, None)
def testIPv6ProxyProtocolSeveralQueriesOverTCP(self):
- qname = 'several-queries-tcp.proxy-protocol.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+ qname = "several-queries-tcp.proxy-protocol.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.1")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
queryPayload = query.to_wire()
- ppPayload = ProxyProtocol.getPayload(False, True, True, '::42', '2001:db8::ff', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ ppPayload = ProxyProtocol.getPayload(
+ False, True, True, "::42", "2001:db8::ff", 0, 65535, [[0, b"foo"], [255, b"bar"]]
+ )
# TCP
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.assertEqual(count, 5)
sock.close()
+
class ProxyProtocolAllowedFFITest(ProxyProtocolAllowedTest):
# same tests than ProxyProtocolAllowedTest but with the Lua FFI interface instead of the regular one
- _confdir = 'ProxyProtocolAllowedFFI'
+ _confdir = "ProxyProtocolAllowedFFI"
_lua_dns_script_file = """
local ffi = require("ffi")
end
""" % (ProxyProtocolAllowedTest._recursorPort)
+
class ProxyProtocolNotAllowedTest(RecursorTest):
- _confdir = 'ProxyProtocolNotAllowed'
+ _confdir = "ProxyProtocolNotAllowed"
_lua_dns_script_file = """
function preresolve(dq)
""" % ()
def testNoHeaderProxyProtocol(self):
- qname = 'no-header.proxy-protocol-not-allowed.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+ qname = "no-header.proxy-protocol-not-allowed.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.1")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
self.assertRRsetInAnswer(res, expected)
def testIPv4ProxyProtocol(self):
- qname = 'ipv4.proxy-protocol-not-allowed.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+ qname = "ipv4.proxy-protocol-not-allowed.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.1")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
sender = getattr(self, method)
- res = sender(query, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ res = sender(query, False, "127.0.0.42", "255.255.255.255", 0, 65535, [[0, b"foo"], [255, b"bar"]])
self.assertEqual(res, None)
+
class ProxyProtocolExceptionTest(RecursorTest):
- _confdir = 'ProxyProtocolException'
+ _confdir = "ProxyProtocolException"
_lua_dns_script_file = """
function preresolve(dq)
""" % (RecursorTest._recursorPort)
def testNoHeaderProxyProtocol(self):
- qname = 'no-header.proxy-protocol-not-allowed.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+ qname = "no-header.proxy-protocol-not-allowed.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.1")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
self.assertRRsetInAnswer(res, expected)
def testIPv4ProxyProtocol(self):
- qname = 'ipv4.proxy-protocol-not-allowed.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+ qname = "ipv4.proxy-protocol-not-allowed.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.1")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
sender = getattr(self, method)
- res = sender(query, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ res = sender(query, False, "127.0.0.42", "255.255.255.255", 0, 65535, [[0, b"foo"], [255, b"bar"]])
self.assertEqual(res, None)
+
class ProxyProtocolConfigReloadTest(RecursorTest):
- _confdir = 'ProxyProtocolConfigReload'
+ _confdir = "ProxyProtocolConfigReload"
_lua_dns_script_file = """
function preresolve(dq)
"""
def testIPv4ProxyProtocol(self):
- qname = 'ipv4.proxy-protocol-not-allowed.recursor-tests.powerdns.com.'
- expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+ qname = "ipv4.proxy-protocol-not-allowed.recursor-tests.powerdns.com."
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, "A", "192.0.2.1")
- query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ query = dns.message.make_query(qname, "A", want_dnssec=True)
for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
sender = getattr(self, method)
- res = sender(query, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ res = sender(query, False, "127.0.0.42", "255.255.255.255", 0, 65535, [[0, b"foo"], [255, b"bar"]])
self.assertEqual(res, None)
ProxyProtocolConfigReloadTest._config_template = """
proxy-protocol-from=127.0.0.1/32
allow-from=127.0.0.0/24, ::1/128
"""
- confdir = os.path.join('configs', ProxyProtocolConfigReloadTest._confdir)
+ confdir = os.path.join("configs", ProxyProtocolConfigReloadTest._confdir)
ProxyProtocolConfigReloadTest.generateRecursorConfig(confdir)
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % confdir,
- 'reload-acls']
+ rec_controlCmd = [os.environ["RECCONTROL"], "--config-dir=%s" % confdir, "reload-acls"]
try:
subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (rec_controlCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (rec_controlCmd, e.returncode, e.output))
for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
sender = getattr(self, method)
- res = sender(query, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ res = sender(query, False, "127.0.0.42", "255.255.255.255", 0, 65535, [[0, b"foo"], [255, b"bar"]])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expected)
import dns
from recursortests import RecursorTest
+
class RDNotAllowedTest(RecursorTest):
- _confdir = 'RDNotAllowed'
+ _confdir = "RDNotAllowed"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
"""
+
def testRD0(self):
- query = dns.message.make_query('ns.secure.example', 'A', want_dnssec=True)
+ query = dns.message.make_query("ns.secure.example", "A", want_dnssec=True)
query.flags |= dns.flags.AD
query.flags &= ~dns.flags.RD
self.assertRcodeEqual(res, dns.rcode.REFUSED)
self.assertAnswerEmpty(res)
+
class RDAllowedTest(RecursorTest):
- _confdir = 'RDAllowed'
+ _confdir = "RDAllowed"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """
disable-packetcache=yes
allow-no-rd=yes
"""
+
def testRD0(self):
- expected = dns.rrset.from_text('ns.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.9'.format(prefix=self._PREFIX))
- query = dns.message.make_query('ns.secure.example', 'A', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "ns.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.9".format(prefix=self._PREFIX)
+ )
+ query = dns.message.make_query("ns.secure.example", "A", want_dnssec=True)
query.flags |= dns.flags.AD
query.flags &= ~dns.flags.RD
from recursortests import RecursorTest
-class RPZServer(object):
+class RPZServer(object):
def __init__(self, port):
self._currentSerial = 0
self._targetSerial = 1
self._serverPort = port
- listener = threading.Thread(name='RPZ Listener', target=self._listener, args=[])
+ listener = threading.Thread(name="RPZ Listener", target=self._listener, args=[])
listener.daemon = True
listener.start()
return False
if newSerial != self._currentSerial + 1:
- raise AssertionError("Asking the RPZ server to serve serial %d, already serving %d" % (newSerial, self._currentSerial))
+ raise AssertionError(
+ "Asking the RPZ server to serve serial %d, already serving %d" % (newSerial, self._currentSerial)
+ )
self._targetSerial = newSerial
return True
if message.question[0].rdtype == dns.rdatatype.AXFR:
if self._currentSerial != 0:
- print('Received an AXFR query but IXFR expected because the current serial is %d' % (self._currentSerial))
+ print(
+ "Received an AXFR query but IXFR expected because the current serial is %d" % (self._currentSerial)
+ )
return (None, self._currentSerial)
newSerial = self._targetSerial
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('a.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("a.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif message.question[0].rdtype == dns.rdatatype.IXFR:
oldSerial = message.authority[0][0].serial
# special case for the 9th update, which might get skipped
if oldSerial != self._currentSerial and self._currentSerial != 9:
- print('Received an IXFR query with an unexpected serial %d, expected %d' % (oldSerial, self._currentSerial))
+ print(
+ "Received an IXFR query with an unexpected serial %d, expected %d"
+ % (oldSerial, self._currentSerial)
+ )
return (None, self._currentSerial)
newSerial = self._targetSerial
if newSerial == 2:
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % oldSerial,
+ ),
# no deletion
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('b.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("b.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 3:
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('a.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text("a.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
# no addition
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 4:
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('b.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('c.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text("b.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("c.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 5:
# this one is a bit special, we are answering with a full AXFR
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('d.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('tc.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-tcp-only.'),
- dns.rrset.from_text('drop.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-drop.'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("d.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "tc.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "rpz-tcp-only."
+ ),
+ dns.rrset.from_text(
+ "drop.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "rpz-drop."
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 6:
# back to IXFR
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('d.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('tc.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-tcp-only.'),
- dns.rrset.from_text('drop.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-drop.'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('e.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1', '192.0.2.2'),
- dns.rrset.from_text('e.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.MX, '10 mx.example.'),
- dns.rrset.from_text('f.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'e.example.'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text("d.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "tc.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "rpz-tcp-only."
+ ),
+ dns.rrset.from_text(
+ "drop.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "rpz-drop."
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "e.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.2"
+ ),
+ dns.rrset.from_text(
+ "e.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.MX, "10 mx.example."
+ ),
+ dns.rrset.from_text(
+ "f.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "e.example."
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 7:
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('e.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1', '192.0.2.2'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('e.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.2'),
- dns.rrset.from_text('tc.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-tcp-only.'),
- dns.rrset.from_text('drop.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-drop.'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text(
+ "e.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1", "192.0.2.2"
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("e.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.2"),
+ dns.rrset.from_text(
+ "tc.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "rpz-tcp-only."
+ ),
+ dns.rrset.from_text(
+ "drop.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "rpz-drop."
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 8:
# this one is a bit special too, we are answering with a full AXFR and the new zone is empty
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 9:
# IXFR inserting a duplicate, we should not crash and skip it
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('dup.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-passthru.'),
- dns.rrset.from_text('dup.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-passthru.'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "dup.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "rpz-passthru."
+ ),
+ dns.rrset.from_text(
+ "dup.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.CNAME, "rpz-passthru."
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 10:
# full AXFR to make sure we are removing the duplicate, adding a record, to check that the update was correctly applied
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('f.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("f.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif newSerial == 11:
# IXFR with two deltas, the first one adding a 'g' and the second one removing 'f'
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1)),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('g.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('f.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1)),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1))
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % (newSerial + 1),
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % oldSerial,
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("g.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("f.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % (newSerial + 1),
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % (newSerial + 1),
+ ),
+ ]
# this one has two updates in one
newSerial = newSerial + 1
self._targetSerial = self._targetSerial + 1
message = dns.message.from_wire(data)
if len(message.question) != 1:
- print('Invalid RPZ query, qdcount is %d' % (len(message.question)))
+ print("Invalid RPZ query, qdcount is %d" % (len(message.question)))
break
if not message.question[0].rdtype in [dns.rdatatype.AXFR, dns.rdatatype.IXFR]:
- print('Invalid RPZ query, qtype is %d' % (message.question.rdtype))
+ print("Invalid RPZ query, qtype is %d" % (message.question.rdtype))
break
(serial, answer) = self._getAnswer(message)
if not answer:
- print('Unable to get a response for %s %d' % (message.question[0].name, message.question[0].rdtype))
+ print("Unable to get a response for %s %d" % (message.question[0].name, message.question[0].rdtype))
break
wire = answer.to_wire()
while True:
try:
(conn, _) = sock.accept()
- thread = threading.Thread(name='RPZ Connection Handler',
- target=self._connectionHandler,
- args=[conn])
+ thread = threading.Thread(name="RPZ Connection Handler", target=self._connectionHandler, args=[conn])
thread.daemon = True
thread.start()
except socket.error as e:
- print('Error in RPZ socket: %s' % str(e))
+ print("Error in RPZ socket: %s" % str(e))
sock.close()
+
class RPZRecursorTest(RecursorTest):
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
- _confdir = 'RPZ'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
+ _confdir = "RPZ"
_lua_dns_script_file = """
function prerpz(dq)
""" % (_confdir, _wsPort, _wsPassword, _apiKey)
def sendNotify(self):
- notify = dns.message.make_query('zone.rpz', 'SOA', want_dnssec=False)
- notify.set_opcode(4) # notify
+ notify = dns.message.make_query("zone.rpz", "SOA", want_dnssec=False)
+ notify.set_opcode(4) # notify
res = self.sendUDPQuery(notify)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(res.opcode(), 4)
- self.assertEqual(res.question[0].to_text(), 'zone.rpz. IN SOA')
+ self.assertEqual(res.question[0].to_text(), "zone.rpz. IN SOA")
def assertAdditionalHasSOA(self, msg, name):
if not isinstance(msg, dns.message.Message):
raise AssertionError("No %s SOA record found in the additional section:\n%s" % (name, msg.to_text()))
def checkBlocked(self, name, shouldBeBlocked=True, adQuery=False, singleCheck=False, soa=None):
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.CD
if adQuery:
query.flags |= dns.flags.AD
res = sender(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
if shouldBeBlocked:
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.1")
else:
- expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, "A", "192.0.2.42")
self.assertRRsetInAnswer(res, expected)
if soa:
if soa:
self.assertAdditionalHasSOA(res, soa)
- def checkNXD(self, qname, qtype='A'):
+ def checkNXD(self, qname, qtype="A"):
query = dns.message.make_query(qname, qtype, want_dnssec=True)
query.flags |= dns.flags.CD
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(len(res.answer), 0)
self.assertEqual(len(res.authority), 1)
- def checkTruncated(self, qname, qtype='A', soa=None):
+ def checkTruncated(self, qname, qtype="A", soa=None):
query = dns.message.make_query(qname, qtype, want_dnssec=True)
query.flags |= dns.flags.CD
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD', 'TC'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD", "TC"])
self.assertEqual(len(res.answer), 0)
self.assertEqual(len(res.authority), 0)
if soa:
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "CD"])
self.assertEqual(len(res.answer), 0)
self.assertEqual(len(res.authority), 1)
self.assertEqual(len(res.additional), 0)
- def checkDropped(self, qname, qtype='A'):
+ def checkDropped(self, qname, qtype="A"):
query = dns.message.make_query(qname, qtype, want_dnssec=True)
query.flags |= dns.flags.CD
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(res, None)
def checkRPZStats(self, serial, recordsCount, fullXFRCount, totalXFRCount):
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/rpzstatistics'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/api/v1/servers/localhost/rpzstatistics"
r = requests.get(url, headers=headers, timeout=self._wsTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertIn('zone.rpz.', content)
- zone = content['zone.rpz.']
- for key in ['last_update', 'records', 'serial', 'transfers_failed', 'transfers_full', 'transfers_success']:
+ self.assertIn("zone.rpz.", content)
+ zone = content["zone.rpz."]
+ for key in ["last_update", "records", "serial", "transfers_failed", "transfers_full", "transfers_success"]:
self.assertIn(key, zone)
- self.assertEqual(zone['serial'], serial)
- self.assertEqual(zone['records'], recordsCount)
- self.assertEqual(zone['transfers_full'], fullXFRCount)
- self.assertEqual(zone['transfers_success'], totalXFRCount)
+ self.assertEqual(zone["serial"], serial)
+ self.assertEqual(zone["records"], recordsCount)
+ self.assertEqual(zone["transfers_full"], fullXFRCount)
+ self.assertEqual(zone["transfers_success"], totalXFRCount)
+
rpzServerPort = 4250
rpzServer = RPZServer(rpzServerPort)
+
class RPZXFRRecursorTest(RPZRecursorTest):
"""
This test makes sure that we correctly update RPZ zones via AXFR then IXFR
"""
global rpzServerPort
- _confdir = 'RPZXFRRecursor'
+ _confdir = "RPZXFRRecursor"
_lua_config_file = """
-- The first server is a bogus one, to test that we correctly fail over to the second one
rpzPrimary({'127.0.0.1:9999', '127.0.0.1:%d'}, 'zone.rpz.', { refresh=1, includeSOA=true, dumpFile="configs/%s/rpz.zone.dump"})
""" % (rpzServerPort, _confdir)
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
auth-zones=example=configs/%s/example.zone
webserver=yes
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
b 3600 IN A 192.0.2.42
c 3600 IN A 192.0.2.42
d 3600 IN A 192.0.2.42
e 3600 IN A 192.0.2.42
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(RPZXFRRecursorTest, cls).generateRecursorConfig(confdir)
def checkDump(self, serial, timeout=2):
- file = 'configs/%s/rpz.zone.dump' % self._confdir
+ file = "configs/%s/rpz.zone.dump" % self._confdir
attempts = 0
- incr = .1
+ incr = 0.1
# There's a file base race here, so do a few attempts
while attempts < timeout:
try:
- zone = dns.zone.from_file(file, 'zone.rpz', relativize=False, check_origin=False, allow_include=False)
- soa = zone['']
+ zone = dns.zone.from_file(file, "zone.rpz", relativize=False, check_origin=False, allow_include=False)
+ soa = zone[""]
soa.find_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA)
# if the above call did not throw an exception, the SOA has the right owner, continue
soa = zone.get_soa()
- if soa.serial == serial and soa.mname == dns.name.from_text('ns.zone.rpz.'):
- return # we found what we expected
+ if soa.serial == serial and soa.mname == dns.name.from_text("ns.zone.rpz."):
+ return # we found what we expected
except FileNotFoundError as e:
pass
attempts = attempts + incr
time.sleep(incr)
- raise AssertionError("Waited %d seconds for the dumpfile to be updated to %d but the serial is still %d" % (timeout, serial, soa.serial))
+ raise AssertionError(
+ "Waited %d seconds for the dumpfile to be updated to %d but the serial is still %d"
+ % (timeout, serial, soa.serial)
+ )
def waitUntilCorrectSerialIsLoaded(self, serial, timeout=5):
global rpzServer
rpzServer.moveToSerial(serial)
attempts = 0
- incr = .1
+ incr = 0.1
while attempts < timeout:
currentSerial = rpzServer.getCurrentSerial()
if currentSerial > serial:
attempts = attempts + incr
time.sleep(incr)
- raise AssertionError("Waited %d seconds for the serial to be updated to %d but the serial is still %d" % (timeout, serial, currentSerial))
+ raise AssertionError(
+ "Waited %d seconds for the serial to be updated to %d but the serial is still %d"
+ % (timeout, serial, currentSerial)
+ )
def testRPZ(self):
# Fresh RPZ does not need a notify
# first zone, only a should be blocked
self.waitUntilCorrectSerialIsLoaded(1)
self.checkRPZStats(1, 1, 1, self._xfrDone)
- self.checkBlocked('a.example.', soa='zone.rpz.')
- self.checkNotBlocked('b.example.')
- self.checkNotBlocked('c.example.')
+ self.checkBlocked("a.example.", soa="zone.rpz.")
+ self.checkNotBlocked("b.example.")
+ self.checkNotBlocked("c.example.")
# second zone, a and b should be blocked
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(2)
self.checkRPZStats(2, 2, 1, self._xfrDone)
- self.checkBlocked('a.example.', soa='zone.rpz.')
- self.checkBlocked('b.example.', soa='zone.rpz.')
- self.checkNotBlocked('c.example.')
+ self.checkBlocked("a.example.", soa="zone.rpz.")
+ self.checkBlocked("b.example.", soa="zone.rpz.")
+ self.checkNotBlocked("c.example.")
# third zone, only b should be blocked
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(3)
self.checkRPZStats(3, 1, 1, self._xfrDone)
- self.checkNotBlocked('a.example.')
- self.checkBlocked('b.example.', soa='zone.rpz.')
- self.checkNotBlocked('c.example.')
+ self.checkNotBlocked("a.example.")
+ self.checkBlocked("b.example.", soa="zone.rpz.")
+ self.checkNotBlocked("c.example.")
# fourth zone, only c should be blocked
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(4)
self.checkRPZStats(4, 1, 1, self._xfrDone)
- self.checkNotBlocked('a.example.')
- self.checkNotBlocked('b.example.')
- self.checkBlocked('c.example.', soa='zone.rpz.')
+ self.checkNotBlocked("a.example.")
+ self.checkNotBlocked("b.example.")
+ self.checkBlocked("c.example.", soa="zone.rpz.")
# fifth zone, we should get a full AXFR this time, and only d should be blocked
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(5)
self.checkRPZStats(5, 3, 2, self._xfrDone)
- self.checkNotBlocked('a.example.')
- self.checkNotBlocked('b.example.')
- self.checkNotBlocked('c.example.')
- self.checkBlocked('d.example.', soa='zone.rpz.')
+ self.checkNotBlocked("a.example.")
+ self.checkNotBlocked("b.example.")
+ self.checkNotBlocked("c.example.")
+ self.checkBlocked("d.example.", soa="zone.rpz.")
# sixth zone, only e should be blocked, f is a local data record
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(6)
self.checkRPZStats(6, 2, 2, self._xfrDone)
- self.checkNotBlocked('a.example.')
- self.checkNotBlocked('b.example.')
- self.checkNotBlocked('c.example.')
- self.checkNotBlocked('d.example.')
- self.checkCustom('e.example.', 'A', dns.rrset.from_text('e.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1', '192.0.2.2'), soa='zone.rpz.')
- self.checkCustom('e.example.', 'MX', dns.rrset.from_text('e.example.', 0, dns.rdataclass.IN, 'MX', '10 mx.example.'))
- self.checkNoData('e.example.', 'AAAA', soa='zone.rpz.')
- self.checkCustom('f.example.', 'A', dns.rrset.from_text('f.example.', 0, dns.rdataclass.IN, 'CNAME', 'e.example.'), soa='zone.rpz.')
+ self.checkNotBlocked("a.example.")
+ self.checkNotBlocked("b.example.")
+ self.checkNotBlocked("c.example.")
+ self.checkNotBlocked("d.example.")
+ self.checkCustom(
+ "e.example.",
+ "A",
+ dns.rrset.from_text("e.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1", "192.0.2.2"),
+ soa="zone.rpz.",
+ )
+ self.checkCustom(
+ "e.example.", "MX", dns.rrset.from_text("e.example.", 0, dns.rdataclass.IN, "MX", "10 mx.example.")
+ )
+ self.checkNoData("e.example.", "AAAA", soa="zone.rpz.")
+ self.checkCustom(
+ "f.example.",
+ "A",
+ dns.rrset.from_text("f.example.", 0, dns.rdataclass.IN, "CNAME", "e.example."),
+ soa="zone.rpz.",
+ )
# seventh zone, e should only have one A
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(7)
self.checkRPZStats(7, 4, 2, self._xfrDone)
- self.checkNotBlocked('a.example.')
- self.checkNotBlocked('b.example.')
- self.checkNotBlocked('c.example.')
- self.checkNotBlocked('d.example.')
- self.checkCustom('e.example.', 'A', dns.rrset.from_text('e.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.2'), soa='zone.rpz.')
- self.checkCustom('e.example.', 'MX', dns.rrset.from_text('e.example.', 0, dns.rdataclass.IN, 'MX', '10 mx.example.'), soa='zone.rpz.')
- self.checkNoData('e.example.', 'AAAA', soa='zone.rpz.')
- self.checkCustom('f.example.', 'A', dns.rrset.from_text('f.example.', 0, dns.rdataclass.IN, 'CNAME', 'e.example.'), soa='zone.rpz.')
+ self.checkNotBlocked("a.example.")
+ self.checkNotBlocked("b.example.")
+ self.checkNotBlocked("c.example.")
+ self.checkNotBlocked("d.example.")
+ self.checkCustom(
+ "e.example.",
+ "A",
+ dns.rrset.from_text("e.example.", 0, dns.rdataclass.IN, "A", "192.0.2.2"),
+ soa="zone.rpz.",
+ )
+ self.checkCustom(
+ "e.example.",
+ "MX",
+ dns.rrset.from_text("e.example.", 0, dns.rdataclass.IN, "MX", "10 mx.example."),
+ soa="zone.rpz.",
+ )
+ self.checkNoData("e.example.", "AAAA", soa="zone.rpz.")
+ self.checkCustom(
+ "f.example.",
+ "A",
+ dns.rrset.from_text("f.example.", 0, dns.rdataclass.IN, "CNAME", "e.example."),
+ soa="zone.rpz.",
+ )
# check that the policy is disabled for AD=1 queries
- self.checkNotBlocked('e.example.', True)
+ self.checkNotBlocked("e.example.", True)
# check non-custom policies
- self.checkTruncated('tc.example.', soa='zone.rpz.')
- self.checkDropped('drop.example.')
+ self.checkTruncated("tc.example.", soa="zone.rpz.")
+ self.checkDropped("drop.example.")
# eighth zone, all entries should be gone
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(8)
self.checkRPZStats(8, 0, 3, self._xfrDone)
- self.checkNotBlocked('a.example.')
- self.checkNotBlocked('b.example.')
- self.checkNotBlocked('c.example.')
- self.checkNotBlocked('d.example.')
- self.checkNotBlocked('e.example.')
- self.checkNXD('f.example.')
- self.checkNXD('tc.example.')
- self.checkNXD('drop.example.')
+ self.checkNotBlocked("a.example.")
+ self.checkNotBlocked("b.example.")
+ self.checkNotBlocked("c.example.")
+ self.checkNotBlocked("d.example.")
+ self.checkNotBlocked("e.example.")
+ self.checkNXD("f.example.")
+ self.checkNXD("tc.example.")
+ self.checkNXD("drop.example.")
# 9th zone is a duplicate, it might get skipped
global rpzServer
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(10)
self.checkRPZStats(10, 1, 4, self._xfrDone)
- self.checkNotBlocked('a.example.')
- self.checkNotBlocked('b.example.')
- self.checkNotBlocked('c.example.')
- self.checkNotBlocked('d.example.')
- self.checkNotBlocked('e.example.')
- self.checkBlocked('f.example.', soa='zone.rpz.')
- self.checkNXD('tc.example.')
- self.checkNXD('drop.example.')
+ self.checkNotBlocked("a.example.")
+ self.checkNotBlocked("b.example.")
+ self.checkNotBlocked("c.example.")
+ self.checkNotBlocked("d.example.")
+ self.checkNotBlocked("e.example.")
+ self.checkBlocked("f.example.", soa="zone.rpz.")
+ self.checkNXD("tc.example.")
+ self.checkNXD("drop.example.")
# the next update will update the zone twice
rpzServer.moveToSerial(11)
self.sendNotify()
self.waitUntilCorrectSerialIsLoaded(12)
self.checkRPZStats(12, 1, 4, self._xfrDone)
- self.checkNotBlocked('a.example.')
- self.checkNotBlocked('b.example.')
- self.checkNotBlocked('c.example.')
- self.checkNotBlocked('d.example.')
- self.checkNotBlocked('e.example.')
- self.checkNXD('f.example.')
- self.checkBlocked('g.example.', soa='zone.rpz.')
- self.checkNXD('tc.example.')
- self.checkNXD('drop.example.')
+ self.checkNotBlocked("a.example.")
+ self.checkNotBlocked("b.example.")
+ self.checkNotBlocked("c.example.")
+ self.checkNotBlocked("d.example.")
+ self.checkNotBlocked("e.example.")
+ self.checkNXD("f.example.")
+ self.checkBlocked("g.example.", soa="zone.rpz.")
+ self.checkNXD("tc.example.")
+ self.checkNXD("drop.example.")
+
class RPZFileRecursorTest(RPZRecursorTest):
"""
This test makes sure that we correctly load RPZ zones from a file
"""
- _confdir = 'RPZFileRecursor'
+ _confdir = "RPZFileRecursor"
_lua_config_file = """
rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", includeSOA=true })
""" % (_confdir)
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
b 3600 IN A 192.0.2.42
d 3600 IN A 192.0.2.42
e 3600 IN A 192.0.2.42
z 3600 IN A 192.0.2.42
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
a.example.zone.rpz. 60 IN A 192.0.2.42
a.example.zone.rpz. 60 IN A 192.0.2.43
drop.example.zone.rpz. 60 IN CNAME rpz-drop.
z.example.zone.rpz. 60 IN A 192.0.2.1
tc.example.zone.rpz. 60 IN CNAME rpz-tcp-only.
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(RPZFileRecursorTest, cls).generateRecursorConfig(confdir)
def testRPZ(self):
- self.checkCustom('a.example.', 'A', dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.42', '192.0.2.43'))
- self.checkCustom('a.example.', 'TXT', dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'TXT', '"some text"'))
- self.checkBlocked('z.example.', soa='zone.rpz.')
- self.checkNotBlocked('b.example.')
- self.checkNotBlocked('c.example.')
- self.checkNotBlocked('d.example.')
- self.checkNotBlocked('e.example.')
+ self.checkCustom(
+ "a.example.", "A", dns.rrset.from_text("a.example.", 0, dns.rdataclass.IN, "A", "192.0.2.42", "192.0.2.43")
+ )
+ self.checkCustom(
+ "a.example.", "TXT", dns.rrset.from_text("a.example.", 0, dns.rdataclass.IN, "TXT", '"some text"')
+ )
+ self.checkBlocked("z.example.", soa="zone.rpz.")
+ self.checkNotBlocked("b.example.")
+ self.checkNotBlocked("c.example.")
+ self.checkNotBlocked("d.example.")
+ self.checkNotBlocked("e.example.")
# check that the policy is disabled for AD=1 queries
- self.checkNotBlocked('z.example.', True)
+ self.checkNotBlocked("z.example.", True)
# check non-custom policies
- self.checkTruncated('tc.example.', soa='zone.rpz.')
- self.checkDropped('drop.example.')
+ self.checkTruncated("tc.example.", soa="zone.rpz.")
+ self.checkDropped("drop.example.")
+
class RPZFileDefaultPolRecursorTest(RPZRecursorTest):
"""
This test makes sure that we correctly load RPZ zones from a file with a default policy
"""
- _confdir = 'RPZFileDefaultPolRecursor'
+ _confdir = "RPZFileDefaultPolRecursor"
_lua_config_file = """
rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", defpol=Policy.NoAction })
""" % (_confdir)
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
b 3600 IN A 192.0.2.42
drop 3600 IN A 192.0.2.42
e 3600 IN A 192.0.2.42
z 3600 IN A 192.0.2.42
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
a.example.zone.rpz. 60 IN A 192.0.2.42
drop.example.zone.rpz. 60 IN CNAME rpz-drop.
z.example.zone.rpz. 60 IN A 192.0.2.1
tc.example.zone.rpz. 60 IN CNAME rpz-tcp-only.
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(RPZFileDefaultPolRecursorTest, cls).generateRecursorConfig(confdir)
def testRPZ(self):
# local data entries are overridden by default
- self.checkCustom('a.example.', 'A', dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.42'))
- self.checkNoData('a.example.', 'TXT')
+ self.checkCustom("a.example.", "A", dns.rrset.from_text("a.example.", 0, dns.rdataclass.IN, "A", "192.0.2.42"))
+ self.checkNoData("a.example.", "TXT")
# will not be blocked because the default policy overrides local data entries by default
- self.checkNotBlocked('z.example.')
- self.checkNotBlocked('b.example.')
- self.checkNotBlocked('c.example.')
- self.checkNotBlocked('d.example.')
- self.checkNotBlocked('e.example.')
+ self.checkNotBlocked("z.example.")
+ self.checkNotBlocked("b.example.")
+ self.checkNotBlocked("c.example.")
+ self.checkNotBlocked("d.example.")
+ self.checkNotBlocked("e.example.")
# check non-local policies, they should be overridden by the default policy
- self.checkNXD('tc.example.', 'A')
- self.checkNotBlocked('drop.example.')
+ self.checkNXD("tc.example.", "A")
+ self.checkNotBlocked("drop.example.")
+
class RPZFileDefaultPolNotOverrideLocalRecursorTest(RPZRecursorTest):
"""
This test makes sure that we correctly load RPZ zones from a file with a default policy, not overriding local data entries
"""
- _confdir = 'RPZFileDefaultPolNotOverrideLocalRecursor'
+ _confdir = "RPZFileDefaultPolNotOverrideLocalRecursor"
_lua_config_file = """
rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", defpol=Policy.NoAction, defpolOverrideLocalData=false })
""" % (_confdir)
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
b 3600 IN A 192.0.2.42
drop 3600 IN A 192.0.2.42
e 3600 IN A 192.0.2.42
z 3600 IN A 192.0.2.42
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
a.example.zone.rpz. 60 IN A 192.0.2.42
a.example.zone.rpz. 60 IN A 192.0.2.43
drop.example.zone.rpz. 60 IN CNAME rpz-drop.
z.example.zone.rpz. 60 IN A 192.0.2.1
tc.example.zone.rpz. 60 IN CNAME rpz-tcp-only.
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(RPZFileDefaultPolNotOverrideLocalRecursorTest, cls).generateRecursorConfig(confdir)
def testRPZ(self):
# local data entries will not be overridden by the default policy
- self.checkCustom('a.example.', 'A', dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.42', '192.0.2.43'))
- self.checkCustom('a.example.', 'TXT', dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'TXT', '"some text"'))
+ self.checkCustom(
+ "a.example.", "A", dns.rrset.from_text("a.example.", 0, dns.rdataclass.IN, "A", "192.0.2.42", "192.0.2.43")
+ )
+ self.checkCustom(
+ "a.example.", "TXT", dns.rrset.from_text("a.example.", 0, dns.rdataclass.IN, "TXT", '"some text"')
+ )
# will be blocked because the default policy does not override local data entries
- self.checkBlocked('z.example.')
- self.checkNotBlocked('b.example.')
- self.checkNotBlocked('c.example.')
- self.checkNotBlocked('d.example.')
- self.checkNotBlocked('e.example.')
+ self.checkBlocked("z.example.")
+ self.checkNotBlocked("b.example.")
+ self.checkNotBlocked("c.example.")
+ self.checkNotBlocked("d.example.")
+ self.checkNotBlocked("e.example.")
# check non-local policies, they should be overridden by the default policy
- self.checkNXD('tc.example.', 'A')
- self.checkNotBlocked('drop.example.')
+ self.checkNXD("tc.example.", "A")
+ self.checkNotBlocked("drop.example.")
-class RPZSimpleAuthServer(object):
+class RPZSimpleAuthServer(object):
def __init__(self, port):
self._serverPort = port
- listener = threading.Thread(name='RPZ Simple Auth Listener', target=self._listener, args=[])
+ listener = threading.Thread(name="RPZ Simple Auth Listener", target=self._listener, args=[])
listener.daemon = True
listener.start()
response = dns.message.make_response(message)
response.flags |= dns.flags.AA
- records = [
- dns.rrset.from_text('nsip.delegated.example.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.42')
- ]
+ records = [dns.rrset.from_text("nsip.delegated.example.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.42")]
response.answer = records
return response
data, addr = sock.recvfrom(4096)
message = dns.message.from_wire(data)
if len(message.question) != 1:
- print('Invalid query, qdcount is %d' % (len(message.question)))
+ print("Invalid query, qdcount is %d" % (len(message.question)))
break
answer = self._getAnswer(message)
if not answer:
- print('Unable to get a response for %s %d' % (message.question[0].name, message.question[0].rdtype))
+ print("Unable to get a response for %s %d" % (message.question[0].name, message.question[0].rdtype))
break
wire = answer.to_wire()
sock.sendto(wire, addr)
except socket.error as e:
- print('Error in RPZ simple auth socket: %s' % str(e))
+ print("Error in RPZ simple auth socket: %s" % str(e))
+
rpzAuthServerPort = 4260
rpzAuthServer = RPZSimpleAuthServer(rpzAuthServerPort)
+
class RPZOrderingPrecedenceRecursorTest(RPZRecursorTest):
"""
This test makes sure that the recursor respects the RPZ ordering precedence rules
"""
- _confdir = 'RPZOrderingPrecedenceRecursor'
+ _confdir = "RPZOrderingPrecedenceRecursor"
_lua_config_file = """
rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
rpzFile('configs/%s/zone2.rpz', { policyName="zone2.rpz."})
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
sub.test 3600 IN A 192.0.2.42
passthru-then-blocked-by-higher 3600 IN A 192.0.2.66
passthru-then-blocked-by-same 3600 IN A 192.0.2.66
blocked-then-passhtru-by-higher 3600 IN A 192.0.2.100
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
*.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
32.66.2.0.192.rpz-ip.zone.rpz. 60 IN A 192.0.2.1
32.100.2.0.192.rpz-ip.zone.rpz. 60 IN CNAME rpz-passthru.
passthru-then-blocked-by-same.example.zone.rpz. 60 IN CNAME rpz-passthru.
32.1.0.0.127.rpz-nsip.zone.rpz. 60 IN CNAME rpz-passthru.
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- rpzFilePath = os.path.join(confdir, 'zone2.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone2.rpz.
+ rpzFilePath = os.path.join(confdir, "zone2.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone2.rpz.
@ 3600 IN SOA {soa}
sub.test.example.com.zone2.rpz. 60 IN CNAME .
passthru-then-blocked-by-higher.example.zone2.rpz. 60 IN CNAME rpz-passthru.
blocked-then-passhtru-by-higher.example.zone2.rpz. 60 IN A 192.0.2.1
32.42.2.0.192.rpz-ip 60 IN CNAME .
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(RPZOrderingPrecedenceRecursorTest, cls).generateRecursorConfig(confdir)
# we respect the order of the RPZ zones), see the pass-thru rule
# and only process RPZ rules of higher precedence.
# The subsequent rule on the content of the A should therefore not trigger a NXDOMAIN.
- self.checkNotBlocked('sub.test.example.')
+ self.checkNotBlocked("sub.test.example.")
def testRPZOrderingWhitelistedThenBlockedByHigher(self):
# we should first match on the qname from the second RPZ zone,
# continue the resolution process, and get blocked by the content of the A record
# based on the first RPZ zone, whose priority is higher than the second one.
- self.checkBlocked('passthru-then-blocked-by-higher.example.')
+ self.checkBlocked("passthru-then-blocked-by-higher.example.")
def testRPZOrderingWhitelistedThenBlockedBySame(self):
# we should first match on the qname from the first RPZ zone,
# continue the resolution process, and NOT get blocked by the content of the A record
# based on the same RPZ zone, since it's not higher.
- self.checkCustom('passthru-then-blocked-by-same.example.', 'A', dns.rrset.from_text('passthru-then-blocked-by-same.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.66'))
+ self.checkCustom(
+ "passthru-then-blocked-by-same.example.",
+ "A",
+ dns.rrset.from_text("passthru-then-blocked-by-same.example.", 0, dns.rdataclass.IN, "A", "192.0.2.66"),
+ )
def testRPZOrderBlockedThenWhitelisted(self):
# The qname is first blocked by the second RPZ zone
# This is what the RPZ specification requires, but we currently decided that we
# don't want to leak queries to malicious DNS servers and waste time if the qname is blacklisted.
# We might change our opinion at some point, though.
- self.checkBlocked('blocked-then-passhtru-by-higher.example.')
+ self.checkBlocked("blocked-then-passhtru-by-higher.example.")
def testRPZOrderDelegate(self):
# The IP of the NS we are going to contact is whitelisted (passthru) in zone 1,
# by zone 2, it should not be blocked.
# We only test once because after that the answer is cached, so the NS is not contacted
# and the whitelist is not applied (yes, NSIP and NSDNAME are brittle).
- self.checkNotBlocked('nsip.delegated.example.', singleCheck=True)
+ self.checkNotBlocked("nsip.delegated.example.", singleCheck=True)
+
class RPZNSIPCustomTest(RPZRecursorTest):
"""
This test makes sure that the recursor handles custom RPZ rules in a NSIP
"""
- _confdir = 'RPZNSIPCustom'
+ _confdir = "RPZNSIPCustom"
_lua_config_file = """
rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
rpzFile('configs/%s/zone2.rpz', { policyName="zone2.rpz."})
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
32.1.0.0.127.rpz-nsip.zone.rpz. 60 IN A 192.0.2.1
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- rpzFilePath = os.path.join(confdir, 'zone2.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone2.rpz.
+ rpzFilePath = os.path.join(confdir, "zone2.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone2.rpz.
@ 3600 IN SOA {soa}
32.1.2.0.192.rpz-ip 60 IN CNAME .
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(RPZNSIPCustomTest, cls).generateRecursorConfig(confdir)
# by zone 2, it should not be blocked.
# We only test once because after that the answer is cached, so the NS is not contacted
# and the whitelist is not applied (yes, NSIP and NSDNAME are brittle).
- self.checkCustom('nsip.delegated.example.', 'A', dns.rrset.from_text('nsip.delegated.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1'))
+ self.checkCustom(
+ "nsip.delegated.example.",
+ "A",
+ dns.rrset.from_text("nsip.delegated.example.", 0, dns.rdataclass.IN, "A", "192.0.2.1"),
+ )
class RPZResponseIPCNameChainCustomTest(RPZRecursorTest):
and resolves the target of a custom CNAME.
"""
- _confdir = 'RPZResponseIPCNameChainCustom'
+ _confdir = "RPZResponseIPCNameChainCustom"
_lua_config_file = """
rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
""" % (_confdir)
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
name IN CNAME cname
cname IN A 192.0.2.255
custom-target IN A 192.0.2.254
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
cname.example IN CNAME custom-target.example.
custom-target.example IN A 192.0.2.253
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(RPZResponseIPCNameChainCustomTest, cls).generateRecursorConfig(confdir)
# two times to check the cache
for _ in range(2):
- query = dns.message.make_query('name.example.', 'A', want_dnssec=True)
+ query = dns.message.make_query("name.example.", "A", want_dnssec=True)
query.flags |= dns.flags.CD
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
- self.assertRRsetInAnswer(res, dns.rrset.from_text('name.example.', 0, dns.rdataclass.IN, 'CNAME', 'cname.example.'))
- self.assertRRsetInAnswer(res, dns.rrset.from_text('cname.example.', 0, dns.rdataclass.IN, 'CNAME', 'custom-target.example.'))
- self.assertRRsetInAnswer(res, dns.rrset.from_text('custom-target.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.254'))
+ self.assertRRsetInAnswer(
+ res, dns.rrset.from_text("name.example.", 0, dns.rdataclass.IN, "CNAME", "cname.example.")
+ )
+ self.assertRRsetInAnswer(
+ res, dns.rrset.from_text("cname.example.", 0, dns.rdataclass.IN, "CNAME", "custom-target.example.")
+ )
+ self.assertRRsetInAnswer(
+ res, dns.rrset.from_text("custom-target.example.", 0, dns.rdataclass.IN, "A", "192.0.2.254")
+ )
class RPZCNameChainCustomTest(RPZRecursorTest):
(with QName Minimization).
"""
- _PREFIX = os.environ['PREFIX']
- _confdir = 'RPZCNameChainCustom'
+ _PREFIX = os.environ["PREFIX"]
+ _confdir = "RPZCNameChainCustom"
_lua_config_file = """
rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
""" % (_confdir)
@classmethod
def generateRecursorConfig(cls, confdir):
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
32.100.2.0.192.rpz-ip IN CNAME .
32.101.2.0.192.rpz-ip IN CNAME *.
32.102.2.0.192.rpz-ip IN A 192.0.2.103
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(RPZCNameChainCustomTest, cls).generateRecursorConfig(confdir)
# two times to check the cache
for _ in range(2):
- query = dns.message.make_query('cname-nxd.example.', 'A', want_dnssec=True)
+ query = dns.message.make_query("cname-nxd.example.", "A", want_dnssec=True)
query.flags |= dns.flags.CD
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
# two times to check the cache
for _ in range(2):
- query = dns.message.make_query('cname-nodata.example.', 'A', want_dnssec=True)
+ query = dns.message.make_query("cname-nodata.example.", "A", want_dnssec=True)
query.flags |= dns.flags.CD
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
# two times to check the cache
for _ in range(2):
- query = dns.message.make_query('cname-custom-a.example.', 'A', want_dnssec=True)
+ query = dns.message.make_query("cname-custom-a.example.", "A", want_dnssec=True)
query.flags |= dns.flags.CD
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
# the original CNAME record is signed
self.assertEqual(len(res.answer), 3)
- self.assertRRsetInAnswer(res, dns.rrset.from_text('cname-custom-a.example.', 0, dns.rdataclass.IN, 'CNAME', 'cname-custom-a-target.example.'))
- self.assertRRsetInAnswer(res, dns.rrset.from_text('cname-custom-a-target.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.103'))
+ self.assertRRsetInAnswer(
+ res,
+ dns.rrset.from_text(
+ "cname-custom-a.example.", 0, dns.rdataclass.IN, "CNAME", "cname-custom-a-target.example."
+ ),
+ )
+ self.assertRRsetInAnswer(
+ res, dns.rrset.from_text("cname-custom-a-target.example.", 0, dns.rdataclass.IN, "A", "192.0.2.103")
+ )
+
class RPZCustomDefpolTest(RPZRecursorTest):
"""
This test makes sure that the recursor applies defpol to hits and follows the CNAME
"""
- _PREFIX = os.environ['PREFIX']
- _confdir = 'RPZCustomDefpol'
+ _PREFIX = os.environ["PREFIX"]
+ _confdir = "RPZCustomDefpol"
_lua_config_file = """
rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", defpol=Policy.Custom, defcontent="a.secure.example"})
""" % (_confdir)
@classmethod
def generateRecursorConfig(cls, confdir):
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
hit.example IN CNAME .
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(RPZCustomDefpolTest, cls).generateRecursorConfig(confdir)
def testRPDefpol(self):
# two times to check the cache
for _ in range(2):
- query = dns.message.make_query('hit.example.', 'A', want_dnssec=True)
+ query = dns.message.make_query("hit.example.", "A", want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
res = sender(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
# the RPZ CNAME record is not signed
self.assertEqual(len(res.answer), 3)
- self.assertRRsetInAnswer(res, dns.rrset.from_text('hit.example.', 0, dns.rdataclass.IN, 'CNAME', 'a.secure.example.'))
- self.assertRRsetInAnswer(res, dns.rrset.from_text('a.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.20', '192.0.2.22'))
+ self.assertRRsetInAnswer(
+ res, dns.rrset.from_text("hit.example.", 0, dns.rdataclass.IN, "CNAME", "a.secure.example.")
+ )
+ self.assertRRsetInAnswer(
+ res, dns.rrset.from_text("a.secure.example.", 0, dns.rdataclass.IN, "A", "192.0.2.20", "192.0.2.22")
+ )
+
class RPZFileModByLuaRecursorTest(RPZRecursorTest):
"""
This test makes sure that we correctly load RPZ zones from a file while being modified by Lua callbacks
"""
- _confdir = 'RPZFileModByLuaRecursor'
+ _confdir = "RPZFileModByLuaRecursor"
_lua_dns_script_file = """
function preresolve(dq)
if dq.qname:equal('zmod.example.') then
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
b 3600 IN A 192.0.2.42
d 3600 IN A 192.0.2.42
e 3600 IN A 192.0.2.42
z 3600 IN A 192.0.2.42
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
- rpzFilePath = os.path.join(confdir, 'zone.rpz')
- with open(rpzFilePath, 'w') as rpzZone:
- rpzZone.write("""$ORIGIN zone.rpz.
+ rpzFilePath = os.path.join(confdir, "zone.rpz")
+ with open(rpzFilePath, "w") as rpzZone:
+ rpzZone.write(
+ """$ORIGIN zone.rpz.
@ 3600 IN SOA {soa}
a.example.zone.rpz. 60 IN A 192.0.2.42
a.example.zone.rpz. 60 IN A 192.0.2.43
tc.example.zone.rpz. 60 IN CNAME rpz-tcp-only.
nxmod.example.zone.rpz. 60 in CNAME .
nodatamod.example.zone.rpz. 60 in CNAME *.
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(RPZFileModByLuaRecursorTest, cls).generateRecursorConfig(confdir)
def testRPZ(self):
- self.checkCustom('a.example.', 'A', dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.42', '192.0.2.43'))
- self.checkCustom('a.example.', 'TXT', dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'TXT', '"some text"'))
- self.checkDropped('zmod.example.')
- self.checkDropped('nxmod.example.')
- self.checkDropped('nodatamod.example.')
- self.checkNotBlocked('b.example.')
- self.checkNotBlocked('c.example.')
- self.checkNotBlocked('d.example.')
- self.checkNotBlocked('e.example.')
+ self.checkCustom(
+ "a.example.", "A", dns.rrset.from_text("a.example.", 0, dns.rdataclass.IN, "A", "192.0.2.42", "192.0.2.43")
+ )
+ self.checkCustom(
+ "a.example.", "TXT", dns.rrset.from_text("a.example.", 0, dns.rdataclass.IN, "TXT", '"some text"')
+ )
+ self.checkDropped("zmod.example.")
+ self.checkDropped("nxmod.example.")
+ self.checkDropped("nodatamod.example.")
+ self.checkNotBlocked("b.example.")
+ self.checkNotBlocked("c.example.")
+ self.checkNotBlocked("d.example.")
+ self.checkNotBlocked("e.example.")
# check non-custom policies
- self.checkTruncated('tc.example.')
- self.checkDropped('drop.example.')
+ self.checkTruncated("tc.example.")
+ self.checkDropped("drop.example.")
from recursortests import RecursorTest
-class BadRPZServer(object):
+class BadRPZServer(object):
def __init__(self, port):
self._currentSerial = 0
self._targetSerial = 1
self._serverPort = port
- listener = threading.Thread(name='RPZ Listener', target=self._listener, args=[])
+ listener = threading.Thread(name="RPZ Listener", target=self._listener, args=[])
listener.daemon = True
listener.start()
if newSerial == self._currentSerial:
return False
- #if newSerial != self._currentSerial + 1:
+ # if newSerial != self._currentSerial + 1:
# raise AssertionError("Asking the RPZ server to serve serial %d, already serving %d" % (newSerial, self._currentSerial))
self._targetSerial = newSerial
return True
if message.question[0].rdtype == dns.rdatatype.AXFR:
if self._currentSerial != 0:
- print('Received an AXFR query but IXFR expected because the current serial is %d' % (self._currentSerial))
+ print(
+ "Received an AXFR query but IXFR expected because the current serial is %d" % (self._currentSerial)
+ )
return (None, self._currentSerial)
newSerial = self._targetSerial
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('a.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("a.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ ]
elif message.question[0].rdtype == dns.rdatatype.IXFR:
oldSerial = message.authority[0][0].serial
newSerial = self._targetSerial
if newSerial == 2:
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % oldSerial,
+ ),
# no deletion
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('b.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("b.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ ]
elif newSerial == 3:
records = [
- dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
- dns.rrset.from_text('a.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
- ]
+ dns.rrset.from_text(
+ "zone.rpz.",
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.SOA,
+ "ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1" % newSerial,
+ ),
+ dns.rrset.from_text("a.example.zone.rpz.", 60, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1"),
+ ]
response.answer = records
return (newSerial, response)
message = dns.message.from_wire(data)
if len(message.question) != 1:
- print('Invalid RPZ query, qdcount is %d' % (len(message.question)), file=sys.stderr)
+ print("Invalid RPZ query, qdcount is %d" % (len(message.question)), file=sys.stderr)
break
if not message.question[0].rdtype in [dns.rdatatype.AXFR, dns.rdatatype.IXFR]:
- print('Invalid RPZ query, qtype is %d' % (message.question.rdtype), file=sys.stderr)
+ print("Invalid RPZ query, qtype is %d" % (message.question.rdtype), file=sys.stderr)
break
(serial, answer) = self._getAnswer(message)
if not answer:
- print('Unable to get a response for %s %d' % (message.question[0].name, message.question[0].rdtype), file=sys.stderr)
+ print(
+ "Unable to get a response for %s %d" % (message.question[0].name, message.question[0].rdtype),
+ file=sys.stderr,
+ )
break
wire = answer.to_wire()
while True:
try:
(conn, _) = sock.accept()
- thread = threading.Thread(name='RPZ Connection Handler',
- target=self._connectionHandler,
- args=[conn])
+ thread = threading.Thread(name="RPZ Connection Handler", target=self._connectionHandler, args=[conn])
thread.daemon = True
thread.start()
except socket.error as e:
- print('Error in RPZ socket: %s' % str(e))
+ print("Error in RPZ socket: %s" % str(e))
sock.close()
+
class RPZIncompleteRecursorTest(RecursorTest):
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
- _confdir = 'RPZIncompleteRecursor'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
+ _confdir = "RPZIncompleteRecursor"
_config_template = """
auth-zones=example=configs/%s/example.zone
""" % (_confdir, _wsPort, _wsPassword, _apiKey)
def checkRPZStats(self, serial, recordsCount, fullXFRCount, totalXFRCount, failedXFRCount):
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/rpzstatistics'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/api/v1/servers/localhost/rpzstatistics"
r = requests.get(url, headers=headers, timeout=self._wsTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
- self.assertIn('zone.rpz.', content)
- zone = content['zone.rpz.']
- for key in ['last_update', 'records', 'serial', 'transfers_failed', 'transfers_full', 'transfers_success']:
+ self.assertIn("zone.rpz.", content)
+ zone = content["zone.rpz."]
+ for key in ["last_update", "records", "serial", "transfers_failed", "transfers_full", "transfers_success"]:
self.assertIn(key, zone)
- self.assertEqual(zone['serial'], serial)
- self.assertEqual(zone['records'], recordsCount)
- self.assertEqual(zone['transfers_full'], fullXFRCount)
- self.assertEqual(zone['transfers_success'], totalXFRCount)
- self.assertEqual(zone['transfers_failed'], failedXFRCount)
+ self.assertEqual(zone["serial"], serial)
+ self.assertEqual(zone["records"], recordsCount)
+ self.assertEqual(zone["transfers_full"], fullXFRCount)
+ self.assertEqual(zone["transfers_success"], totalXFRCount)
+ self.assertEqual(zone["transfers_failed"], failedXFRCount)
+
badrpzServerPort = 4251
badrpzServer = BadRPZServer(badrpzServerPort)
+
class RPZXFRIncompleteRecursorTest(RPZIncompleteRecursorTest):
"""
This test makes sure that we correctly detect incomplete RPZ zones via AXFR then IXFR
-- The first server is a bogus one, to test that we correctly fail over to the second one
rpzPrimary({'127.0.0.1:9999', '127.0.0.1:%d'}, 'zone.rpz.', { refresh=1 })
""" % (badrpzServerPort)
- _confdir = 'RPZXFRIncompleteRecursor'
+ _confdir = "RPZXFRIncompleteRecursor"
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
auth-zones=example=configs/%s/example.zone
webserver=yes
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
b 3600 IN A 192.0.2.42
c 3600 IN A 192.0.2.42
d 3600 IN A 192.0.2.42
e 3600 IN A 192.0.2.42
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(RPZXFRIncompleteRecursorTest, cls).generateRecursorConfig(confdir)
def waitUntilCorrectSerialIsLoaded(self, serial, timeout=5):
attempts = attempts + 1
time.sleep(1)
- raise AssertionError("Waited %d seconds for the serial to be updated to %d but the serial is still %d" % (timeout, serial, currentSerial))
+ raise AssertionError(
+ "Waited %d seconds for the serial to be updated to %d but the serial is still %d"
+ % (timeout, serial, currentSerial)
+ )
def testRPZ(self):
self.waitForTCPSocket("127.0.0.1", self._wsPort)
# First zone
self.waitUntilCorrectSerialIsLoaded(1)
- self.checkRPZStats(1, 1, 1, 1, 1) # failure count includes a port 9999 attempt
+ self.checkRPZStats(1, 1, 1, 1, 1) # failure count includes a port 9999 attempt
# second zone, should fail, incomplete IXFR
self.waitUntilCorrectSerialIsLoaded(2)
# third zone, should fail, incomplete AXFR
self.waitUntilCorrectSerialIsLoaded(3)
self.checkRPZStats(1, 1, 1, 1, 5)
-
class ReadTrustAnchorsFromFileTest(RecursorTest):
- _confdir = 'ReadTrustAnchorsFromFile'
+ _confdir = "ReadTrustAnchorsFromFile"
_config_template = """dnssec=validate"""
_lua_config_file = """clearTA()
def testCorrectFile(self):
"""Ensure the file is read correctly"""
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'get-tas']
+ rec_controlCmd = [os.environ["RECCONTROL"], "--config-dir=%s" % "configs/" + self._confdir, "get-tas"]
expected = b"""Configured Trust Anchors:
.
\t\t36914 13 2 c94ed457ff79afe03804c26ce4fa832687db92bc231aff98617791fc71a65870
except subprocess.CalledProcessError as e:
print(e.output)
raise
-
def checkDnstapBase(testinstance, dnstap, protocol, initiator, responder, response_port=53):
testinstance.assertTrue(dnstap)
- testinstance.assertTrue(dnstap.HasField('identity'))
- #testinstance.assertEqual(dnstap.identity, b'a.server')
- testinstance.assertTrue(dnstap.HasField('version'))
- #testinstance.assertIn(b'dnsdist ', dnstap.version)
- testinstance.assertTrue(dnstap.HasField('type'))
+ testinstance.assertTrue(dnstap.HasField("identity"))
+ # testinstance.assertEqual(dnstap.identity, b'a.server')
+ testinstance.assertTrue(dnstap.HasField("version"))
+ # testinstance.assertIn(b'dnsdist ', dnstap.version)
+ testinstance.assertTrue(dnstap.HasField("type"))
testinstance.assertEqual(dnstap.type, dnstap.MESSAGE)
- testinstance.assertTrue(dnstap.HasField('message'))
- testinstance.assertTrue(dnstap.message.HasField('socket_protocol'))
+ testinstance.assertTrue(dnstap.HasField("message"))
+ testinstance.assertTrue(dnstap.message.HasField("socket_protocol"))
testinstance.assertEqual(dnstap.message.socket_protocol, protocol)
- testinstance.assertTrue(dnstap.message.HasField('socket_family'))
+ testinstance.assertTrue(dnstap.message.HasField("socket_family"))
testinstance.assertEqual(dnstap.message.socket_family, dnstap_pb2.INET)
#
# The query address and port are from the recursor, we don't know the port
#
- testinstance.assertTrue(dnstap.message.HasField('query_address'))
+ testinstance.assertTrue(dnstap.message.HasField("query_address"))
testinstance.assertEqual(socket.inet_ntop(socket.AF_INET, dnstap.message.query_address), initiator)
- testinstance.assertTrue(dnstap.message.HasField('query_port'))
- testinstance.assertTrue(dnstap.message.HasField('response_address'))
+ testinstance.assertTrue(dnstap.message.HasField("query_port"))
+ testinstance.assertTrue(dnstap.message.HasField("response_address"))
testinstance.assertEqual(socket.inet_ntop(socket.AF_INET, dnstap.message.response_address), responder)
- testinstance.assertTrue(dnstap.message.HasField('response_port'))
+ testinstance.assertTrue(dnstap.message.HasField("response_port"))
testinstance.assertEqual(dnstap.message.response_port, response_port)
testinstance.assertEqual(dnstap.message.type, dnstap_pb2.Message.RESOLVER_QUERY)
checkDnstapBase(testinstance, dnstap, protocol, initiator, responder)
- testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
- testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_sec"))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_nsec"))
- testinstance.assertTrue(dnstap.message.HasField('query_message'))
+ testinstance.assertTrue(dnstap.message.HasField("query_message"))
#
# We cannot compare the incoming query with the outgoing one
# The IDs and some other fields will be different
#
- #wire_message = dns.message.from_wire(dnstap.message.query_message)
- #testinstance.assertEqual(wire_message, query)
+ # wire_message = dns.message.from_wire(dnstap.message.query_message)
+ # testinstance.assertEqual(wire_message, query)
+
def checkDnstapNOD(testinstance, dnstap, protocol, initiator, responder, response_port, query_zone):
testinstance.assertEqual(dnstap.message.type, dnstap_pb2.Message.CLIENT_QUERY)
checkDnstapBase(testinstance, dnstap, protocol, initiator, responder, response_port)
- testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
- testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_sec"))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_nsec"))
- testinstance.assertTrue(dnstap.message.HasField('query_zone'))
+ testinstance.assertTrue(dnstap.message.HasField("query_zone"))
testinstance.assertEqual(dns.name.from_wire(dnstap.message.query_zone, 0)[0].to_text(), query_zone)
+
def checkDnstapUDR(testinstance, dnstap, protocol, initiator, responder, response_port, query_zone):
testinstance.assertEqual(dnstap.message.type, dnstap_pb2.Message.RESOLVER_RESPONSE)
checkDnstapBase(testinstance, dnstap, protocol, initiator, responder, response_port)
- testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
- testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_sec"))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_nsec"))
- testinstance.assertTrue(dnstap.message.HasField('query_zone'))
+ testinstance.assertTrue(dnstap.message.HasField("query_zone"))
testinstance.assertEqual(dns.name.from_wire(dnstap.message.query_zone, 0)[0].to_text(), query_zone)
- testinstance.assertTrue(dnstap.message.HasField('response_message'))
+ testinstance.assertTrue(dnstap.message.HasField("response_message"))
wire_message = dns.message.from_wire(dnstap.message.response_message)
testinstance.assertTrue(isinstance(wire_message, dns.message.Message))
+
def checkDnstapExtra(testinstance, dnstap, expected):
- testinstance.assertTrue(dnstap.HasField('extra'))
+ testinstance.assertTrue(dnstap.HasField("extra"))
testinstance.assertEqual(dnstap.extra, expected)
def checkDnstapNoExtra(testinstance, dnstap):
- testinstance.assertFalse(dnstap.HasField('extra'))
+ testinstance.assertFalse(dnstap.HasField("extra"))
def checkDnstapResponse(testinstance, dnstap, protocol, response, initiator, responder):
testinstance.assertEqual(dnstap.message.type, dnstap_pb2.Message.RESOLVER_RESPONSE)
checkDnstapBase(testinstance, dnstap, protocol, initiator, responder)
- testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
- testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_sec"))
+ testinstance.assertTrue(dnstap.message.HasField("query_time_nsec"))
- testinstance.assertTrue(dnstap.message.HasField('response_time_sec'))
- testinstance.assertTrue(dnstap.message.HasField('response_time_nsec'))
+ testinstance.assertTrue(dnstap.message.HasField("response_time_sec"))
+ testinstance.assertTrue(dnstap.message.HasField("response_time_nsec"))
- testinstance.assertTrue(dnstap.message.response_time_sec > dnstap.message.query_time_sec or \
- dnstap.message.response_time_nsec > dnstap.message.query_time_nsec)
+ testinstance.assertTrue(
+ dnstap.message.response_time_sec > dnstap.message.query_time_sec
+ or dnstap.message.response_time_nsec > dnstap.message.query_time_nsec
+ )
- testinstance.assertTrue(dnstap.message.HasField('response_message'))
+ testinstance.assertTrue(dnstap.message.HasField("response_message"))
wire_message = dns.message.from_wire(dnstap.message.response_message)
testinstance.assertEqual(wire_message, response)
+
def fstrm_get_control_frame_type(data):
(t,) = struct.unpack("!L", data[0:4])
return t
def fstrm_make_control_frame_reply(cft):
if cft == FSTRM_CONTROL_READY:
# Reply with ACCEPT frame and content-type
- contenttype = b'protobuf:dnstap.Dnstap'
- frame = struct.pack('!LLL', FSTRM_CONTROL_ACCEPT, 1,
- len(contenttype)) + contenttype
+ contenttype = b"protobuf:dnstap.Dnstap"
+ frame = struct.pack("!LLL", FSTRM_CONTROL_ACCEPT, 1, len(contenttype)) + contenttype
buf = struct.pack("!LL", 0, len(frame)) + frame
return buf
elif cft == FSTRM_CONTROL_START:
return None
else:
- raise Exception('unhandled control frame ' + str(cft))
+ raise Exception("unhandled control frame " + str(cft))
def fstrm_read_and_dispatch_control_frame(conn):
data = conn.recv(4)
if not data:
- raise Exception('length of control frame payload could not be read')
+ raise Exception("length of control frame payload could not be read")
(datalen,) = struct.unpack("!L", data)
data = conn.recv(datalen)
cft = fstrm_get_control_frame_type(data)
on_data(data)
-
class DNSTapServerParams(object):
def __init__(self, path):
self.queue = Queue()
DNSTapServerParameters = DNSTapServerParams("/tmp/dnstap.sock")
DNSTapListeners = []
+
class TestRecursorDNSTap(RecursorTest):
@classmethod
def FrameStreamUnixListener(cls, conn, param):
while True:
try:
- fstrm_handle_bidir_connection(conn, lambda data: \
- param.queue.put(data, True, timeout=2.0))
+ fstrm_handle_bidir_connection(conn, lambda data: param.queue.put(data, True, timeout=2.0))
except socket.error as e:
if e.errno in (errno.EBADF, errno.EPIPE):
break
while True:
try:
(conn, addr) = sock.accept()
- listener = threading.Thread(name='DNSTap Worker', target=cls.FrameStreamUnixListener, args=[conn, param])
+ listener = threading.Thread(
+ name="DNSTap Worker", target=cls.FrameStreamUnixListener, args=[conn, param]
+ )
listener.daemon = True
listener.start()
except socket.error as e:
sock.close()
@classmethod
- @pytest.mark.skipif('dnstap-framestream' not in RecursorTest.recFeatures(), reason='dnstap feature not available')
+ @pytest.mark.skipif("dnstap-framestream" not in RecursorTest.recFeatures(), reason="dnstap feature not available")
def setUpClass(cls):
cls.setUpSockets()
cls.startResponders()
- listener = threading.Thread(name='DNSTap Listener', target=cls.FrameStreamUnixListenerMain, args=[DNSTapServerParameters])
+ listener = threading.Thread(
+ name="DNSTap Listener", target=cls.FrameStreamUnixListenerMain, args=[DNSTapServerParameters]
+ )
listener.daemon = True
listener.start()
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
cls.createConfigDir(confdir)
cls.generateRecursorConfig(confdir)
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'example.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN example.
+ authzonepath = os.path.join(confdir, "example.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN example.
@ 3600 IN SOA {soa}
a 3600 IN A 192.0.2.42
tagged 3600 IN A 192.0.2.84
types 3600 IN SRV 10 20 443 a.example.
cname 3600 IN CNAME a.example.
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(TestRecursorDNSTap, cls).generateRecursorConfig(confdir)
@classmethod
dnstap.ParseFromString(data)
return dnstap
+
class DNSTapDefaultTest(TestRecursorDNSTap):
"""
This test makes sure that we correctly export outgoing queries over DNSTap.
that the recursor at least connects to the DNSTap server.
"""
- _confdir = 'DNSTapDefault'
- _config_template = """
-auth-zones=example=configs/%s/example.zone""" % _confdir
- _lua_config_file = """
+ _confdir = "DNSTapDefault"
+ _config_template = (
+ """
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
+ _lua_config_file = (
+ """
dnstapFrameStreamServer({"%s"})
- """ % DNSTapServerParameters.path
+ """
+ % DNSTapServerParameters.path
+ )
def testA(self):
- name = 'www.example.org.'
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "www.example.org."
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.RD
res = self.sendUDPQuery(query)
self.assertNotEqual(res, None)
# check the dnstap message corresponding to the UDP query
dnstap = self.getFirstDnstap()
- checkDnstapQuery(self, dnstap, dnstap_pb2.UDP, '127.0.0.1', '127.0.0.8')
+ checkDnstapQuery(self, dnstap, dnstap_pb2.UDP, "127.0.0.1", "127.0.0.8")
# We don't expect a response
checkDnstapNoExtra(self, dnstap)
# We don't expect anything more, but we'll sleep anyway to avoid a LeakSanitizer race
time.sleep(1)
-class DNSTapLogNoQueriesTest(TestRecursorDNSTap):
- _confdir = 'DNSTapLogNoQueries'
- _config_template = """
-auth-zones=example=configs/%s/example.zone""" % _confdir
+class DNSTapLogNoQueriesTest(TestRecursorDNSTap):
+ _confdir = "DNSTapLogNoQueries"
+ _config_template = (
+ """
+auth-zones=example=configs/%s/example.zone"""
+ % _confdir
+ )
_lua_config_file = """
dnstapFrameStreamServer({"%s"}, {logQueries=false})
""" % (DNSTapServerParameters.path)
def testA(self):
- name = 'www.example.org.'
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "www.example.org."
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.RD
res = self.sendUDPQuery(query)
self.assertNotEqual(res, None)
time.sleep(1)
self.assertTrue(DNSTapServerParameters.queue.empty())
+
class DNSTapLogNODTest(TestRecursorDNSTap):
"""
This test makes sure that we correctly export outgoing queries over DNSTap.
that the recursor at least connects to the DNSTap server.
"""
- _confdir = 'DNSTapLogNOD'
+ _confdir = "DNSTapLogNOD"
_config_template = """
new-domain-tracking=yes
new-domain-history-dir=configs/%s/nod
@classmethod
def generateRecursorConfig(cls, confdir):
for directory in ["nod", "udr"]:
- path = os.path.join('configs', cls._confdir, directory)
+ path = os.path.join("configs", cls._confdir, directory)
cls.createConfigDir(path)
super(DNSTapLogNODTest, cls).generateRecursorConfig(confdir)
def testA(self):
- name = 'types.example.'
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "types.example."
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.RD
res = self.sendUDPQuery(query)
self.assertNotEqual(res, None)
# check the dnstap message corresponding to the UDP query
dnstap = self.getFirstDnstap()
- checkDnstapNOD(self, dnstap, dnstap_pb2.UDP, '127.0.0.1', '127.0.0.1', 5300, name)
+ checkDnstapNOD(self, dnstap, dnstap_pb2.UDP, "127.0.0.1", "127.0.0.1", 5300, name)
# We don't expect a response
checkDnstapNoExtra(self, dnstap)
# We don't expect anything more
time.sleep(1)
self.assertTrue(DNSTapServerParameters.queue.empty())
-class DNSTapLogUDRTest(TestRecursorDNSTap):
- _confdir = 'DNSTapLogUDR'
+class DNSTapLogUDRTest(TestRecursorDNSTap):
+ _confdir = "DNSTapLogUDR"
_config_template = """
new-domain-tracking=yes
new-domain-history-dir=configs/%s/nod
@classmethod
def generateRecursorConfig(cls, confdir):
for directory in ["nod", "udr"]:
- path = os.path.join('configs', cls._confdir, directory)
+ path = os.path.join("configs", cls._confdir, directory)
cls.createConfigDir(path)
super(DNSTapLogUDRTest, cls).generateRecursorConfig(confdir)
def testA(self):
- name = 'types.example.'
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "types.example."
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.RD
res = self.sendUDPQuery(query)
self.assertNotEqual(res, None)
# check the dnstap message corresponding to the UDP query
dnstap = self.getFirstDnstap()
- checkDnstapUDR(self, dnstap, dnstap_pb2.UDP, '127.0.0.1', '127.0.0.1', 5300, name)
+ checkDnstapUDR(self, dnstap, dnstap_pb2.UDP, "127.0.0.1", "127.0.0.1", 5300, name)
# We don't expect a rpasesponse
checkDnstapNoExtra(self, dnstap)
# We don't expect anything more
time.sleep(1)
self.assertTrue(DNSTapServerParameters.queue.empty())
-class DNSTapLogNODUDRTest(TestRecursorDNSTap):
- _confdir = 'DNSTapLogNODUDR'
+class DNSTapLogNODUDRTest(TestRecursorDNSTap):
+ _confdir = "DNSTapLogNODUDR"
_config_template = """
new-domain-tracking=yes
new-domain-history-dir=configs/%s/nod
@classmethod
def generateRecursorConfig(cls, confdir):
for directory in ["nod", "udr"]:
- path = os.path.join('configs', cls._confdir, directory)
+ path = os.path.join("configs", cls._confdir, directory)
cls.createConfigDir(path)
super(DNSTapLogNODUDRTest, cls).generateRecursorConfig(confdir)
def testA(self):
- name = 'types.example.'
- query = dns.message.make_query(name, 'A', want_dnssec=True)
+ name = "types.example."
+ query = dns.message.make_query(name, "A", want_dnssec=True)
query.flags |= dns.flags.RD
res = self.sendUDPQuery(query)
self.assertNotEqual(res, None)
dnstap = self.getFirstDnstap()
- checkDnstapUDR(self, dnstap, dnstap_pb2.UDP, '127.0.0.1', '127.0.0.1', 5300, name)
+ checkDnstapUDR(self, dnstap, dnstap_pb2.UDP, "127.0.0.1", "127.0.0.1", 5300, name)
dnstap = self.getFirstDnstap()
- checkDnstapNOD(self, dnstap, dnstap_pb2.UDP, '127.0.0.1', '127.0.0.1', 5300, name)
+ checkDnstapNOD(self, dnstap, dnstap_pb2.UDP, "127.0.0.1", "127.0.0.1", 5300, name)
checkDnstapNoExtra(self, dnstap)
# We don't expect anything more
from recursortests import RecursorTest
-class RootNXTrustRecursorTest(RecursorTest):
+class RootNXTrustRecursorTest(RecursorTest):
def getOutgoingQueriesCount(self):
- headers = {'x-api-key': self._apiKey}
- url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+ headers = {"x-api-key": self._apiKey}
+ url = "http://127.0.0.1:" + str(self._wsPort) + "/api/v1/servers/localhost/statistics"
r = requests.get(url, headers=headers, timeout=self._wsTimeout)
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
self.assertTrue(r.json())
content = r.json()
for entry in content:
- if entry['name'] == 'all-outqueries':
- return int(entry['value'])
+ if entry["name"] == "all-outqueries":
+ return int(entry["value"])
return 0
# Code below is inherently racey, but better than a fixed sleep
def waitForOutgoingToStabilize(self):
for count in range(20):
- outgoing1 = self.getOutgoingQueriesCount();
- time.sleep(0.1);
- outgoing2 = self.getOutgoingQueriesCount();
+ outgoing1 = self.getOutgoingQueriesCount()
+ time.sleep(0.1)
+ outgoing2 = self.getOutgoingQueriesCount()
if outgoing1 == outgoing2:
break
+
class RootNXTrustDisabledTest(RootNXTrustRecursorTest):
- _confdir = 'RootNXTrustDisabled'
+ _confdir = "RootNXTrustDisabled"
_auth_zones = RecursorTest._default_auth_zones
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
root-nx-trust=no
self.waitForOutgoingToStabilize()
# First query nx.example.
before = self.getOutgoingQueriesCount()
- query = dns.message.make_query('www.nx-example.', 'A')
+ query = dns.message.make_query("www.nx-example.", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
# then query nx2.example.
before = after
- query = dns.message.make_query('www2.nx-example.', 'A', use_edns=True)
+ query = dns.message.make_query("www2.nx-example.", "A", use_edns=True)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 0)
+
class RootNXTrustEnabledTest(RootNXTrustRecursorTest):
- _confdir = 'RootNXTrustEnabled'
+ _confdir = "RootNXTrustEnabled"
_auth_zones = RecursorTest._default_auth_zones
_wsPort = 8042
_wsTimeout = 2
- _wsPassword = 'secretpassword'
- _apiKey = 'secretapikey'
+ _wsPassword = "secretpassword"
+ _apiKey = "secretapikey"
_config_template = """
root-nx-trust=yes
self.waitForOutgoingToStabilize()
# first query nx.example.
before = self.getOutgoingQueriesCount()
- query = dns.message.make_query('www.nx-example.', 'A')
+ query = dns.message.make_query("www.nx-example.", "A")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
# then query nx2.example.
before = after
- query = dns.message.make_query('www2.nx-example.', 'A', use_edns=True)
+ query = dns.message.make_query("www2.nx-example.", "A", use_edns=True)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertEqual(res.edns, 0)
self.assertEqual(len(res.options), 1)
self.assertEqual(res.options[0].otype, 15)
- self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(29, b'Result synthesized by root-nx-trust'))
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(29, b"Result synthesized by root-nx-trust"))
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
-emptyECSText = 'No ECS received'
-nameECS = 'ecs-echo.example.'
-nameECSInvalidScope = 'invalid-scope.ecs-echo.example.'
+emptyECSText = "No ECS received"
+nameECS = "ecs-echo.example."
+nameECSInvalidScope = "invalid-scope.ecs-echo.example."
ttlECS = 60
routingReactorRunning = False
+
class RoutingTagTest(RecursorTest):
_config_template_default = """
daemon=no
def setRoutingTag(self, tag):
# This value is picked up by the gettag()
- with open('tagfile', 'w') as file:
+ with open("tagfile", "w") as file:
if tag:
file.write(tag)
global routingReactorRunning
print("Launching responders..")
- address = cls._PREFIX + '.24'
+ address = cls._PREFIX + ".24"
port = 53
if not routingReactorRunning:
@classmethod
def tearDownClass(cls):
- os.unlink('tagfile')
+ os.unlink("tagfile")
super().tearDownClass()
+
class RoutingTagTest(RoutingTagTest):
- _confdir = 'RoutingTag'
+ _confdir = "RoutingTag"
_config_template = """
use-incoming-edns-subnet=yes
edns-subnet-allow-list=ecs-echo.example.
forward-zones=ecs-echo.example=%s.24
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
_lua_dns_script_file = """
function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp, proxyProtocolValues)
def testSendECS(self):
# First send an ECS query with routingTag
- self.setRoutingTag('foo')
- expected1 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.0/24')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ self.setRoutingTag("foo")
+ expected1 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "192.0.2.0/24")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected1)
# Now check a cache hit with the same routingTag (but no ECS)
- query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ query = dns.message.make_query(nameECS, "TXT", "IN")
self.checkECSQueryHit(query, expected1)
- expected2 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
+ expected2 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/24")
# And see if a different tag does *not* hit the first one
- self.setRoutingTag('bar')
- query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.setRoutingTag("bar")
+ query = dns.message.make_query(nameECS, "TXT", "IN")
self.sendECSQuery(query, expected2)
# And see if a *no* tag does *not* hit the first one
- expected3 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.3.0/24')
+ expected3 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "192.0.3.0/24")
self.setRoutingTag(None)
- ecso = clientsubnetoption.ClientSubnetOption('192.0.3.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.3.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected3)
# And see if an unknown tag from the same subnet does hit the last
- self.setRoutingTag('baz')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.3.2', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ self.setRoutingTag("baz")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.3.2", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.checkECSQueryHit(query, expected3)
# And a no tag and no subnet query does hit the general case
self.setRoutingTag(None)
- query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ query = dns.message.make_query(nameECS, "TXT", "IN")
self.sendECSQuery(query, expected2)
# And an unknown tag and no subnet query does hit the general case
- self.setRoutingTag('bag')
- query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.setRoutingTag("bag")
+ query = dns.message.make_query(nameECS, "TXT", "IN")
self.sendECSQuery(query, expected2)
- if not "PEEK_AT_CACHE" in os.environ: # set to peek at cache
+ if not "PEEK_AT_CACHE" in os.environ: # set to peek at cache
return
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'dump-cache', 'x']
+ rec_controlCmd = [os.environ["RECCONTROL"], "--config-dir=%s" % "configs/" + self._confdir, "dump-cache", "x"]
try:
- expected = b'dumped 7 records\n'
+ expected = b"dumped 7 records\n"
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
self.assertEqual(ret, expected)
print(e.output)
raise
+
class RoutingTagFFITest(RoutingTagTest):
- _confdir = 'RoutingTagFFI'
+ _confdir = "RoutingTagFFI"
_config_template = """
use-incoming-edns-subnet=yes
edns-subnet-allow-list=ecs-echo.example.
forward-zones=ecs-echo.example=%s.24
- """ % (os.environ['PREFIX'])
+ """ % (os.environ["PREFIX"])
_lua_dns_script_file = """
local ffi = require("ffi")
return 0
end
"""
+
def testSendECS(self):
# First send an ECS query with routingTag
- self.setRoutingTag('foo')
- expected1 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.0/24')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ self.setRoutingTag("foo")
+ expected1 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "192.0.2.0/24")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.2.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected1)
# Now check a cache hit with the same routingTag (but no ECS)
- query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ query = dns.message.make_query(nameECS, "TXT", "IN")
self.checkECSQueryHit(query, expected1)
- expected2 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
+ expected2 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "127.0.0.0/24")
# And see if a different tag does *not* hit the first one
- self.setRoutingTag('bar')
- query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.setRoutingTag("bar")
+ query = dns.message.make_query(nameECS, "TXT", "IN")
self.sendECSQuery(query, expected2)
# And see if a *no* tag does *not* hit the first one
- expected3 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.3.0/24')
+ expected3 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "TXT", "192.0.3.0/24")
self.setRoutingTag(None)
- ecso = clientsubnetoption.ClientSubnetOption('192.0.3.1', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.3.1", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected3)
# And see if an unknown tag from the same subnet does hit the last
- self.setRoutingTag('baz')
- ecso = clientsubnetoption.ClientSubnetOption('192.0.3.2', 32)
- query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ self.setRoutingTag("baz")
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.3.2", 32)
+ query = dns.message.make_query(nameECS, "TXT", "IN", use_edns=True, options=[ecso], payload=512)
self.checkECSQueryHit(query, expected3)
# And a no tag and no subnet query does hit the general case
self.setRoutingTag(None)
- query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ query = dns.message.make_query(nameECS, "TXT", "IN")
self.sendECSQuery(query, expected2)
# And an unknown tag and no subnet query does hit the general case
- self.setRoutingTag('bag')
- query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.setRoutingTag("bag")
+ query = dns.message.make_query(nameECS, "TXT", "IN")
self.sendECSQuery(query, expected2)
- if not "PEEK_AT_CACHE" in os.environ: # set to peek at cache
+ if not "PEEK_AT_CACHE" in os.environ: # set to peek at cache
return
- rec_controlCmd = [os.environ['RECCONTROL'],
- '--config-dir=%s' % 'configs/' + self._confdir,
- 'dump-cache y']
+ rec_controlCmd = [os.environ["RECCONTROL"], "--config-dir=%s" % "configs/" + self._confdir, "dump-cache y"]
try:
- expected = 'dumped 6 records\n'
+ expected = "dumped 6 records\n"
ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
self.assertEqual(ret, expected)
print(e.output)
raise
+
class UDPRoutingResponder(DatagramProtocol):
@staticmethod
def ipToStr(option):
if option.family == clientsubnetoption.FAMILY_IPV4:
- ip = socket.inet_ntop(socket.AF_INET, struct.pack('!L', option.ip))
+ ip = socket.inet_ntop(socket.AF_INET, struct.pack("!L", option.ip))
elif option.family == clientsubnetoption.FAMILY_IPV6:
- ip = socket.inet_ntop(socket.AF_INET6,
- struct.pack('!QQ',
- option.ip >> 64,
- option.ip & (2 ** 64 - 1)))
+ ip = socket.inet_ntop(socket.AF_INET6, struct.pack("!QQ", option.ip >> 64, option.ip & (2**64 - 1)))
return ip
def datagramReceived(self, datagram, address):
response.flags |= dns.flags.AA
ecso = None
- if (request.question[0].name == dns.name.from_text(nameECS) or request.question[0].name == dns.name.from_text(nameECSInvalidScope)) and request.question[0].rdtype == dns.rdatatype.TXT:
-
+ if (
+ request.question[0].name == dns.name.from_text(nameECS)
+ or request.question[0].name == dns.name.from_text(nameECSInvalidScope)
+ ) and request.question[0].rdtype == dns.rdatatype.TXT:
text = emptyECSText
for option in request.options:
- if option.otype == clientsubnetoption.ASSIGNED_OPTION_CODE and isinstance(option, clientsubnetoption.ClientSubnetOption):
- text = self.ipToStr(option) + '/' + str(option.mask)
+ if option.otype == clientsubnetoption.ASSIGNED_OPTION_CODE and isinstance(
+ option, clientsubnetoption.ClientSubnetOption
+ ):
+ text = self.ipToStr(option) + "/" + str(option.mask)
# Send a scope more specific than the received source for nameECSInvalidScope
if request.question[0].name == dns.name.from_text(nameECSInvalidScope):
else:
ecso = clientsubnetoption.ClientSubnetOption(self.ipToStr(option), option.mask, option.mask)
- answer = dns.rrset.from_text(request.question[0].name, ttlECS, dns.rdataclass.IN, 'TXT', text)
+ answer = dns.rrset.from_text(request.question[0].name, ttlECS, dns.rdataclass.IN, "TXT", text)
response.answer.append(answer)
elif request.question[0].name == dns.name.from_text(nameECS) and request.question[0].rdtype == dns.rdatatype.NS:
- answer = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'NS', 'ns1.ecs-echo.example.')
+ answer = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, "NS", "ns1.ecs-echo.example.")
response.answer.append(answer)
- additional = dns.rrset.from_text('ns1.ecs-echo.example.', 15, dns.rdataclass.IN, 'A', os.environ['PREFIX'] + '.24')
+ additional = dns.rrset.from_text(
+ "ns1.ecs-echo.example.", 15, dns.rdataclass.IN, "A", os.environ["PREFIX"] + ".24"
+ )
response.additional.append(additional)
if ecso:
- response.use_edns(options = [ecso])
+ response.use_edns(options=[ecso])
self.transport.write(response.to_wire(), address)
from recursortests import RecursorTest
-class SNMPTest(RecursorTest):
+class SNMPTest(RecursorTest):
_snmpTimeout = 2.0
- _snmpServer = '127.0.0.1'
+ _snmpServer = "127.0.0.1"
_snmpPort = 161
- _snmpV2Community = 'secretcommunity'
- _snmpV3User = 'secretuser'
- _snmpV3AuthKey = 'mysecretauthkey'
- _snmpV3EncKey = 'mysecretenckey'
- _snmpOID = '1.3.6.1.4.1.43315.2'
+ _snmpV2Community = "secretcommunity"
+ _snmpV3User = "secretuser"
+ _snmpV3AuthKey = "mysecretauthkey"
+ _snmpV3EncKey = "mysecretenckey"
+ _snmpOID = "1.3.6.1.4.1.43315.2"
_queriesSent = 0
- _confdir = 'SNMP'
+ _confdir = "SNMP"
_config_template = """
snmp-agent=yes
"""
def _checkStatsValues(self, results):
count = 161
for i in list(range(1, count)):
- oid = self._snmpOID + '.1.' + str(i) + '.0'
+ oid = self._snmpOID + ".1." + str(i) + ".0"
self.assertIn(oid, results)
self.assertTrue(isinstance(results[oid], Counter64))
- oid = self._snmpOID + '.1.' + str(count + 1) + '.0'
+ oid = self._snmpOID + ".1." + str(count + 1) + ".0"
self.assertNotIn(oid, results)
# check uptime > 0
- self.assertGreater(results['1.3.6.1.4.1.43315.2.1.75.0'], 0)
+ self.assertGreater(results["1.3.6.1.4.1.43315.2.1.75.0"], 0)
# check memory usage > 0
- self.assertGreater(results['1.3.6.1.4.1.43315.2.1.76.0'], 0)
+ self.assertGreater(results["1.3.6.1.4.1.43315.2.1.76.0"], 0)
def _getSNMPStats(self, auth):
results = {}
- for (errorIndication, errorStatus, errorIndex, varBinds) in nextCmd(SnmpEngine(),
- auth,
- UdpTransportTarget((self._snmpServer, self._snmpPort), timeout=self._snmpTimeout),
- ContextData(),
- ObjectType(ObjectIdentity(self._snmpOID)),
- lookupMib=False):
+ for errorIndication, errorStatus, errorIndex, varBinds in nextCmd(
+ SnmpEngine(),
+ auth,
+ UdpTransportTarget((self._snmpServer, self._snmpPort), timeout=self._snmpTimeout),
+ ContextData(),
+ ObjectType(ObjectIdentity(self._snmpOID)),
+ lookupMib=False,
+ ):
self.assertFalse(errorIndication)
self.assertFalse(errorStatus)
self.assertTrue(varBinds)
SNMP: Retrieve statistics via SNMPv3
"""
- auth = UsmUserData(self._snmpV3User,
- authKey=self._snmpV3AuthKey,
- privKey=self._snmpV3EncKey,
- authProtocol=usmHMACSHAAuthProtocol,
- privProtocol=usmAesCfb128Protocol)
+ auth = UsmUserData(
+ self._snmpV3User,
+ authKey=self._snmpV3AuthKey,
+ privKey=self._snmpV3EncKey,
+ authProtocol=usmHMACSHAAuthProtocol,
+ privProtocol=usmAesCfb128Protocol,
+ )
self._checkStats(auth)
import dns
from recursortests import RecursorTest
+
class ServerNamesTest(RecursorTest):
"""
This tests all kinds naming things
"""
- _confdir = 'ServerNames'
+ _confdir = "ServerNames"
_auth_zones = RecursorTest._default_auth_zones
- _servername = 'awesome-pdns1.example.com'
- _versionbind = 'Awesome!'
- _versionbind_expected = dns.rrset.from_text('version.bind.', 86400, 'CH', 'TXT', _versionbind)
- _idserver_expected = dns.rrset.from_text('id.server.', 86400, 'CH', 'TXT', _servername)
+ _servername = "awesome-pdns1.example.com"
+ _versionbind = "Awesome!"
+ _versionbind_expected = dns.rrset.from_text("version.bind.", 86400, "CH", "TXT", _versionbind)
+ _idserver_expected = dns.rrset.from_text("id.server.", 86400, "CH", "TXT", _servername)
_config_template = """
server-id=%s
version-string=%s
""" % (_servername, _versionbind)
-
def testVersionBindUDP(self):
"""
Send a version.bind CH TXT query over UDP and look for the version string
"""
- query = dns.message.make_query('version.bind', 'TXT', 'CH', use_edns=False)
+ query = dns.message.make_query("version.bind", "TXT", "CH", use_edns=False)
response = self.sendUDPQuery(query)
self.assertEqual(len(response.answer), 1)
"""
Send a version.bind CH TXT query over TCP and look for the version string
"""
- query = dns.message.make_query('version.bind', 'TXT', 'CH', use_edns=False)
+ query = dns.message.make_query("version.bind", "TXT", "CH", use_edns=False)
response = self.sendTCPQuery(query)
self.assertEqual(len(response.answer), 1)
"""
Send a version.bind CH TXT query over UDP (with EDNS) and look for the version string
"""
- query = dns.message.make_query('version.bind', 'TXT', 'CH', use_edns=True)
+ query = dns.message.make_query("version.bind", "TXT", "CH", use_edns=True)
response = self.sendUDPQuery(query)
self.assertEqual(len(response.answer), 1)
"""
Send a version.bind CH TXT query over TCP (with EDNS) and look for the version string
"""
- query = dns.message.make_query('version.bind', 'TXT', 'CH', use_edns=True)
+ query = dns.message.make_query("version.bind", "TXT", "CH", use_edns=True)
response = self.sendTCPQuery(query)
self.assertEqual(len(response.answer), 1)
"""
Send an id.server CH TXT query over UDP and look for the server id
"""
- query = dns.message.make_query('id.server', 'TXT', 'CH', use_edns=False)
+ query = dns.message.make_query("id.server", "TXT", "CH", use_edns=False)
response = self.sendUDPQuery(query)
self.assertEqual(len(response.answer), 1)
"""
Send an id.server CH TXT query over TCP and look for the server id
"""
- query = dns.message.make_query('id.server', 'TXT', 'CH', use_edns=False)
+ query = dns.message.make_query("id.server", "TXT", "CH", use_edns=False)
response = self.sendTCPQuery(query)
self.assertEqual(len(response.answer), 1)
"""
Send an id.server CH TXT query over UDP (with EDNS) and look for the server id
"""
- query = dns.message.make_query('id.server', 'TXT', 'CH', use_edns=True)
+ query = dns.message.make_query("id.server", "TXT", "CH", use_edns=True)
response = self.sendUDPQuery(query)
self.assertEqual(len(response.answer), 1)
"""
Send an id.server CH TXT query over TCP (with EDNS) and look for the server id
"""
- query = dns.message.make_query('id.server', 'TXT', 'CH', use_edns=True)
+ query = dns.message.make_query("id.server", "TXT", "CH", use_edns=True)
response = self.sendTCPQuery(query)
self.assertEqual(len(response.answer), 1)
"""
Send a .|NS query with NSID option
"""
- opts = [dns.edns.GenericOption(dns.edns.NSID, b'')]
- query = dns.message.make_query('.', 'NS', 'IN', use_edns=True, options=opts)
+ opts = [dns.edns.GenericOption(dns.edns.NSID, b"")]
+ query = dns.message.make_query(".", "NS", "IN", use_edns=True, options=opts)
response = self.sendUDPQuery(query)
self.assertEqual(len(response.options), 1)
if dns.version.MAJOR < 2 or (dns.version.MAJOR == 2 and dns.version.MINOR < 6):
- self.assertEqual(response.options[0].data, self._servername.encode('ascii'))
+ self.assertEqual(response.options[0].data, self._servername.encode("ascii"))
else:
- self.assertEqual(response.options[0].to_text(), 'NSID ' + self._servername)
+ self.assertEqual(response.options[0].to_text(), "NSID " + self._servername)
def testNSIDTCP(self):
"""
Send a .|NS query with NSID option
"""
- opts = [dns.edns.GenericOption(dns.edns.NSID, b'')]
- query = dns.message.make_query('.', 'NS', 'IN', use_edns=True, options=opts)
+ opts = [dns.edns.GenericOption(dns.edns.NSID, b"")]
+ query = dns.message.make_query(".", "NS", "IN", use_edns=True, options=opts)
response = self.sendTCPQuery(query)
self.assertEqual(len(response.options), 1)
if dns.version.MAJOR < 2 or (dns.version.MAJOR == 2 and dns.version.MINOR < 6):
- self.assertEqual(response.options[0].data, self._servername.encode('ascii'))
+ self.assertEqual(response.options[0].data, self._servername.encode("ascii"))
else:
- self.assertEqual(response.options[0].to_text(), 'NSID ' + self._servername)
+ self.assertEqual(response.options[0].to_text(), "NSID " + self._servername)
import os
from recursortests import RecursorTest
+
class SimpleTest(RecursorTest):
- _confdir = 'Simple'
+ _confdir = "Simple"
_auth_zones = RecursorTest._default_auth_zones
- _config_template = """dnssec=validate
-auth-zones=authzone.example=configs/%s/authzone.zone""" % _confdir
+ _config_template = (
+ """dnssec=validate
+auth-zones=authzone.example=configs/%s/authzone.zone"""
+ % _confdir
+ )
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'authzone.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN authzone.example.
+ authzonepath = os.path.join(confdir, "authzone.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN authzone.example.
@ 3600 IN SOA {soa}
@ 3600 IN A 192.0.2.88
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(SimpleTest, cls).generateRecursorConfig(confdir)
def testSOAs(self):
- for zone in ['.', 'example.', 'secure.example.']:
- expected = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, 'SOA', self._SOA)
- query = dns.message.make_query(zone, 'SOA', want_dnssec=True)
+ for zone in [".", "example.", "secure.example."]:
+ expected = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, "SOA", self._SOA)
+ query = dns.message.make_query(zone, "SOA", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
self.assertMatchingRRSIGInAnswer(res, expected)
def testA(self):
- expected = dns.rrset.from_text('ns.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.9'.format(prefix=self._PREFIX))
- query = dns.message.make_query('ns.secure.example', 'A', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "ns.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.9".format(prefix=self._PREFIX)
+ )
+ query = dns.message.make_query("ns.secure.example", "A", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
self.assertMatchingRRSIGInAnswer(res, expected)
def testDelegation(self):
- query = dns.message.make_query('example', 'NS', want_dnssec=True)
+ query = dns.message.make_query("example", "NS", want_dnssec=True)
query.flags |= dns.flags.AD
- expectedNS = dns.rrset.from_text('example.', 0, 'IN', 'NS', 'ns1.example.', 'ns2.example.')
+ expectedNS = dns.rrset.from_text("example.", 0, "IN", "NS", "ns1.example.", "ns2.example.")
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expectedNS)
def testBogus(self):
- query = dns.message.make_query('ted.bogus.example', 'A', want_dnssec=True)
+ query = dns.message.make_query("ted.bogus.example", "A", want_dnssec=True)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
def testAuthZone(self):
- query = dns.message.make_query('authzone.example', 'A', want_dnssec=True)
+ query = dns.message.make_query("authzone.example", "A", want_dnssec=True)
- expectedA = dns.rrset.from_text('authzone.example.', 0, 'IN', 'A', '192.0.2.88')
+ expectedA = dns.rrset.from_text("authzone.example.", 0, "IN", "A", "192.0.2.88")
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expectedA)
def testLocalhost(self):
- queryA = dns.message.make_query('localhost', 'A', want_dnssec=True)
- expectedA = dns.rrset.from_text('localhost.', 0, 'IN', 'A', '127.0.0.1')
+ queryA = dns.message.make_query("localhost", "A", want_dnssec=True)
+ expectedA = dns.rrset.from_text("localhost.", 0, "IN", "A", "127.0.0.1")
- queryPTR = dns.message.make_query('1.0.0.127.in-addr.arpa', 'PTR', want_dnssec=True)
- expectedPTR = dns.rrset.from_text('1.0.0.127.in-addr.arpa.', 0, 'IN', 'PTR', 'localhost.')
+ queryPTR = dns.message.make_query("1.0.0.127.in-addr.arpa", "PTR", want_dnssec=True)
+ expectedPTR = dns.rrset.from_text("1.0.0.127.in-addr.arpa.", 0, "IN", "PTR", "localhost.")
resA = self.sendUDPQuery(queryA)
resPTR = self.sendUDPQuery(queryPTR)
self.assertRRsetInAnswer(resPTR, expectedPTR)
def testLocalhostSubdomain(self):
- queryA = dns.message.make_query('foo.localhost', 'A', want_dnssec=True)
- expectedA = dns.rrset.from_text('foo.localhost.', 0, 'IN', 'A', '127.0.0.1')
+ queryA = dns.message.make_query("foo.localhost", "A", want_dnssec=True)
+ expectedA = dns.rrset.from_text("foo.localhost.", 0, "IN", "A", "127.0.0.1")
resA = self.sendUDPQuery(queryA)
self.assertRRsetInAnswer(resA, expectedA)
def testIslandOfSecurity(self):
- query = dns.message.make_query('cname-to-islandofsecurity.secure.example.', 'A', want_dnssec=True)
+ query = dns.message.make_query("cname-to-islandofsecurity.secure.example.", "A", want_dnssec=True)
- expectedA = dns.rrset.from_text('node1.islandofsecurity.example.', 0, 'IN', 'A', '192.0.2.20')
+ expectedA = dns.rrset.from_text("node1.islandofsecurity.example.", 0, "IN", "A", "192.0.2.20")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expectedA)
-
import shutil
from recursortests import RecursorTest
+
class SimpleCookiesTest(RecursorTest):
- _confdir = 'SimpleCookies'
+ _confdir = "SimpleCookies"
_auth_zones = RecursorTest._default_auth_zones
- _config_template = """
+ _config_template = (
+ """
recursor:
auth_zones:
- zone: authzone.example
dnssec:
validation: validate
outgoing:
- cookies: true""" % _confdir
+ cookies: true"""
+ % _confdir
+ )
+
+ _expectedCookies = "Unsupported"
- _expectedCookies = 'Unsupported'
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'authzone.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN authzone.example.
+ authzonepath = os.path.join(confdir, "authzone.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN authzone.example.
@ 3600 IN SOA {soa}
@ 3600 IN A 192.0.2.88
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(SimpleCookiesTest, cls).generateRecursorYamlConfig(confdir)
def checkCookies(self):
- confdir = os.path.join('configs', self._confdir)
- output = self.recControl(confdir, 'dump-cookies', '-')
+ confdir = os.path.join("configs", self._confdir)
+ output = self.recControl(confdir, "dump-cookies", "-")
for line in output.splitlines():
tokens = line.split()
- if tokens[0] == ';' or tokens[0] == 'dump-cookies:':
+ if tokens[0] == ";" or tokens[0] == "dump-cookies:":
continue
print(tokens)
self.assertEqual(len(tokens), 5)
self.assertEqual(tokens[3], self._expectedCookies)
def testSOAs(self):
- for zone in ['.', 'example.', 'secure.example.']:
- expected = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, 'SOA', self._SOA)
- query = dns.message.make_query(zone, 'SOA', want_dnssec=True)
+ for zone in [".", "example.", "secure.example."]:
+ expected = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, "SOA", self._SOA)
+ query = dns.message.make_query(zone, "SOA", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
self.checkCookies()
def testA(self):
- expected = dns.rrset.from_text('ns.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.9'.format(prefix=self._PREFIX))
- query = dns.message.make_query('ns.secure.example', 'A', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "ns.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.9".format(prefix=self._PREFIX)
+ )
+ query = dns.message.make_query("ns.secure.example", "A", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
self.checkCookies()
def testDelegation(self):
- query = dns.message.make_query('example', 'NS', want_dnssec=True)
+ query = dns.message.make_query("example", "NS", want_dnssec=True)
query.flags |= dns.flags.AD
- expectedNS = dns.rrset.from_text('example.', 0, 'IN', 'NS', 'ns1.example.', 'ns2.example.')
+ expectedNS = dns.rrset.from_text("example.", 0, "IN", "NS", "ns1.example.", "ns2.example.")
res = self.sendUDPQuery(query)
self.checkCookies()
def testBogus(self):
- query = dns.message.make_query('ted.bogus.example', 'A', want_dnssec=True)
+ query = dns.message.make_query("ted.bogus.example", "A", want_dnssec=True)
res = self.sendUDPQuery(query)
self.checkCookies()
def testAuthZone(self):
- query = dns.message.make_query('authzone.example', 'A', want_dnssec=True)
+ query = dns.message.make_query("authzone.example", "A", want_dnssec=True)
- expectedA = dns.rrset.from_text('authzone.example.', 0, 'IN', 'A', '192.0.2.88')
+ expectedA = dns.rrset.from_text("authzone.example.", 0, "IN", "A", "192.0.2.88")
res = self.sendUDPQuery(query)
self.checkCookies()
def testLocalhost(self):
- queryA = dns.message.make_query('localhost', 'A', want_dnssec=True)
- expectedA = dns.rrset.from_text('localhost.', 0, 'IN', 'A', '127.0.0.1')
+ queryA = dns.message.make_query("localhost", "A", want_dnssec=True)
+ expectedA = dns.rrset.from_text("localhost.", 0, "IN", "A", "127.0.0.1")
- queryPTR = dns.message.make_query('1.0.0.127.in-addr.arpa', 'PTR', want_dnssec=True)
- expectedPTR = dns.rrset.from_text('1.0.0.127.in-addr.arpa.', 0, 'IN', 'PTR', 'localhost.')
+ queryPTR = dns.message.make_query("1.0.0.127.in-addr.arpa", "PTR", want_dnssec=True)
+ expectedPTR = dns.rrset.from_text("1.0.0.127.in-addr.arpa.", 0, "IN", "PTR", "localhost.")
resA = self.sendUDPQuery(queryA)
resPTR = self.sendUDPQuery(queryPTR)
self.checkCookies()
def testLocalhostSubdomain(self):
- queryA = dns.message.make_query('foo.localhost', 'A', want_dnssec=True)
- expectedA = dns.rrset.from_text('foo.localhost.', 0, 'IN', 'A', '127.0.0.1')
+ queryA = dns.message.make_query("foo.localhost", "A", want_dnssec=True)
+ expectedA = dns.rrset.from_text("foo.localhost.", 0, "IN", "A", "127.0.0.1")
resA = self.sendUDPQuery(queryA)
self.checkCookies()
def testIslandOfSecurity(self):
- query = dns.message.make_query('cname-to-islandofsecurity.secure.example.', 'A', want_dnssec=True)
+ query = dns.message.make_query("cname-to-islandofsecurity.secure.example.", "A", want_dnssec=True)
- expectedA = dns.rrset.from_text('node1.islandofsecurity.example.', 0, 'IN', 'A', '192.0.2.20')
+ expectedA = dns.rrset.from_text("node1.islandofsecurity.example.", 0, "IN", "A", "192.0.2.20")
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expectedA)
self.checkCookies()
+
class SimpleCookiesAuthEnabledTest(SimpleCookiesTest):
- _confdir = 'SimpleCookiesAuthEnabled'
+ _confdir = "SimpleCookiesAuthEnabled"
_auth_zones = SimpleCookiesTest._auth_zones
- _expectedCookies = 'Supported'
+ _expectedCookies = "Supported"
@classmethod
def generateAuthConfig(cls, confdir, threads):
- super(SimpleCookiesAuthEnabledTest, cls).generateAuthConfig(confdir, threads, "edns-cookie-secret=01234567890123456789012345678901")
+ super(SimpleCookiesAuthEnabledTest, cls).generateAuthConfig(
+ confdir, threads, "edns-cookie-secret=01234567890123456789012345678901"
+ )
@classmethod
def setUpClass(cls):
@classmethod
def tearDownClass(cls):
- confdir = os.path.join('configs', 'auths')
+ confdir = os.path.join("configs", "auths")
print("Specialized auth teardown " + confdir)
# tear down specialized auths, and then start standard ones
super().tearDownClass(True)
print("Starting default auths")
- #confdir = 'configs/auths'
+ # confdir = 'configs/auths'
shutil.rmtree(confdir, True)
os.mkdir(confdir)
# Be careful here, we don't want the overridden secureZone(), so call RecursorTest explicitly
import os
from recursortests import RecursorTest
+
class SimpleTCPTest(RecursorTest):
- _confdir = 'SimpleTCP'
+ _confdir = "SimpleTCP"
_auth_zones = RecursorTest._default_auth_zones
- _config_template = """dnssec=validate
-auth-zones=authzone.example=configs/%s/authzone.zone""" % _confdir
+ _config_template = (
+ """dnssec=validate
+auth-zones=authzone.example=configs/%s/authzone.zone"""
+ % _confdir
+ )
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'authzone.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN authzone.example.
+ authzonepath = os.path.join(confdir, "authzone.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN authzone.example.
@ 3600 IN SOA {soa}
@ 3600 IN A 192.0.2.88
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(SimpleTCPTest, cls).generateRecursorConfig(confdir)
def testSOAs(self):
- for zone in ['.', 'example.', 'secure.example.']:
- expected = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, 'SOA', self._SOA)
- query = dns.message.make_query(zone, 'SOA', want_dnssec=True)
+ for zone in [".", "example.", "secure.example."]:
+ expected = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, "SOA", self._SOA)
+ query = dns.message.make_query(zone, "SOA", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendTCPQuery(query)
self.assertMatchingRRSIGInAnswer(res, expected)
def testA(self):
- expected = dns.rrset.from_text('ns.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.9'.format(prefix=self._PREFIX))
- query = dns.message.make_query('ns.secure.example', 'A', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "ns.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.9".format(prefix=self._PREFIX)
+ )
+ query = dns.message.make_query("ns.secure.example", "A", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendTCPQuery(query)
self.assertMatchingRRSIGInAnswer(res, expected)
def testDelegation(self):
- query = dns.message.make_query('example', 'NS', want_dnssec=True)
+ query = dns.message.make_query("example", "NS", want_dnssec=True)
query.flags |= dns.flags.AD
- expectedNS = dns.rrset.from_text('example.', 0, 'IN', 'NS', 'ns1.example.', 'ns2.example.')
+ expectedNS = dns.rrset.from_text("example.", 0, "IN", "NS", "ns1.example.", "ns2.example.")
res = self.sendTCPQuery(query)
self.assertRRsetInAnswer(res, expectedNS)
def testBogus(self):
- query = dns.message.make_query('ted.bogus.example', 'A', want_dnssec=True)
+ query = dns.message.make_query("ted.bogus.example", "A", want_dnssec=True)
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
def testAuthZone(self):
- query = dns.message.make_query('authzone.example', 'A', want_dnssec=True)
+ query = dns.message.make_query("authzone.example", "A", want_dnssec=True)
- expectedA = dns.rrset.from_text('authzone.example.', 0, 'IN', 'A', '192.0.2.88')
+ expectedA = dns.rrset.from_text("authzone.example.", 0, "IN", "A", "192.0.2.88")
res = self.sendTCPQuery(query)
self.assertRRsetInAnswer(res, expectedA)
def testLocalhost(self):
- queryA = dns.message.make_query('localhost', 'A', want_dnssec=True)
- expectedA = dns.rrset.from_text('localhost.', 0, 'IN', 'A', '127.0.0.1')
+ queryA = dns.message.make_query("localhost", "A", want_dnssec=True)
+ expectedA = dns.rrset.from_text("localhost.", 0, "IN", "A", "127.0.0.1")
- queryPTR = dns.message.make_query('1.0.0.127.in-addr.arpa', 'PTR', want_dnssec=True)
- expectedPTR = dns.rrset.from_text('1.0.0.127.in-addr.arpa.', 0, 'IN', 'PTR', 'localhost.')
+ queryPTR = dns.message.make_query("1.0.0.127.in-addr.arpa", "PTR", want_dnssec=True)
+ expectedPTR = dns.rrset.from_text("1.0.0.127.in-addr.arpa.", 0, "IN", "PTR", "localhost.")
resA = self.sendTCPQuery(queryA)
resPTR = self.sendTCPQuery(queryPTR)
self.assertRRsetInAnswer(resPTR, expectedPTR)
def testLocalhostSubdomain(self):
- queryA = dns.message.make_query('foo.localhost', 'A', want_dnssec=True)
- expectedA = dns.rrset.from_text('foo.localhost.', 0, 'IN', 'A', '127.0.0.1')
+ queryA = dns.message.make_query("foo.localhost", "A", want_dnssec=True)
+ expectedA = dns.rrset.from_text("foo.localhost.", 0, "IN", "A", "127.0.0.1")
resA = self.sendTCPQuery(queryA)
self.assertRRsetInAnswer(resA, expectedA)
def testIslandOfSecurity(self):
- query = dns.message.make_query('cname-to-islandofsecurity.secure.example.', 'A', want_dnssec=True)
+ query = dns.message.make_query("cname-to-islandofsecurity.secure.example.", "A", want_dnssec=True)
- expectedA = dns.rrset.from_text('node1.islandofsecurity.example.', 0, 'IN', 'A', '192.0.2.20')
+ expectedA = dns.rrset.from_text("node1.islandofsecurity.example.", 0, "IN", "A", "192.0.2.20")
res = self.sendTCPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expectedA)
-
def testVeryBasicPipeline(self):
# This test does not enforce order, it will accept replies in any order. So
# it does not actually test OOO behaviour.
expected = {}
queries = []
- for zone in ['.', 'example.', 'secure.example.']:
- expected[zone] = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, 'SOA', self._SOA)
- query = dns.message.make_query(zone, 'SOA', want_dnssec=True)
+ for zone in [".", "example.", "secure.example."]:
+ expected[zone] = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, "SOA", self._SOA)
+ query = dns.message.make_query(zone, "SOA", want_dnssec=True)
query.flags |= dns.flags.AD
queries.append(query)
- expected['ns.secure.example.'] = dns.rrset.from_text('ns.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.9'.format(prefix=self._PREFIX))
- query = dns.message.make_query('ns.secure.example', 'A', want_dnssec=True)
+ expected["ns.secure.example."] = dns.rrset.from_text(
+ "ns.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.9".format(prefix=self._PREFIX)
+ )
+ query = dns.message.make_query("ns.secure.example", "A", want_dnssec=True)
query.flags |= dns.flags.AD
queries.append(query)
self.assertMessageIsAuthenticated(res)
self.assertRRsetInAnswer(res, exp)
self.assertMatchingRRSIGInAnswer(res, exp)
-
import os
from recursortests import RecursorTest
+
class SimpleYAMLTest(RecursorTest):
- _confdir = 'SimpleYAML'
+ _confdir = "SimpleYAML"
_auth_zones = RecursorTest._default_auth_zones
- _config_template = """
+ _config_template = (
+ """
recursor:
auth_zones:
- zone: authzone.example
file: configs/%s/authzone.zone
dnssec:
- validation: validate""" % _confdir
+ validation: validate"""
+ % _confdir
+ )
@classmethod
def generateRecursorConfig(cls, confdir):
- authzonepath = os.path.join(confdir, 'authzone.zone')
- with open(authzonepath, 'w') as authzone:
- authzone.write("""$ORIGIN authzone.example.
+ authzonepath = os.path.join(confdir, "authzone.zone")
+ with open(authzonepath, "w") as authzone:
+ authzone.write(
+ """$ORIGIN authzone.example.
@ 3600 IN SOA {soa}
@ 3600 IN A 192.0.2.88
-""".format(soa=cls._SOA))
+""".format(soa=cls._SOA)
+ )
super(SimpleYAMLTest, cls).generateRecursorYamlConfig(confdir)
def testSOAs(self):
- for zone in ['.', 'example.', 'secure.example.']:
- expected = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, 'SOA', self._SOA)
- query = dns.message.make_query(zone, 'SOA', want_dnssec=True)
+ for zone in [".", "example.", "secure.example."]:
+ expected = dns.rrset.from_text(zone, 0, dns.rdataclass.IN, "SOA", self._SOA)
+ query = dns.message.make_query(zone, "SOA", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
self.assertMatchingRRSIGInAnswer(res, expected)
def testA(self):
- expected = dns.rrset.from_text('ns.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.9'.format(prefix=self._PREFIX))
- query = dns.message.make_query('ns.secure.example', 'A', want_dnssec=True)
+ expected = dns.rrset.from_text(
+ "ns.secure.example.", 0, dns.rdataclass.IN, "A", "{prefix}.9".format(prefix=self._PREFIX)
+ )
+ query = dns.message.make_query("ns.secure.example", "A", want_dnssec=True)
query.flags |= dns.flags.AD
res = self.sendUDPQuery(query)
self.assertMatchingRRSIGInAnswer(res, expected)
def testDelegation(self):
- query = dns.message.make_query('example', 'NS', want_dnssec=True)
+ query = dns.message.make_query("example", "NS", want_dnssec=True)
query.flags |= dns.flags.AD
- expectedNS = dns.rrset.from_text('example.', 0, 'IN', 'NS', 'ns1.example.', 'ns2.example.')
+ expectedNS = dns.rrset.from_text("example.", 0, "IN", "NS", "ns1.example.", "ns2.example.")
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expectedNS)
def testBogus(self):
- query = dns.message.make_query('ted.bogus.example', 'A', want_dnssec=True)
+ query = dns.message.make_query("ted.bogus.example", "A", want_dnssec=True)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
def testAuthZone(self):
- query = dns.message.make_query('authzone.example', 'A', want_dnssec=True)
+ query = dns.message.make_query("authzone.example", "A", want_dnssec=True)
- expectedA = dns.rrset.from_text('authzone.example.', 0, 'IN', 'A', '192.0.2.88')
+ expectedA = dns.rrset.from_text("authzone.example.", 0, "IN", "A", "192.0.2.88")
res = self.sendUDPQuery(query)
self.assertRRsetInAnswer(res, expectedA)
def testLocalhost(self):
- queryA = dns.message.make_query('localhost', 'A', want_dnssec=True)
- expectedA = dns.rrset.from_text('localhost.', 0, 'IN', 'A', '127.0.0.1')
+ queryA = dns.message.make_query("localhost", "A", want_dnssec=True)
+ expectedA = dns.rrset.from_text("localhost.", 0, "IN", "A", "127.0.0.1")
- queryPTR = dns.message.make_query('1.0.0.127.in-addr.arpa', 'PTR', want_dnssec=True)
- expectedPTR = dns.rrset.from_text('1.0.0.127.in-addr.arpa.', 0, 'IN', 'PTR', 'localhost.')
+ queryPTR = dns.message.make_query("1.0.0.127.in-addr.arpa", "PTR", want_dnssec=True)
+ expectedPTR = dns.rrset.from_text("1.0.0.127.in-addr.arpa.", 0, "IN", "PTR", "localhost.")
resA = self.sendUDPQuery(queryA)
resPTR = self.sendUDPQuery(queryPTR)
self.assertRRsetInAnswer(resPTR, expectedPTR)
def testLocalhostSubdomain(self):
- queryA = dns.message.make_query('foo.localhost', 'A', want_dnssec=True)
- expectedA = dns.rrset.from_text('foo.localhost.', 0, 'IN', 'A', '127.0.0.1')
+ queryA = dns.message.make_query("foo.localhost", "A", want_dnssec=True)
+ expectedA = dns.rrset.from_text("foo.localhost.", 0, "IN", "A", "127.0.0.1")
resA = self.sendUDPQuery(queryA)
self.assertRRsetInAnswer(resA, expectedA)
def testIslandOfSecurity(self):
- query = dns.message.make_query('cname-to-islandofsecurity.secure.example.', 'A', want_dnssec=True)
+ query = dns.message.make_query("cname-to-islandofsecurity.secure.example.", "A", want_dnssec=True)
- expectedA = dns.rrset.from_text('node1.islandofsecurity.example.', 0, 'IN', 'A', '192.0.2.20')
+ expectedA = dns.rrset.from_text("node1.islandofsecurity.example.", 0, "IN", "A", "192.0.2.20")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRRsetInAnswer(res, expectedA)
-
class SortlistTest(RecursorTest):
- _confdir = 'Sortlist'
+ _confdir = "Sortlist"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """dnssec=off"""
def testSortlist(self):
msg = dns.message.make_query("sortcname.example.", dns.rdatatype.ANY)
- msg.flags = dns.flags.from_text('RD')
+ msg.flags = dns.flags.from_text("RD")
res = self.sendUDPQuery(msg, fwparams=dict(one_rr_per_rrset=True))
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'TC'], [])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD", "TC"], [])
res = self.sendTCPQuery(msg, fwparams=dict(one_rr_per_rrset=True))
- self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], [])
+ self.assertMessageHasFlags(res, ["QR", "RA", "RD"], [])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
indexCNAME = -1
self.assertEqual(indexCNAME, 0)
self.assertGreater(indexMX, 0)
- self.assertEqual(recordsA, ['17.238.240.5', '17.38.42.80', '192.168.0.1'])
+ self.assertEqual(recordsA, ["17.238.240.5", "17.38.42.80", "192.168.0.1"])
import os
from recursortests import RecursorTest
+
class BogusMaxTTLTest(RecursorTest):
- _confdir = 'BogusMaxTTL'
+ _confdir = "BogusMaxTTL"
_auth_zones = RecursorTest._default_auth_zones
_config_template = """dnssec=validate
@classmethod
def setUp(cls):
- confdir = os.path.join('configs', cls._confdir)
+ confdir = os.path.join("configs", cls._confdir)
cls.wipeRecursorCache(confdir)
def testBogusCheckDisabled(self):
# first query with CD=0, so we should get a ServFail
- query = self.createQuery('ted.bogus.example.', 'A', 'AD', 'DO')
+ query = self.createQuery("ted.bogus.example.", "A", "AD", "DO")
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
# then with CD=1 so we should get the A + RRSIG
# check that we correctly applied the maximum TTL when caching Bogus entries
- query = self.createQuery('ted.bogus.example.', 'A', 'AD CD', 'DO')
+ query = self.createQuery("ted.bogus.example.", "A", "AD CD", "DO")
res = self.sendUDPQuery(query)
- self.assertMessageHasFlags(res, ['CD', 'QR', 'RA', 'RD'], ['DO'])
+ self.assertMessageHasFlags(res, ["CD", "QR", "RA", "RD"], ["DO"])
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(len(res.answer), 2)
for ans in res.answer:
import subprocess
from recursortests import RecursorTest
+
class TraceFailTest(RecursorTest):
- _confdir = 'TraceFail'
+ _confdir = "TraceFail"
_config_template = """
trace=fail
"""
def testA(self):
- query = dns.message.make_query('example', 'A', want_dnssec=False)
+ query = dns.message.make_query("example", "A", want_dnssec=False)
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
- grepCmd = ['grep', 'END OF FAIL TRACE', 'configs/' + self._confdir + '/recursor.log']
- ret = b''
+ grepCmd = ["grep", "END OF FAIL TRACE", "configs/" + self._confdir + "/recursor.log"]
+ ret = b""
for i in range(1000):
time.sleep(0.01)
try:
ret = subprocess.check_output(grepCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
continue
- print(b'A' + ret)
+ print(b"A" + ret)
break
print(ret)
- self.assertNotEqual(ret, b'')
+ self.assertNotEqual(ret, b"")
"""This test will do a query for "trustanchor.server CH TXT" and hopes to get
a proper answer"""
- _confdir = 'TrustAnchorsEnabled'
+ _confdir = "TrustAnchorsEnabled"
_roothints = None
_root_DS = None
_config_template = """
def testTrustanchorDotServer(self):
expected = dns.rrset.from_text_list(
- 'trustanchor.server.', 86400, dns.rdataclass.CH, 'TXT',
- ['". 20326 38696"', '"powerdns.com. 44030"'])
- query = dns.message.make_query('trustanchor.server', 'TXT',
- dns.rdataclass.CH)
+ "trustanchor.server.", 86400, dns.rdataclass.CH, "TXT", ['". 20326 38696"', '"powerdns.com. 44030"']
+ )
+ query = dns.message.make_query("trustanchor.server", "TXT", dns.rdataclass.CH)
result = self.sendUDPQuery(query)
self.assertRcodeEqual(result, dns.rcode.NOERROR)
def testNegativerustanchorDotServer(self):
expected = dns.rrset.from_text_list(
- 'negativetrustanchor.server.', 86400, dns.rdataclass.CH, 'TXT',
- ['"example."', '"example.com. some reason"'])
- query = dns.message.make_query('negativetrustanchor.server', 'TXT',
- dns.rdataclass.CH)
+ "negativetrustanchor.server.", 86400, dns.rdataclass.CH, "TXT", ['"example."', '"example.com. some reason"']
+ )
+ query = dns.message.make_query("negativetrustanchor.server", "TXT", dns.rdataclass.CH)
result = self.sendUDPQuery(query)
self.assertRcodeEqual(result, dns.rcode.NOERROR)
"""This test will do a query for "trustanchor.server CH TXT" and hopes to get
a proper answer"""
- _confdir = 'TrustAnchorsDisabled'
+ _confdir = "TrustAnchorsDisabled"
_roothints = None
_root_DS = None
_config_template = """
"""
def testTrustanchorDotServer(self):
- query = dns.message.make_query('trustanchor.server', 'TXT',
- dns.rdataclass.CH)
+ query = dns.message.make_query("trustanchor.server", "TXT", dns.rdataclass.CH)
result = self.sendUDPQuery(query)
self.assertRcodeEqual(result, dns.rcode.SERVFAIL)
def testNegativerustanchorDotServer(self):
- query = dns.message.make_query('negativetrustanchor.server', 'TXT',
- dns.rdataclass.CH)
+ query = dns.message.make_query("negativetrustanchor.server", "TXT", dns.rdataclass.CH)
result = self.sendUDPQuery(query)
self.assertRcodeEqual(result, dns.rcode.SERVFAIL)
import dns
from recursortests import RecursorTest
+
@pytest.mark.external
class WellKnownTest(RecursorTest):
_auths_zones = None
- _confdir = 'WellKnown'
+ _confdir = "WellKnown"
_roothints = None
_root_DS = None
_config_template = """dnssec=validate"""
def testServFail(self):
- names = ['servfail.nl', 'dnssec-failed.org']
+ names = ["servfail.nl", "dnssec-failed.org"]
results = []
for name in names:
- query = dns.message.make_query(name, 'SOA')
+ query = dns.message.make_query(name, "SOA")
results.append(self.sendUDPQuery(query, timeout=5.0))
self.assertEqual(len(results), len(names))
self.assertRcodeEqual(result, dns.rcode.SERVFAIL)
def testNoError(self):
- names = ['powerdns.com', 'nlnetlabs.nl', 'knot-dns.cz']
+ names = ["powerdns.com", "nlnetlabs.nl", "knot-dns.cz"]
results = []
for name in names:
- query = dns.message.make_query(name, 'SOA')
+ query = dns.message.make_query(name, "SOA")
results.append(self.sendUDPQuery(query))
self.assertEqual(len(results), len(names))
from recursortests import RecursorTest
+
class ZTCTest(RecursorTest):
- _confdir = 'ZTC'
+ _confdir = "ZTC"
_config_template = """
dnssec:
validation: validate
super(ZTCTest, cls).generateRecursorYamlConfig(confdir, False)
def testZTC(self):
- grepCmd = ['grep', 'validationStatus="Secure"', 'configs/' + self._confdir + '/recursor.log']
- ret = b''
+ grepCmd = ["grep", 'validationStatus="Secure"', "configs/" + self._confdir + "/recursor.log"]
+ ret = b""
for i in range(3000):
time.sleep(0.01)
try:
ret = subprocess.check_output(grepCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
continue
- print(b'A' + ret)
+ print(b"A" + ret)
break
print(ret)
- self.assertNotEqual(ret, b'')
-
+ self.assertNotEqual(ret, b"")
from basicDNSSEC import BasicDNSSEC
+
class basicNSECTest(BasicDNSSEC):
__test__ = True
- _confdir = 'basicNSEC'
+ _confdir = "basicNSEC"
_auth_zones = BasicDNSSEC._auth_zones
import os
import subprocess
+
class basicNSEC3Test(BasicDNSSEC):
__test__ = True
- _confdir = 'basicNSEC3'
+ _confdir = "basicNSEC3"
_auth_zones = BasicDNSSEC._auth_zones
@classmethod
def secureZone(cls, confdir, zonename, key=None):
- zone = '.' if zonename == 'ROOT' else zonename
+ zone = "." if zonename == "ROOT" else zonename
if not key:
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'secure-zone',
- zone]
+ pdnsutilCmd = [os.environ["PDNSUTIL"], "--config-dir=%s" % confdir, "secure-zone", zone]
else:
- keyfile = os.path.join(confdir, 'dnssec.key')
- with open(keyfile, 'w') as fdKeyfile:
+ keyfile = os.path.join(confdir, "dnssec.key")
+ with open(keyfile, "w") as fdKeyfile:
fdKeyfile.write(key)
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'import-zone-key',
- zone,
- keyfile,
- 'active',
- 'ksk']
-
- print(' '.join(pdnsutilCmd))
+ pdnsutilCmd = [
+ os.environ["PDNSUTIL"],
+ "--config-dir=%s" % confdir,
+ "import-zone-key",
+ zone,
+ keyfile,
+ "active",
+ "ksk",
+ ]
+
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
params = "1 0 50 AABBCCDDEEFF112233"
if zone == "optout.example":
params = "1 1 50 AABBCCDDEEFF112233"
- pdnsutilCmd = [os.environ['PDNSUTIL'],
- '--config-dir=%s' % confdir,
- 'set-nsec3',
- zone,
- params]
+ pdnsutilCmd = [os.environ["PDNSUTIL"], "--config-dir=%s" % confdir, "set-nsec3", zone, params]
- print(' '.join(pdnsutilCmd))
+ print(" ".join(pdnsutilCmd))
try:
subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
- raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
+ raise AssertionError("%s failed (%d): %s" % (pdnsutilCmd, e.returncode, e.output))
runs = json.load(sys.stdin)
-selectors = dict(s.split('=',1) for s in sys.argv[1:])
+selectors = dict(s.split("=", 1) for s in sys.argv[1:])
-selected=list()
+selected = list()
-names=set()
+names = set()
for run in runs:
- match = True
- for k,v in selectors.iteritems():
- # print k, v, run[k]
- if run[k] != v:
- match = False
- break
+ match = True
+ for k, v in selectors.iteritems():
+ # print k, v, run[k]
+ if run[k] != v:
+ match = False
+ break
- if match:
- selected.append((run['tag'], run))
- names.update(run)
+ if match:
+ selected.append((run["tag"], run))
+ names.update(run)
selected.sort()
-names.discard('tag')
+names.discard("tag")
-fmt=''.join('%%%ds' % max(15, i+4) for i in [3]+map(len, sorted(names)))
-print(fmt % tuple(['tag']+sorted(names)))
+fmt = "".join("%%%ds" % max(15, i + 4) for i in [3] + map(len, sorted(names)))
+print(fmt % tuple(["tag"] + sorted(names)))
for tag, stats in selected:
- print(fmt % tuple([tag] + [stats.get(s) for s in sorted(names)]))
+ print(fmt % tuple([tag] + [stats.get(s) for s in sorted(names)]))
statnames = set()
runs = list()
-for fname in glob.glob('testresults-*.xml'):
- info = fname[12:-4].split('_')
- tag = info.pop(0)
- vars = dict(s.split(':') for s in info)
- vars['tag'] = tag
- varnames.update(vars.keys())
- stats=dict()
- with open(fname) as f:
- for line in f:
- if line.startswith('<'):
- sname = line.split(';')[4][:-3]
- sval = line.split(';')[8][:-3]
- stats[sname]=sval
- statnames.add(sname)
- # print fname, vars, stats
- runs.append(dict(vars.items()+stats.items()))
+for fname in glob.glob("testresults-*.xml"):
+ info = fname[12:-4].split("_")
+ tag = info.pop(0)
+ vars = dict(s.split(":") for s in info)
+ vars["tag"] = tag
+ varnames.update(vars.keys())
+ stats = dict()
+ with open(fname) as f:
+ for line in f:
+ if line.startswith("<"):
+ sname = line.split(";")[4][:-3]
+ sval = line.split(";")[8][:-3]
+ stats[sname] = sval
+ statnames.add(sname)
+ # print fname, vars, stats
+ runs.append(dict(vars.items() + stats.items()))
# print varnames
# print statnames
import socket
import sys
+
def ensure(data, offset, value):
- if (data[offset:offset+len(value)] != value):
- raise Exception("Mismatch at packet offset {0!s} {1!r} != {2!r}".format(offset,data[offset:offset+len(value)], value))
+ if data[offset : offset + len(value)] != value:
+ raise Exception(
+ "Mismatch at packet offset {0!s} {1!r} != {2!r}".format(offset, data[offset : offset + len(value)], value)
+ )
+
def main(host, port):
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+ msg = b"\xaa\x77\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x04tkey\x04unit\x04test\x00\x00\xf9\x00\xff\x04tkey\x04unit\x04test\x00\x00\xf9\x00\xff\x00\x00\x00\x00\x00\x22\x03bad\04algo\x00\x00\x00\x30\x39\x00\x00\x30\x39\x00\x03\x00\x00\x00\x04test\x00\x00"
- msg = b"\xaa\x77\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x04tkey\x04unit\x04test\x00\x00\xf9\x00\xff\x04tkey\x04unit\x04test\x00\x00\xf9\x00\xff\x00\x00\x00\x00\x00\x22\x03bad\04algo\x00\x00\x00\x30\x39\x00\x00\x30\x39\x00\x03\x00\x00\x00\x04test\x00\x00"
+ s.sendto(msg, (host, port))
+ s.settimeout(2)
+ data, addr = s.recvfrom(512)
- s.sendto(msg, (host, port))
- s.settimeout(2)
- data, addr = s.recvfrom(512)
+ # make sure the data validates
- # make sure the data validates
+ # transaction id
+ ensure(data, 0, msg[0:2])
- # transaction id
- ensure(data, 0, msg[0:2])
+ # has one question, one answer
+ ensure(data, 4, b"\x00\x01")
+ ensure(data, 6, b"\x00\x01")
- # has one question, one answer
- ensure(data, 4, b"\x00\x01")
- ensure(data, 6, b"\x00\x01")
+ # question is tkey.unit.test ANY TKEY?
+ ensure(data, 12, b"\x04tkey\x04unit\x04test\x00\x00\xf9\x00\xff")
+ # answer is called tkey.unit.test ANY TKEY (compressed it seems)
+ ensure(data, 32, b"\xc0\x0c\x00\xf9\x00\xff")
- # question is tkey.unit.test ANY TKEY?
- ensure(data, 12, b"\x04tkey\x04unit\x04test\x00\x00\xf9\x00\xff")
- # answer is called tkey.unit.test ANY TKEY (compressed it seems)
- ensure(data, 32, b"\xc0\x0c\x00\xf9\x00\xff")
+ # and then ensure we get an BADALG or error, at least.
+ if data[64:66] == "\x00\x00":
+ raise Exception(
+ "At packet offset {0!s}: expected {2!r}, got {1!r}".format(
+ offset, data[offset : offset + len(value)], value
+ )
+ )
- # and then ensure we get an BADALG or error, at least.
- if (data[64:66] == "\x00\x00"):
- raise Exception("At packet offset {0!s}: expected {2!r}, got {1!r}".format(offset,data[offset:offset+len(value)], value))
+ print("Got expected TKEY response\n")
- print("Got expected TKEY response\n")
-if (len(sys.argv) < 3):
- print("Usage: tkey.py host port")
- sys.exit(1)
+if len(sys.argv) < 3:
+ print("Usage: tkey.py host port")
+ sys.exit(1)
-if __name__ == '__main__':
- main(sys.argv[1], int(sys.argv[2]))
+if __name__ == "__main__":
+ main(sys.argv[1], int(sys.argv[2]))
from invoke import task
from invoke.exceptions import Failure, UnexpectedExit
-auth_backend_ip_addr = os.getenv('AUTH_BACKEND_IP_ADDR', '127.0.0.1')
+auth_backend_ip_addr = os.getenv("AUTH_BACKEND_IP_ADDR", "127.0.0.1")
-clang_version = os.getenv('CLANG_VERSION', '13')
-repo_home = os.getenv('REPO_HOME', '.')
+clang_version = os.getenv("CLANG_VERSION", "13")
+repo_home = os.getenv("REPO_HOME", ".")
all_build_deps = [
- 'ccache',
- 'libboost-all-dev',
- 'libluajit-5.1-dev',
- 'libsodium-dev',
- 'libssl-dev', # This will install libssl 1.1 on Debian 11 and libssl3 on Debian 12
- 'libsystemd-dev',
- 'libtool',
- 'make',
- 'pkg-config',
- 'python3-venv',
- 'systemd',
+ "ccache",
+ "libboost-all-dev",
+ "libluajit-5.1-dev",
+ "libsodium-dev",
+ "libssl-dev", # This will install libssl 1.1 on Debian 11 and libssl3 on Debian 12
+ "libsystemd-dev",
+ "libtool",
+ "make",
+ "pkg-config",
+ "python3-venv",
+ "systemd",
]
-git_build_deps = [
- 'autoconf',
- 'automake',
- 'bison',
- 'bzip2',
- 'curl',
- 'flex',
- 'git',
- 'ragel'
-]
-auth_build_deps = [ # FIXME: perhaps we should be stealing these from the debian (Ubuntu) control file
- 'default-libmysqlclient-dev',
- 'libcdb-dev',
- 'libcurl4-openssl-dev',
- 'libgeoip-dev',
- 'libkrb5-dev',
- 'libldap2-dev',
- 'liblmdb-dev',
- 'libmaxminddb-dev',
- 'libp11-kit-dev',
- 'libpq-dev',
- 'libsqlite3-dev',
- 'libyaml-cpp-dev',
- 'libzmq3-dev',
- 'python3-venv',
- 'socat',
- 'sqlite3',
- 'unixodbc-dev',
- 'cmake',
+git_build_deps = ["autoconf", "automake", "bison", "bzip2", "curl", "flex", "git", "ragel"]
+auth_build_deps = [ # FIXME: perhaps we should be stealing these from the debian (Ubuntu) control file
+ "default-libmysqlclient-dev",
+ "libcdb-dev",
+ "libcurl4-openssl-dev",
+ "libgeoip-dev",
+ "libkrb5-dev",
+ "libldap2-dev",
+ "liblmdb-dev",
+ "libmaxminddb-dev",
+ "libp11-kit-dev",
+ "libpq-dev",
+ "libsqlite3-dev",
+ "libyaml-cpp-dev",
+ "libzmq3-dev",
+ "python3-venv",
+ "socat",
+ "sqlite3",
+ "unixodbc-dev",
+ "cmake",
]
rec_build_deps = [
- 'libcap-dev',
- 'libfstrm-dev',
- 'libgnutls28-dev',
- 'libsnmp-dev',
+ "libcap-dev",
+ "libfstrm-dev",
+ "libgnutls28-dev",
+ "libsnmp-dev",
]
rec_bulk_deps = [
- 'curl',
- 'bind9-dnsutils',
- 'libboost-all-dev',
- 'libcap2',
- 'libfstrm0',
- 'libluajit-5.1-2',
+ "curl",
+ "bind9-dnsutils",
+ "libboost-all-dev",
+ "libcap2",
+ "libfstrm0",
+ "libluajit-5.1-2",
'"libsnmp[1-9]+"',
- 'libsodium23',
- 'libsystemd0',
- 'moreutils',
- 'pdns-tools',
- 'unzip',
+ "libsodium23",
+ "libsystemd0",
+ "moreutils",
+ "pdns-tools",
+ "unzip",
]
rec_bulk_ubicloud_deps = [
- 'curl',
- 'bind9-dnsutils',
- 'libboost-context1.83.0',
- 'libboost-filesystem1.83.0',
- 'libcap2',
- 'libfstrm0',
- 'libluajit-5.1-2',
+ "curl",
+ "bind9-dnsutils",
+ "libboost-context1.83.0",
+ "libboost-filesystem1.83.0",
+ "libcap2",
+ "libfstrm0",
+ "libluajit-5.1-2",
'"libsnmp[1-9]+"',
- 'libsodium23',
- 'libsystemd0',
- 'moreutils',
- 'pdns-tools',
- 'unzip',
+ "libsodium23",
+ "libsystemd0",
+ "moreutils",
+ "pdns-tools",
+ "unzip",
]
dnsdist_build_deps = [
- 'libcap-dev',
- 'libcdb-dev',
- 'libedit-dev',
- 'libfstrm-dev',
- 'libgnutls28-dev',
- 'liblmdb-dev',
- 'libnghttp2-dev',
- 'libre2-dev',
- 'libsnmp-dev',
+ "libcap-dev",
+ "libcdb-dev",
+ "libedit-dev",
+ "libfstrm-dev",
+ "libgnutls28-dev",
+ "liblmdb-dev",
+ "libnghttp2-dev",
+ "libre2-dev",
+ "libsnmp-dev",
]
dnsdist_xdp_build_deps = [
- 'libbpf-dev',
- 'libxdp-dev',
+ "libbpf-dev",
+ "libxdp-dev",
]
-auth_test_deps = [ # FIXME: we should be generating some of these from shlibdeps in build
- 'authbind',
- 'bc',
- 'bind9utils',
- 'curl',
- 'default-jre-headless',
- 'bind9-dnsutils',
- 'gawk',
- 'krb5-user',
- 'ldnsutils',
+auth_test_deps = [ # FIXME: we should be generating some of these from shlibdeps in build
+ "authbind",
+ "bc",
+ "bind9utils",
+ "curl",
+ "default-jre-headless",
+ "bind9-dnsutils",
+ "gawk",
+ "krb5-user",
+ "ldnsutils",
'"libboost-serialization1.7[1-9]+"',
- 'libcdb1',
- 'libcurl4',
- 'libgeoip1',
- 'libkrb5-3',
+ "libcdb1",
+ "libcurl4",
+ "libgeoip1",
+ "libkrb5-3",
'"libldap-2.[1-9]+"',
- 'liblmdb0',
- 'libluajit-5.1-2',
- 'libmaxminddb0',
- 'libnet-dns-perl',
- 'libp11-kit0',
- 'libpq5',
- 'libsodium23',
- 'libsqlite3-dev',
- 'libsystemd0',
+ "liblmdb0",
+ "libluajit-5.1-2",
+ "libmaxminddb0",
+ "libnet-dns-perl",
+ "libp11-kit0",
+ "libpq5",
+ "libsodium23",
+ "libsqlite3-dev",
+ "libsystemd0",
'"libyaml-cpp0.[1-9]+"',
- 'libzmq3-dev',
- 'lmdb-utils',
- 'prometheus',
- 'python3-venv',
- 'socat',
- 'softhsm2',
- 'unbound-host',
- 'unixodbc',
- 'wget',
+ "libzmq3-dev",
+ "lmdb-utils",
+ "prometheus",
+ "python3-venv",
+ "socat",
+ "softhsm2",
+ "unbound-host",
+ "unixodbc",
+ "wget",
]
doc_deps = [
- 'autoconf',
- 'automake',
- 'bison',
- 'curl',
- 'flex',
- 'g++',
- 'git',
- 'latexmk',
- 'libboost-all-dev',
- 'libedit-dev',
- 'libluajit-5.1-dev',
- 'libssl-dev',
- 'make',
- 'pkg-config',
- 'python3-venv',
- 'ragel',
- 'rsync',
+ "autoconf",
+ "automake",
+ "bison",
+ "curl",
+ "flex",
+ "g++",
+ "git",
+ "latexmk",
+ "libboost-all-dev",
+ "libedit-dev",
+ "libluajit-5.1-dev",
+ "libssl-dev",
+ "make",
+ "pkg-config",
+ "python3-venv",
+ "ragel",
+ "rsync",
]
doc_deps_pdf = [
- 'texlive-binaries',
- 'texlive-formats-extra',
- 'texlive-latex-extra',
+ "texlive-binaries",
+ "texlive-formats-extra",
+ "texlive-latex-extra",
]
+
@task
def apt_fresh(c):
- c.sudo('apt-get update')
- c.sudo('apt-get -y --allow-downgrades dist-upgrade')
+ c.sudo("apt-get update")
+ c.sudo("apt-get -y --allow-downgrades dist-upgrade")
+
@task
def install_lld_linker_if_needed(c):
if is_compiler_clang():
- c.sudo(f'apt-get -y --no-install-recommends install lld-{clang_version}')
+ c.sudo(f"apt-get -y --no-install-recommends install lld-{clang_version}")
+
@task
def install_clang(c):
install clang and llvm
"""
if int(clang_version) >= 14:
- c.sudo(f'apt-get -y --no-install-recommends install clang-{clang_version} llvm-{clang_version} llvm-{clang_version}-dev libclang-rt-{clang_version}-dev')
+ c.sudo(
+ f"apt-get -y --no-install-recommends install clang-{clang_version} llvm-{clang_version} llvm-{clang_version}-dev libclang-rt-{clang_version}-dev"
+ )
else:
- c.sudo(f'apt-get -y --no-install-recommends install clang-{clang_version} llvm-{clang_version} llvm-{clang_version}-dev')
+ c.sudo(
+ f"apt-get -y --no-install-recommends install clang-{clang_version} llvm-{clang_version} llvm-{clang_version}-dev"
+ )
+
@task
def install_clang_tidy_tools(c):
- c.sudo(f'apt-get -y --no-install-recommends install clang-tidy-{clang_version} clang-tools-{clang_version} bear python3-yaml')
+ c.sudo(
+ f"apt-get -y --no-install-recommends install clang-tidy-{clang_version} clang-tools-{clang_version} bear python3-yaml"
+ )
+
@task
def install_clang_runtime(c):
# this gives us the symbolizer, for symbols in asan/ubsan traces
# on Debian we need llvm-symbolizer-XX
- #c.sudo(f'apt-get -y --no-install-recommends install llvm-symbolizer-{clang_version}')
+ # c.sudo(f'apt-get -y --no-install-recommends install llvm-symbolizer-{clang_version}')
# on Ubuntu we need llvm-XX instead
- c.sudo(f'apt-get -y --no-install-recommends install llvm-{clang_version}')
+ c.sudo(f"apt-get -y --no-install-recommends install llvm-{clang_version}")
+
@task
def ci_install_rust(c, repo):
- with c.cd(f'{repo}/builder-support/helpers/'):
- c.run('sudo sh install_rust.sh')
+ with c.cd(f"{repo}/builder-support/helpers/"):
+ c.run("sudo sh install_rust.sh")
+
@task
def install_doc_deps(c):
- c.sudo('apt-get install -y ' + ' '.join(doc_deps))
+ c.sudo("apt-get install -y " + " ".join(doc_deps))
+
@task
def install_doc_deps_pdf(c):
- c.sudo('apt-get install -y ' + ' '.join(doc_deps_pdf))
+ c.sudo("apt-get install -y " + " ".join(doc_deps_pdf))
+
@task
def install_auth_build_deps(c):
- c.sudo('apt-get install -y --no-install-recommends ' + ' '.join(all_build_deps + git_build_deps + auth_build_deps))
+ c.sudo("apt-get install -y --no-install-recommends " + " ".join(all_build_deps + git_build_deps + auth_build_deps))
+
def is_coverage_enabled():
- sanitizers = os.getenv('SANITIZERS')
+ sanitizers = os.getenv("SANITIZERS")
if sanitizers:
- sanitizers = sanitizers.split('+')
- if 'tsan' in sanitizers:
+ sanitizers = sanitizers.split("+")
+ if "tsan" in sanitizers:
return False
- return os.getenv('COVERAGE') == 'yes'
+ return os.getenv("COVERAGE") == "yes"
+
def get_coverage(meson=False):
if meson:
- return '-Dclang-coverage-format=true' if is_coverage_enabled() else ''
- return '--enable-coverage=clang' if is_coverage_enabled() else ''
+ return "-Dclang-coverage-format=true" if is_coverage_enabled() else ""
+ return "--enable-coverage=clang" if is_coverage_enabled() else ""
+
@task
def install_coverage_deps(c):
if is_coverage_enabled():
- c.sudo(f'apt-get install -y --no-install-recommends llvm-{clang_version}')
+ c.sudo(f"apt-get install -y --no-install-recommends llvm-{clang_version}")
+
@task
def generate_coverage_info(c, binary, product, outputDir):
if is_coverage_enabled():
- version = os.getenv('BUILDER_VERSION')
- c.run(f'llvm-profdata-{clang_version} merge -sparse -o {outputDir}/temp.profdata /tmp/code-*.profraw')
- c.run(f'llvm-cov-{clang_version} export --format=lcov --ignore-filename-regex=\'^/usr/\' --ignore-filename-regex=\'ext/\' -instr-profile={outputDir}/temp.profdata -object {binary} > {outputDir}/coverage.lcov')
- c.run(f'{outputDir}/.github/scripts/normalize_paths_in_coverage.py {outputDir} {product} {version} {outputDir}/coverage.lcov {outputDir}/normalized_coverage.lcov 0')
- c.run(f'mv {outputDir}/normalized_coverage.lcov {outputDir}/coverage.lcov')
+ version = os.getenv("BUILDER_VERSION")
+ c.run(f"llvm-profdata-{clang_version} merge -sparse -o {outputDir}/temp.profdata /tmp/code-*.profraw")
+ c.run(
+ f"llvm-cov-{clang_version} export --format=lcov --ignore-filename-regex='^/usr/' --ignore-filename-regex='ext/' -instr-profile={outputDir}/temp.profdata -object {binary} > {outputDir}/coverage.lcov"
+ )
+ c.run(
+ f"{outputDir}/.github/scripts/normalize_paths_in_coverage.py {outputDir} {product} {version} {outputDir}/coverage.lcov {outputDir}/normalized_coverage.lcov 0"
+ )
+ c.run(f"mv {outputDir}/normalized_coverage.lcov {outputDir}/coverage.lcov")
+
def setup_authbind(c):
- c.sudo('touch /etc/authbind/byport/53')
- c.sudo('chmod 755 /etc/authbind/byport/53')
- c.sudo('touch /etc/authbind/byport/!853')
- c.sudo('chmod 755 /etc/authbind/byport/!853')
+ c.sudo("touch /etc/authbind/byport/53")
+ c.sudo("chmod 755 /etc/authbind/byport/53")
+ c.sudo("touch /etc/authbind/byport/!853")
+ c.sudo("chmod 755 /etc/authbind/byport/!853")
+
# Builds and installs libfaketime from wolfcw/libfaketime (master)
def build_and_install_libfaketime(c):
- c.run(f'git clone https://github.com/wolfcw/libfaketime.git {repo_home}/libfaketime')
- with c.cd(f'{repo_home}/libfaketime'):
- c.run('git checkout master')
- c.run('make && sudo make install')
+ c.run(f"git clone https://github.com/wolfcw/libfaketime.git {repo_home}/libfaketime")
+ with c.cd(f"{repo_home}/libfaketime"):
+ c.run("git checkout master")
+ c.run("make && sudo make install")
+
auth_backend_test_deps = dict(
- gsqlite3=['sqlite3'],
- gmysql=['default-libmysqlclient-dev'],
- gpgsql=['libpq-dev'],
+ gsqlite3=["sqlite3"],
+ gmysql=["default-libmysqlclient-dev"],
+ gpgsql=["libpq-dev"],
lmdb=[],
remote=[],
bind=[],
lua2=[],
tinydns=[],
authpy=[],
- godbc_sqlite3=['libsqliteodbc'],
- godbc_mssql=['freetds-bin','tdsodbc'],
+ godbc_sqlite3=["libsqliteodbc"],
+ godbc_mssql=["freetds-bin", "tdsodbc"],
ldap=[],
- geoip_mmdb=[]
+ geoip_mmdb=[],
)
-@task(help={'backend': 'Backend to install test deps for, e.g. gsqlite3; can be repeated'}, iterable=['backend'], optional=['backend'])
+
+@task(
+ help={"backend": "Backend to install test deps for, e.g. gsqlite3; can be repeated"},
+ iterable=["backend"],
+ optional=["backend"],
+)
def install_auth_test_deps_only(c, backend):
- extra=[]
+ extra = []
for b in backend:
extra.extend(auth_backend_test_deps[b])
- c.sudo('apt-get update')
- c.sudo('DEBIAN_FRONTEND=noninteractive apt-get -y install ' + ' '.join(extra+auth_test_deps))
+ c.sudo("apt-get update")
+ c.sudo("DEBIAN_FRONTEND=noninteractive apt-get -y install " + " ".join(extra + auth_test_deps))
# install libfaketime manually
build_and_install_libfaketime(c)
-@task(help={'backend': 'Backend to install test deps for, e.g. gsqlite3; can be repeated'}, iterable=['backend'], optional=['backend'])
-def install_auth_test_deps(c, backend): # FIXME: rename this, we do way more than apt-get
+
+@task(
+ help={"backend": "Backend to install test deps for, e.g. gsqlite3; can be repeated"},
+ iterable=["backend"],
+ optional=["backend"],
+)
+def install_auth_test_deps(c, backend): # FIXME: rename this, we do way more than apt-get
install_auth_test_deps_only(c, backend)
- c.run('chmod +x /opt/pdns-auth/bin/* /opt/pdns-auth/sbin/*')
+ c.run("chmod +x /opt/pdns-auth/bin/* /opt/pdns-auth/sbin/*")
# c.run('''if [ ! -e $HOME/bin/jdnssec-verifyzone ]; then
# wget https://github.com/dblacka/jdnssec-tools/releases/download/0.14/jdnssec-tools-0.14.tar.gz
# tar xfz jdnssec-tools-0.14.tar.gz -C $HOME
# rm jdnssec-tools-0.14.tar.gz
# fi
# echo 'export PATH=$HOME/jdnssec-tools-0.14/bin:$PATH' >> $BASH_ENV''') # FIXME: why did this fail with no error?
- c.run('touch regression-tests/tests/verify-dnssec-zone/allow-missing regression-tests.nobackend/rectify-axfr/allow-missing') # FIXME: can this go?
+ c.run(
+ "touch regression-tests/tests/verify-dnssec-zone/allow-missing regression-tests.nobackend/rectify-axfr/allow-missing"
+ ) # FIXME: can this go?
# FIXME we may want to start a background recursor here to make ALIAS tests more robust
setup_authbind(c)
+
@task
-def install_rec_bulk_deps(c): # FIXME: rename this, we do way more than apt-get
- c.sudo('apt-get --no-install-recommends -y install ' + ' '.join(rec_bulk_deps))
- c.run('chmod +x /opt/pdns-recursor/bin/* /opt/pdns-recursor/sbin/*')
+def install_rec_bulk_deps(c): # FIXME: rename this, we do way more than apt-get
+ c.sudo("apt-get --no-install-recommends -y install " + " ".join(rec_bulk_deps))
+ c.run("chmod +x /opt/pdns-recursor/bin/* /opt/pdns-recursor/sbin/*")
+
@task
-def install_rec_bulk_ubicloud_deps(c): # FIXME: rename this, we do way more than apt-get
- c.sudo('apt-get --no-install-recommends -y install ' + ' '.join(rec_bulk_ubicloud_deps))
- c.run('chmod +x /opt/pdns-recursor/bin/* /opt/pdns-recursor/sbin/*')
+def install_rec_bulk_ubicloud_deps(c): # FIXME: rename this, we do way more than apt-get
+ c.sudo("apt-get --no-install-recommends -y install " + " ".join(rec_bulk_ubicloud_deps))
+ c.run("chmod +x /opt/pdns-recursor/bin/* /opt/pdns-recursor/sbin/*")
+
@task
-def install_rec_test_deps(c): # FIXME: rename this, we do way more than apt-get
- c.sudo('apt-get --no-install-recommends install -y ' + ' '.join(rec_bulk_deps) + ' \
+def install_rec_test_deps(c): # FIXME: rename this, we do way more than apt-get
+ c.sudo(
+ "apt-get --no-install-recommends install -y "
+ + " ".join(rec_bulk_deps)
+ + " \
pdns-server pdns-backend-bind daemontools \
jq lua-posix lua-socket bc authbind \
python3-venv python3-dev default-libmysqlclient-dev libpq-dev \
- protobuf-compiler snmpd prometheus')
- c.run('chmod +x /opt/pdns-recursor/bin/* /opt/pdns-recursor/sbin/*')
+ protobuf-compiler snmpd prometheus"
+ )
+ c.run("chmod +x /opt/pdns-recursor/bin/* /opt/pdns-recursor/sbin/*")
setup_authbind(c)
- c.run('sed "s/agentxperms 0700 0755 recursor/agentxperms 0777 0755/g" regression-tests.recursor-dnssec/snmpd.conf | sudo tee /etc/snmp/snmpd.conf')
- c.sudo('/etc/init.d/snmpd restart')
+ c.run(
+ 'sed "s/agentxperms 0700 0755 recursor/agentxperms 0777 0755/g" regression-tests.recursor-dnssec/snmpd.conf | sudo tee /etc/snmp/snmpd.conf'
+ )
+ c.sudo("/etc/init.d/snmpd restart")
time.sleep(5)
- c.sudo('chmod 755 /var/agentx')
+ c.sudo("chmod 755 /var/agentx")
# install libfaketime manually
build_and_install_libfaketime(c)
-@task(optional=['skipXDP'])
-def install_dnsdist_test_deps(c, skipXDP=False): # FIXME: rename this, we do way more than apt-get
+
+@task(optional=["skipXDP"])
+def install_dnsdist_test_deps(c, skipXDP=False): # FIXME: rename this, we do way more than apt-get
deps = 'libluajit-5.1-2 \
libboost-all-dev \
libcap2 \
protobuf-compiler \
python3-venv snmpd prometheus'
if not skipXDP:
- deps = deps + '\
+ deps = (
+ deps
+ + "\
libbpf1 \
- libxdp1'
-
- c.sudo(f'apt-get install -y {deps}')
- c.run('sed "s/agentxperms 0700 0755 dnsdist/agentxperms 0777 0755/g" regression-tests.dnsdist/snmpd.conf | sudo tee /etc/snmp/snmpd.conf')
- c.sudo('/etc/init.d/snmpd restart')
+ libxdp1"
+ )
+
+ c.sudo(f"apt-get install -y {deps}")
+ c.run(
+ 'sed "s/agentxperms 0700 0755 dnsdist/agentxperms 0777 0755/g" regression-tests.dnsdist/snmpd.conf | sudo tee /etc/snmp/snmpd.conf'
+ )
+ c.sudo("/etc/init.d/snmpd restart")
time.sleep(5)
- c.sudo('chmod 755 /var/agentx')
+ c.sudo("chmod 755 /var/agentx")
+
@task
def install_rec_build_deps(c):
- c.sudo('apt-get install -y --no-install-recommends ' + ' '.join(all_build_deps + git_build_deps + rec_build_deps))
+ c.sudo("apt-get install -y --no-install-recommends " + " ".join(all_build_deps + git_build_deps + rec_build_deps))
-@task(optional=['skipXDP'])
+
+@task(optional=["skipXDP"])
def install_dnsdist_build_deps(c, skipXDP=False):
- c.sudo('apt-get install -y --no-install-recommends ' + ' '.join(all_build_deps + git_build_deps + dnsdist_build_deps + (dnsdist_xdp_build_deps if not skipXDP else [])))
+ c.sudo(
+ "apt-get install -y --no-install-recommends "
+ + " ".join(
+ all_build_deps + git_build_deps + dnsdist_build_deps + (dnsdist_xdp_build_deps if not skipXDP else [])
+ )
+ )
+
@task
def ci_autoconf(c, meson=False):
if not meson:
- c.run('autoreconf -vfi')
+ c.run("autoreconf -vfi")
+
@task
def ci_docs_rec_generate(c):
- c.run('python3 generate.py')
+ c.run("python3 generate.py")
+
@task
def ci_metrics_rec_generate(c):
- c.run('python3 metrics.py')
+ c.run("python3 metrics.py")
+
@task
def ci_docs_dnsdist_generate(c):
- c.run('python3 dnsdist-settings-documentation-generator.py .')
+ c.run("python3 dnsdist-settings-documentation-generator.py .")
+
@task
def ci_docs_build(c):
- c.run('make -f Makefile.sphinx -C docs html')
+ c.run("make -f Makefile.sphinx -C docs html")
+
@task
def ci_docs_build_pdf(c):
- c.run('make -f Makefile.sphinx -C docs latexpdf')
+ c.run("make -f Makefile.sphinx -C docs latexpdf")
+
@task
def ci_docs_upload_master(c, docs_host, pdf, username, product, directory=""):
- rsync_cmd = " ".join([
- "rsync",
- "--checksum",
- "--recursive",
- "--verbose",
- "--no-p",
- "--chmod=g=rwX",
- "--exclude '*~'",
- ])
+ rsync_cmd = " ".join(
+ [
+ "rsync",
+ "--checksum",
+ "--recursive",
+ "--verbose",
+ "--no-p",
+ "--chmod=g=rwX",
+ "--exclude '*~'",
+ ]
+ )
c.run(f"{rsync_cmd} --delete ./docs/_build/{product}-html-docs/ {username}@{docs_host}:{directory}")
+
@task
def ci_docs_add_ssh(c, ssh_key, host_key):
- c.run('mkdir -m 700 -p ~/.ssh')
+ c.run("mkdir -m 700 -p ~/.ssh")
c.run(f'echo "{ssh_key}" > ~/.ssh/id_ed25519')
- c.run('chmod 600 ~/.ssh/id_ed25519')
+ c.run("chmod 600 ~/.ssh/id_ed25519")
c.run(f'echo "{host_key}" > ~/.ssh/known_hosts')
def get_sanitizers(meson=False):
- sanitizers = os.getenv('SANITIZERS', '')
+ sanitizers = os.getenv("SANITIZERS", "")
if meson:
- subst = {
- 'tsan': 'thread',
- 'asan': 'address',
- 'ubsan': 'undefined'
- }
- meson_sanitizers = ''
- sanitizers = sanitizers.split('+')
+ subst = {"tsan": "thread", "asan": "address", "ubsan": "undefined"}
+ meson_sanitizers = ""
+ sanitizers = sanitizers.split("+")
for sanitizer in sanitizers:
if sanitizer in subst:
- if meson_sanitizers != '':
- meson_sanitizers = meson_sanitizers + ','
+ if meson_sanitizers != "":
+ meson_sanitizers = meson_sanitizers + ","
meson_sanitizers = meson_sanitizers + subst[sanitizer]
else:
meson_sanitizers = meson_sanitizers + sanitizer
- return f'-D b_sanitize={meson_sanitizers}' if meson_sanitizers != '' else ''
- if sanitizers != '':
- sanitizers = sanitizers.split('+')
- sanitizers = ['--enable-' + sanitizer for sanitizer in sanitizers]
- sanitizers = ' '.join(sanitizers)
+ return f"-D b_sanitize={meson_sanitizers}" if meson_sanitizers != "" else ""
+ if sanitizers != "":
+ sanitizers = sanitizers.split("+")
+ sanitizers = ["--enable-" + sanitizer for sanitizer in sanitizers]
+ sanitizers = " ".join(sanitizers)
return sanitizers
+
def get_unit_tests(meson=False, auth=False):
- if os.getenv('UNIT_TESTS') != 'yes':
- return ''
+ if os.getenv("UNIT_TESTS") != "yes":
+ return ""
if meson:
- return '-D unit-tests=true -D unit-tests-backends=true' if auth else '-D unit-tests=true'
- return '--enable-unit-tests --enable-backend-unit-tests' if auth else '--enable-unit-tests'
+ return "-D unit-tests=true -D unit-tests-backends=true" if auth else "-D unit-tests=true"
+ return "--enable-unit-tests --enable-backend-unit-tests" if auth else "--enable-unit-tests"
+
def get_build_concurrency(default=8):
- return os.getenv('CONCURRENCY', default)
+ return os.getenv("CONCURRENCY", default)
+
def get_fuzzing_targets(meson=False):
if meson:
- return '-D fuzz-targets=true' if os.getenv('FUZZING_TARGETS') == 'yes' else ''
- return '--enable-fuzz-targets' if os.getenv('FUZZING_TARGETS') == 'yes' else ''
+ return "-D fuzz-targets=true" if os.getenv("FUZZING_TARGETS") == "yes" else ""
+ return "--enable-fuzz-targets" if os.getenv("FUZZING_TARGETS") == "yes" else ""
+
def is_compiler_clang():
- compiler = os.getenv('COMPILER', 'clang')
- return compiler == 'clang'
+ compiler = os.getenv("COMPILER", "clang")
+ return compiler == "clang"
+
def get_c_compiler(versioned_clang=True):
- compiler = f'clang' if is_compiler_clang() else 'gcc'
+ compiler = f"clang" if is_compiler_clang() else "gcc"
if is_compiler_clang() and versioned_clang:
- compiler = f'clang-{clang_version}'
+ compiler = f"clang-{clang_version}"
return compiler
+
def get_cxx_compiler(versioned_clang=True):
- compiler = f'clang++' if is_compiler_clang() else 'g++'
+ compiler = f"clang++" if is_compiler_clang() else "g++"
if is_compiler_clang() and versioned_clang:
- compiler = f'clang++-{clang_version}'
+ compiler = f"clang++-{clang_version}"
return compiler
+
def get_optimizations():
- optimizations = os.getenv('OPTIMIZATIONS', 'yes')
- return '-O1' if optimizations == 'yes' else '-O0'
+ optimizations = os.getenv("OPTIMIZATIONS", "yes")
+ return "-O1" if optimizations == "yes" else "-O0"
+
def get_protections():
- if platform.machine() in ['aarch64', 'arm64']:
- return "-fcf-protection=check",
- return "-fcf-protection=full",
+ if platform.machine() in ["aarch64", "arm64"]:
+ return ("-fcf-protection=check",)
+ return ("-fcf-protection=full",)
def get_cflags():
- return " ".join([
- get_optimizations(),
- "-Werror=vla",
- "-Werror=shadow",
- "-Wformat=2",
- "-Werror=format-security",
- "-fstack-clash-protection",
- "-fstack-protector-strong",
- "-Werror=string-plus-int" if is_compiler_clang() else '',
- ])
+ return " ".join(
+ [
+ get_optimizations(),
+ "-Werror=vla",
+ "-Werror=shadow",
+ "-Wformat=2",
+ "-Werror=format-security",
+ "-fstack-clash-protection",
+ "-fstack-protector-strong",
+ "-Werror=string-plus-int" if is_compiler_clang() else "",
+ ]
+ )
def get_cxxflags():
- return " ".join([
- get_cflags(),
- "-Wp,-D_GLIBCXX_ASSERTIONS",
- ])
-
-
-def get_base_configure_cmd(additional_c_flags='', additional_cxx_flags='', additional_ld_flags='', enable_systemd=True, enable_sodium=True, out_of_tree_build=False):
+ return " ".join(
+ [
+ get_cflags(),
+ "-Wp,-D_GLIBCXX_ASSERTIONS",
+ ]
+ )
+
+
+def get_base_configure_cmd(
+ additional_c_flags="",
+ additional_cxx_flags="",
+ additional_ld_flags="",
+ enable_systemd=True,
+ enable_sodium=True,
+ out_of_tree_build=False,
+):
cflags = " ".join([get_cflags(), additional_c_flags])
cxxflags = " ".join([get_cxxflags(), additional_cxx_flags])
ldflags = additional_ld_flags
- return " ".join([
- f'CFLAGS="{cflags}"',
- f'CXXFLAGS="{cxxflags}"',
- f'LDFLAGS="{ldflags}"',
- './configure' if not out_of_tree_build else '../configure',
- f"CC='{get_c_compiler()}'",
- f"CXX='{get_cxx_compiler()}'",
- "--enable-option-checking=fatal",
- "--enable-systemd" if enable_systemd else '',
- "--with-libsodium" if enable_sodium else '',
- "--enable-fortify-source=auto",
- "--enable-auto-var-init=pattern",
- get_coverage(),
- get_sanitizers()
- ])
-
-def get_base_configure_cmd_meson(build_dir, additional_c_flags='', additional_cxx_flags='', enable_systemd=True, enable_sodium=True, src_dir=''):
+ return " ".join(
+ [
+ f'CFLAGS="{cflags}"',
+ f'CXXFLAGS="{cxxflags}"',
+ f'LDFLAGS="{ldflags}"',
+ "./configure" if not out_of_tree_build else "../configure",
+ f"CC='{get_c_compiler()}'",
+ f"CXX='{get_cxx_compiler()}'",
+ "--enable-option-checking=fatal",
+ "--enable-systemd" if enable_systemd else "",
+ "--with-libsodium" if enable_sodium else "",
+ "--enable-fortify-source=auto",
+ "--enable-auto-var-init=pattern",
+ get_coverage(),
+ get_sanitizers(),
+ ]
+ )
+
+
+def get_base_configure_cmd_meson(
+ build_dir, additional_c_flags="", additional_cxx_flags="", enable_systemd=True, enable_sodium=True, src_dir=""
+):
cflags = " ".join([get_cflags(), additional_c_flags])
cxxflags = " ".join([get_cxxflags(), additional_cxx_flags])
- env = " ".join([
- f'CFLAGS="{cflags}"',
- f'CXXFLAGS="{cxxflags}"',
- f"CC='{get_c_compiler()}'",
- f"CXX='{get_cxx_compiler()}'"
- ])
- return " ".join([
- f'{env} meson setup {build_dir} {src_dir}',
- "-D systemd-service={}".format("enabled" if enable_systemd else "disabled"),
- "-D signers-libsodium={}".format("enabled" if enable_sodium else "disabled"),
- "-D hardening-fortify-source=auto",
- "-D auto-var-init=pattern",
- get_coverage(meson=True),
- get_sanitizers(meson=True)
- ])
+ env = " ".join(
+ [f'CFLAGS="{cflags}"', f'CXXFLAGS="{cxxflags}"', f"CC='{get_c_compiler()}'", f"CXX='{get_cxx_compiler()}'"]
+ )
+ return " ".join(
+ [
+ f"{env} meson setup {build_dir} {src_dir}",
+ "-D systemd-service={}".format("enabled" if enable_systemd else "disabled"),
+ "-D signers-libsodium={}".format("enabled" if enable_sodium else "disabled"),
+ "-D hardening-fortify-source=auto",
+ "-D auto-var-init=pattern",
+ get_coverage(meson=True),
+ get_sanitizers(meson=True),
+ ]
+ )
+
def ci_auth_configure_autotools(c):
unittests = get_unit_tests(auth=True)
fuzz_targets = get_fuzzing_targets()
- modules = " ".join([
- "bind",
- "geoip",
- "gmysql",
- "godbc",
- "gpgsql",
- "gsqlite3",
- "ldap",
- "lmdb",
- "lua2",
- "pipe",
- "remote",
- "tinydns",
- ])
- configure_cmd = " ".join([
- get_base_configure_cmd(),
- "LDFLAGS='-L/usr/local/lib -Wl,-rpath,/usr/local/lib'",
- f"--with-modules='{modules}'",
- "--enable-tools",
- "--enable-dns-over-tls",
- "--enable-experimental-pkcs11",
- "--enable-experimental-gss-tsig",
- "--enable-remotebackend-zeromq",
- "--enable-verbose-logging",
- "--with-lmdb=/usr",
- "--prefix=/opt/pdns-auth",
- "--enable-ixfrdist",
- unittests,
- fuzz_targets
- ])
+ modules = " ".join(
+ [
+ "bind",
+ "geoip",
+ "gmysql",
+ "godbc",
+ "gpgsql",
+ "gsqlite3",
+ "ldap",
+ "lmdb",
+ "lua2",
+ "pipe",
+ "remote",
+ "tinydns",
+ ]
+ )
+ configure_cmd = " ".join(
+ [
+ get_base_configure_cmd(),
+ "LDFLAGS='-L/usr/local/lib -Wl,-rpath,/usr/local/lib'",
+ f"--with-modules='{modules}'",
+ "--enable-tools",
+ "--enable-dns-over-tls",
+ "--enable-experimental-pkcs11",
+ "--enable-experimental-gss-tsig",
+ "--enable-remotebackend-zeromq",
+ "--enable-verbose-logging",
+ "--with-lmdb=/usr",
+ "--prefix=/opt/pdns-auth",
+ "--enable-ixfrdist",
+ unittests,
+ fuzz_targets,
+ ]
+ )
res = c.run(configure_cmd, warn=True)
if res.exited != 0:
- c.run('cat config.log')
+ c.run("cat config.log")
raise UnexpectedExit(res)
-AUTH_CONFIGURE_MESON_ALL_BACKENDS_STATIC = " ".join([
- "-D module-bind=static",
- "-D module-geoip=static",
- "-D module-gmysql=static",
- "-D module-godbc=static",
- "-D module-gpgsql=static",
- "-D module-gsqlite3=static",
- "-D module-ldap=static",
- "-D module-lmdb=static",
- "-D module-lua2=static",
- "-D module-pipe=static",
- "-D module-remote=static",
- "-D module-remote-zeromq=true",
- "-D module-tinydns=static"])
-
-AUTH_CONFIGURE_MESON_LEAST_BACKENDS_STATIC = " ".join([
- "-D module-bind=static",
- "-D module-gsqlite3=static",
- "-D module-remote=static"])
-
-AUTH_CONFIGURE_MESON_TOOLS = " ".join([
- "-D tools=true",
- "-D tools-ixfrdist=true"])
-
-AUTH_CONFIGURE_MESON_FEATURES = " ".join([
- "-D dns-over-tls=enabled",
- "-D experimental-pkcs11=enabled",
- "-D experimental-gss-tsig=enabled"])
-
-@task(help={
- 'features': 'What feature-set to build, one of "full" or "least". The former builds all backends, the latter only bind, gsqlite3 and remote',
- 'build_dir': 'Where Meson should configure into',
- 'clang': 'Use clang instead of GCC',
- 'ccache': 'Use ccache',
- 'tools': 'Build all tools (sdig, ixfrdist, etc.)',
- 'unit_tests': 'Enable unit tests',
- 'coverage': 'Create code coverage files',
-})
-def dev_auth_configure_meson(c, features, build_dir="build", clang=False, ccache=False, tools=True, unit_tests=False, coverage=False):
- additional_ld_flags = ''
- unittests=''
- cc = 'gcc'
- cxx = 'g++'
+
+AUTH_CONFIGURE_MESON_ALL_BACKENDS_STATIC = " ".join(
+ [
+ "-D module-bind=static",
+ "-D module-geoip=static",
+ "-D module-gmysql=static",
+ "-D module-godbc=static",
+ "-D module-gpgsql=static",
+ "-D module-gsqlite3=static",
+ "-D module-ldap=static",
+ "-D module-lmdb=static",
+ "-D module-lua2=static",
+ "-D module-pipe=static",
+ "-D module-remote=static",
+ "-D module-remote-zeromq=true",
+ "-D module-tinydns=static",
+ ]
+)
+
+AUTH_CONFIGURE_MESON_LEAST_BACKENDS_STATIC = " ".join(
+ ["-D module-bind=static", "-D module-gsqlite3=static", "-D module-remote=static"]
+)
+
+AUTH_CONFIGURE_MESON_TOOLS = " ".join(["-D tools=true", "-D tools-ixfrdist=true"])
+
+AUTH_CONFIGURE_MESON_FEATURES = " ".join(
+ ["-D dns-over-tls=enabled", "-D experimental-pkcs11=enabled", "-D experimental-gss-tsig=enabled"]
+)
+
+
+@task(
+ help={
+ "features": 'What feature-set to build, one of "full" or "least". The former builds all backends, the latter only bind, gsqlite3 and remote',
+ "build_dir": "Where Meson should configure into",
+ "clang": "Use clang instead of GCC",
+ "ccache": "Use ccache",
+ "tools": "Build all tools (sdig, ixfrdist, etc.)",
+ "unit_tests": "Enable unit tests",
+ "coverage": "Create code coverage files",
+ }
+)
+def dev_auth_configure_meson(
+ c, features, build_dir="build", clang=False, ccache=False, tools=True, unit_tests=False, coverage=False
+):
+ additional_ld_flags = ""
+ unittests = ""
+ cc = "gcc"
+ cxx = "g++"
if clang:
- additional_ld_flags += '-fuse-ld=lld '
- cc = 'clang'
- cxx = 'clang++'
+ additional_ld_flags += "-fuse-ld=lld "
+ cc = "clang"
+ cxx = "clang++"
- os.environ['COMPILER'] = cc
+ os.environ["COMPILER"] = cc
if ccache:
- cc = f'ccache {cc}'
- cxx = f'ccache {cxx}'
+ cc = f"ccache {cc}"
+ cxx = f"ccache {cxx}"
if unit_tests:
- unittests = '-D unit-tests=true'
+ unittests = "-D unit-tests=true"
cflags = " ".join([get_cflags()])
cxxflags = " ".join([get_cxxflags()])
tools_opts = ""
if tools:
- tools_opts = AUTH_CONFIGURE_MESON_TOOLS
+ tools_opts = AUTH_CONFIGURE_MESON_TOOLS
features_set = ""
- if features == 'full':
- features_set = AUTH_CONFIGURE_MESON_FEATURES
- backend_opts = AUTH_CONFIGURE_MESON_ALL_BACKENDS_STATIC
- elif features == 'least':
- backend_opts = AUTH_CONFIGURE_MESON_LEAST_BACKENDS_STATIC
+ if features == "full":
+ features_set = AUTH_CONFIGURE_MESON_FEATURES
+ backend_opts = AUTH_CONFIGURE_MESON_ALL_BACKENDS_STATIC
+ elif features == "least":
+ backend_opts = AUTH_CONFIGURE_MESON_LEAST_BACKENDS_STATIC
else:
raise KeyError(f'features should be one of "full" or "least", not "{features}"')
if coverage:
- os.environ['COVERAGE'] = 'yes'
+ os.environ["COVERAGE"] = "yes"
reconf_opt = "--reconfigure" if os.path.exists(build_dir) else ""
- env = " ".join([
- f"CC='{cc}'",
- f"CXX='{cxx}'",
- f'CFLAGS="{cflags}"',
- f'LDFLAGS="{additional_ld_flags}"',
- f'CXXFLAGS="{cxxflags}"',
- ])
- c.run(" ".join([
- f'{env} meson setup {build_dir}',
- reconf_opt,
- features_set,
- tools_opts,
- backend_opts,
- unittests,
- "-D hardening-fortify-source=auto",
- "-D auto-var-init=pattern",
- get_coverage(meson=True),
- get_sanitizers(meson=True)
- ]))
+ env = " ".join(
+ [
+ f"CC='{cc}'",
+ f"CXX='{cxx}'",
+ f'CFLAGS="{cflags}"',
+ f'LDFLAGS="{additional_ld_flags}"',
+ f'CXXFLAGS="{cxxflags}"',
+ ]
+ )
+ c.run(
+ " ".join(
+ [
+ f"{env} meson setup {build_dir}",
+ reconf_opt,
+ features_set,
+ tools_opts,
+ backend_opts,
+ unittests,
+ "-D hardening-fortify-source=auto",
+ "-D auto-var-init=pattern",
+ get_coverage(meson=True),
+ get_sanitizers(meson=True),
+ ]
+ )
+ )
+
def ci_auth_configure_meson(c, build_dir):
unittests = get_unit_tests(meson=True, auth=True)
fuzz_targets = get_fuzzing_targets(meson=True)
- configure_cmd = " ".join([
- "LDFLAGS='-L/usr/local/lib -Wl,-rpath,/usr/local/lib'",
- get_base_configure_cmd_meson(build_dir),
- "-D prefix=/opt/pdns-auth",
- AUTH_CONFIGURE_MESON_FEATURES,
- AUTH_CONFIGURE_MESON_ALL_BACKENDS_STATIC,
- AUTH_CONFIGURE_MESON_TOOLS,
- unittests,
- fuzz_targets
- ])
+ configure_cmd = " ".join(
+ [
+ "LDFLAGS='-L/usr/local/lib -Wl,-rpath,/usr/local/lib'",
+ get_base_configure_cmd_meson(build_dir),
+ "-D prefix=/opt/pdns-auth",
+ AUTH_CONFIGURE_MESON_FEATURES,
+ AUTH_CONFIGURE_MESON_ALL_BACKENDS_STATIC,
+ AUTH_CONFIGURE_MESON_TOOLS,
+ unittests,
+ fuzz_targets,
+ ]
+ )
res = c.run(configure_cmd, warn=True)
if res.exited != 0:
- c.run(f'cat {build_dir}/meson-logs/meson-log.txt')
+ c.run(f"cat {build_dir}/meson-logs/meson-log.txt")
raise UnexpectedExit(res)
+
@task
def ci_auth_configure(c, build_dir=None, meson=False):
if meson:
ci_auth_configure_autotools(c)
if build_dir:
ci_make_distdir(c)
- with c.cd(f'{build_dir}'):
+ with c.cd(f"{build_dir}"):
ci_auth_configure_autotools(c)
-REC_CONFIGURE_MESON_FEATURE_SET_FULL = " ".join([
- "-D dns-over-tls=enabled",
- "-D tls-libssl=enabled",
- "-D tls-gnutls=enabled",
- "-D nod=enabled",
- "-D libcap=enabled",
- "-D lua=luajit",
- "-D snmp=enabled"])
-
-REC_CONFIGURE_MESON_FEATURE_SET_LEAST = " ".join([
- "-D dnstap=disabled",
- "-D dns-over-tls=disabled",
- "-D tls-libssl=disabled",
- "-D tls-gnutls=disabled",
- "-D nod=disabled",
- "-D systemd-service=disabled",
- "-D lua=luajit",
- "-D libcap=disabled",
- "-D libcurl=disabled",
- "-D signers-libsodium=disabled",
- "-D snmp=disabled"])
+
+REC_CONFIGURE_MESON_FEATURE_SET_FULL = " ".join(
+ [
+ "-D dns-over-tls=enabled",
+ "-D tls-libssl=enabled",
+ "-D tls-gnutls=enabled",
+ "-D nod=enabled",
+ "-D libcap=enabled",
+ "-D lua=luajit",
+ "-D snmp=enabled",
+ ]
+)
+
+REC_CONFIGURE_MESON_FEATURE_SET_LEAST = " ".join(
+ [
+ "-D dnstap=disabled",
+ "-D dns-over-tls=disabled",
+ "-D tls-libssl=disabled",
+ "-D tls-gnutls=disabled",
+ "-D nod=disabled",
+ "-D systemd-service=disabled",
+ "-D lua=luajit",
+ "-D libcap=disabled",
+ "-D libcurl=disabled",
+ "-D signers-libsodium=disabled",
+ "-D snmp=disabled",
+ ]
+)
+
@task
-def dev_rec_configure_meson(c, features, build_dir="build", clang=False, ccache=False, unit_tests=False, coverage=False):
- additional_ld_flags = ''
- unittests=''
- cc = 'gcc'
- cxx = 'g++'
+def dev_rec_configure_meson(
+ c, features, build_dir="build", clang=False, ccache=False, unit_tests=False, coverage=False
+):
+ additional_ld_flags = ""
+ unittests = ""
+ cc = "gcc"
+ cxx = "g++"
if clang:
- additional_ld_flags += '-fuse-ld=lld '
- cc = 'clang'
- cxx = 'clang++'
+ additional_ld_flags += "-fuse-ld=lld "
+ cc = "clang"
+ cxx = "clang++"
- os.environ['COMPILER'] = cc
+ os.environ["COMPILER"] = cc
if ccache:
- cc = f'ccache {cc}'
- cxx = f'ccache {cxx}'
+ cc = f"ccache {cc}"
+ cxx = f"ccache {cxx}"
if unit_tests:
- unittests = '-D unit-tests=true'
+ unittests = "-D unit-tests=true"
cflags = " ".join([get_cflags()])
cxxflags = " ".join([get_cxxflags()])
- if features == 'full':
- features_set = REC_CONFIGURE_MESON_FEATURE_SET_FULL
+ if features == "full":
+ features_set = REC_CONFIGURE_MESON_FEATURE_SET_FULL
elif features == "least":
- features_set = REC_CONFIGURE_MESON_FEATURE_SET_LEAST
+ features_set = REC_CONFIGURE_MESON_FEATURE_SET_LEAST
else:
raise KeyError(f'features should be one of "full", "least", not "{features}"')
if coverage:
- os.environ['COVERAGE'] = 'yes'
+ os.environ["COVERAGE"] = "yes"
reconf_opt = "--reconfigure" if os.path.exists(build_dir) else ""
- env = " ".join([
- f"CC='{cc}'",
- f"CXX='{cxx}'",
- f'CFLAGS="{cflags}"',
- f'LDFLAGS="{additional_ld_flags}"',
- f'CXXFLAGS="{cxxflags}"',
- ])
- c.run(" ".join([
- f'{env} meson setup {build_dir}',
- reconf_opt,
- features_set,
- unittests,
- "-D hardening-fortify-source=auto",
- "-D auto-var-init=pattern",
- get_coverage(meson=True),
- get_sanitizers(meson=True)
- ]))
+ env = " ".join(
+ [
+ f"CC='{cc}'",
+ f"CXX='{cxx}'",
+ f'CFLAGS="{cflags}"',
+ f'LDFLAGS="{additional_ld_flags}"',
+ f'CXXFLAGS="{cxxflags}"',
+ ]
+ )
+ c.run(
+ " ".join(
+ [
+ f"{env} meson setup {build_dir}",
+ reconf_opt,
+ features_set,
+ unittests,
+ "-D hardening-fortify-source=auto",
+ "-D auto-var-init=pattern",
+ get_coverage(meson=True),
+ get_sanitizers(meson=True),
+ ]
+ )
+ )
def ci_rec_configure_meson(c, features, build_dir):
- builder_version = os.getenv('BUILDER_VERSION')
- dist_dir = '/tmp/rec-meson-dist-build'
- c.run(f'meson setup {dist_dir} && meson dist -C {dist_dir} --no-tests')
- with c.cd(f'{dist_dir}/meson-dist'):
- c.run(f'tar xf pdns-recursor-{builder_version}.tar.xz')
- src_dir = f'{dist_dir}/meson-dist/pdns-recursor-{builder_version}'
+ builder_version = os.getenv("BUILDER_VERSION")
+ dist_dir = "/tmp/rec-meson-dist-build"
+ c.run(f"meson setup {dist_dir} && meson dist -C {dist_dir} --no-tests")
+ with c.cd(f"{dist_dir}/meson-dist"):
+ c.run(f"tar xf pdns-recursor-{builder_version}.tar.xz")
+ src_dir = f"{dist_dir}/meson-dist/pdns-recursor-{builder_version}"
unittests = get_unit_tests(meson=True, auth=False)
if features == "full":
- configure_cmd = " ".join([
- "LDFLAGS='-L/usr/local/lib -Wl,-rpath,/usr/local/lib'",
- get_base_configure_cmd_meson(build_dir, src_dir=src_dir),
- "-D prefix=/opt/pdns-recursor",
- unittests,
- REC_CONFIGURE_MESON_FEATURE_SET_FULL
- ])
+ configure_cmd = " ".join(
+ [
+ "LDFLAGS='-L/usr/local/lib -Wl,-rpath,/usr/local/lib'",
+ get_base_configure_cmd_meson(build_dir, src_dir=src_dir),
+ "-D prefix=/opt/pdns-recursor",
+ unittests,
+ REC_CONFIGURE_MESON_FEATURE_SET_FULL,
+ ]
+ )
else:
- configure_cmd = " ".join([
- "LDFLAGS='-L/usr/local/lib -Wl,-rpath,/usr/local/lib'",
- get_base_configure_cmd_meson(build_dir, src_dir=src_dir),
- "-D prefix=/opt/pdns-recursor",
- unittests,
- REC_CONFIGURE_MESON_FEATURE_SET_LEAST
- ])
+ configure_cmd = " ".join(
+ [
+ "LDFLAGS='-L/usr/local/lib -Wl,-rpath,/usr/local/lib'",
+ get_base_configure_cmd_meson(build_dir, src_dir=src_dir),
+ "-D prefix=/opt/pdns-recursor",
+ unittests,
+ REC_CONFIGURE_MESON_FEATURE_SET_LEAST,
+ ]
+ )
res = c.run(configure_cmd, warn=True)
if res.exited != 0:
- c.run(f'cat {build_dir}/meson-logs/meson-log.txt')
+ c.run(f"cat {build_dir}/meson-logs/meson-log.txt")
raise UnexpectedExit(res)
+
def ci_rec_configure_autotools(c, features, build_dir=None):
unittests = get_unit_tests()
out_of_tree_build = build_dir is not None
- if features == 'full':
- configure_cmd = " ".join([
- get_base_configure_cmd(out_of_tree_build=out_of_tree_build),
- "--prefix=/opt/pdns-recursor",
- "--enable-option-checking",
- "--enable-verbose-logging",
- "--enable-dns-over-tls",
- "--enable-nod",
- "--with-libcap",
- "--with-lua=luajit",
- "--with-net-snmp",
- unittests,
- ])
+ if features == "full":
+ configure_cmd = " ".join(
+ [
+ get_base_configure_cmd(out_of_tree_build=out_of_tree_build),
+ "--prefix=/opt/pdns-recursor",
+ "--enable-option-checking",
+ "--enable-verbose-logging",
+ "--enable-dns-over-tls",
+ "--enable-nod",
+ "--with-libcap",
+ "--with-lua=luajit",
+ "--with-net-snmp",
+ unittests,
+ ]
+ )
else:
- configure_cmd = " ".join([
- get_base_configure_cmd(out_of_tree_build=out_of_tree_build),
- "--prefix=/opt/pdns-recursor",
- "--enable-option-checking",
- "--enable-verbose-logging",
- "--disable-dns-over-tls",
- "--disable-dnstap",
- "--disable-nod",
- "--disable-systemd",
- "--with-lua=luajit",
- "--without-libcap",
- "--without-libcurl",
- "--without-libsodium",
- "--without-net-snmp",
- unittests,
- ])
+ configure_cmd = " ".join(
+ [
+ get_base_configure_cmd(out_of_tree_build=out_of_tree_build),
+ "--prefix=/opt/pdns-recursor",
+ "--enable-option-checking",
+ "--enable-verbose-logging",
+ "--disable-dns-over-tls",
+ "--disable-dnstap",
+ "--disable-nod",
+ "--disable-systemd",
+ "--with-lua=luajit",
+ "--without-libcap",
+ "--without-libcurl",
+ "--without-libsodium",
+ "--without-net-snmp",
+ unittests,
+ ]
+ )
res = c.run(configure_cmd, warn=True)
if res.exited != 0:
- c.run('cat config.log')
+ c.run("cat config.log")
raise UnexpectedExit(res)
+
@task
def ci_rec_configure(c, features, build_dir=None, meson=False):
if meson:
ci_rec_configure_meson(c, features, build_dir)
else:
if build_dir:
- c.run(f'mkdir -p {build_dir}')
- with c.cd(f'{build_dir}'):
+ c.run(f"mkdir -p {build_dir}")
+ with c.cd(f"{build_dir}"):
ci_rec_configure_autotools(c, features, build_dir)
else:
ci_rec_configure_autotools(c, features)
-DNSDIST_CONFIGURE_CXXFLAGS_LEAST= " ".join([
- '-DDISABLE_COMPLETION',
- '-DDISABLE_DELAY_PIPE',
- '-DDISABLE_DYNBLOCKS',
- '-DDISABLE_PROMETHEUS',
- '-DDISABLE_PROTOBUF',
- '-DDISABLE_BUILTIN_HTML',
- '-DDISABLE_CARBON',
- '-DDISABLE_SECPOLL',
- '-DDISABLE_DEPRECATED_DYNBLOCK',
- '-DDISABLE_LUA_WEB_HANDLERS',
- '-DDISABLE_NON_FFI_DQ_BINDINGS',
- '-DDISABLE_POLICIES_BINDINGS',
- '-DDISABLE_PACKETCACHE_BINDINGS',
- '-DDISABLE_DOWNSTREAM_BINDINGS',
- '-DDISABLE_COMBO_ADDR_BINDINGS',
- '-DDISABLE_CLIENT_STATE_BINDINGS',
- '-DDISABLE_QPS_LIMITER_BINDINGS',
- '-DDISABLE_SUFFIX_MATCH_BINDINGS',
- '-DDISABLE_NETMASK_BINDINGS',
- '-DDISABLE_DNSNAME_BINDINGS',
- '-DDISABLE_DNSHEADER_BINDINGS',
- '-DDISABLE_RECVMMSG',
- '-DDISABLE_WEB_CACHE_MANAGEMENT',
- '-DDISABLE_WEB_CONFIG',
- '-DDISABLE_RULES_ALTERING_QUERIES',
- '-DDISABLE_ECS_ACTIONS',
- '-DDISABLE_TOP_N_BINDINGS',
- '-DDISABLE_OCSP_STAPLING',
- '-DDISABLE_HASHED_CREDENTIALS',
- '-DDISABLE_FALSE_SHARING_PADDING',
- '-DDISABLE_NPN'])
+
+DNSDIST_CONFIGURE_CXXFLAGS_LEAST = " ".join(
+ [
+ "-DDISABLE_COMPLETION",
+ "-DDISABLE_DELAY_PIPE",
+ "-DDISABLE_DYNBLOCKS",
+ "-DDISABLE_PROMETHEUS",
+ "-DDISABLE_PROTOBUF",
+ "-DDISABLE_BUILTIN_HTML",
+ "-DDISABLE_CARBON",
+ "-DDISABLE_SECPOLL",
+ "-DDISABLE_DEPRECATED_DYNBLOCK",
+ "-DDISABLE_LUA_WEB_HANDLERS",
+ "-DDISABLE_NON_FFI_DQ_BINDINGS",
+ "-DDISABLE_POLICIES_BINDINGS",
+ "-DDISABLE_PACKETCACHE_BINDINGS",
+ "-DDISABLE_DOWNSTREAM_BINDINGS",
+ "-DDISABLE_COMBO_ADDR_BINDINGS",
+ "-DDISABLE_CLIENT_STATE_BINDINGS",
+ "-DDISABLE_QPS_LIMITER_BINDINGS",
+ "-DDISABLE_SUFFIX_MATCH_BINDINGS",
+ "-DDISABLE_NETMASK_BINDINGS",
+ "-DDISABLE_DNSNAME_BINDINGS",
+ "-DDISABLE_DNSHEADER_BINDINGS",
+ "-DDISABLE_RECVMMSG",
+ "-DDISABLE_WEB_CACHE_MANAGEMENT",
+ "-DDISABLE_WEB_CONFIG",
+ "-DDISABLE_RULES_ALTERING_QUERIES",
+ "-DDISABLE_ECS_ACTIONS",
+ "-DDISABLE_TOP_N_BINDINGS",
+ "-DDISABLE_OCSP_STAPLING",
+ "-DDISABLE_HASHED_CREDENTIALS",
+ "-DDISABLE_FALSE_SHARING_PADDING",
+ "-DDISABLE_NPN",
+ ]
+)
+
@task
def ci_dnsdist_configure(c, features, builder, build_dir):
- additional_flags = ''
- additional_ld_flags = ''
+ additional_flags = ""
+ additional_ld_flags = ""
if is_compiler_clang():
- additional_ld_flags += '-fuse-ld=lld '
+ additional_ld_flags += "-fuse-ld=lld "
- if features == 'least':
+ if features == "least":
additional_flags = DNSDIST_CONFIGURE_CXXFLAGS_LEAST
- if builder == 'meson':
+ if builder == "meson":
cmd = ci_dnsdist_configure_meson(c, features, additional_flags, additional_ld_flags, build_dir)
- logfile = 'meson-logs/meson-log.txt'
+ logfile = "meson-logs/meson-log.txt"
else:
cmd = ci_dnsdist_configure_autotools(features, additional_flags, additional_ld_flags, build_dir)
- logfile = 'config.log'
+ logfile = "config.log"
res = c.run(cmd, warn=True)
if res.exited != 0:
- c.run(f'cat {logfile}')
+ c.run(f"cat {logfile}")
raise UnexpectedExit(res)
+
def ci_dnsdist_configure_autotools(features, additional_flags, additional_ld_flags, build_dir):
- if features == 'full':
- features_set = '--enable-dnstap \
+ if features == "full":
+ features_set = "--enable-dnstap \
--enable-dnscrypt \
--enable-dns-over-tls \
--enable-dns-over-https \
--with-libcap \
--with-net-snmp \
--with-nghttp2 \
- --with-re2'
+ --with-re2"
else:
- features_set = '--disable-dnstap \
+ features_set = "--disable-dnstap \
--disable-dnscrypt \
--disable-ipcipher \
--disable-ipcrypt2 \
--without-lmdb \
--without-net-snmp \
--without-nghttp2 \
- --without-re2'
+ --without-re2"
unittests = get_unit_tests()
fuzztargets = get_fuzzing_targets()
- tools = f'''AR=llvm-ar-{clang_version} RANLIB=llvm-ranlib-{clang_version}''' if is_compiler_clang() else ''
- out_of_tree_build = build_dir != ''
- return " ".join([
- tools,
- get_base_configure_cmd(additional_c_flags='', additional_cxx_flags=additional_flags, additional_ld_flags=additional_ld_flags, enable_systemd=False, enable_sodium=False, out_of_tree_build=out_of_tree_build),
- features_set,
- unittests,
- fuzztargets,
- '--enable-lto=thin',
- '--prefix=/opt/dnsdist'
- ])
-
-DNSDIST_CONFIGURE_MESON_FEATURE_SET_FULL = " ".join([
- '-D cdb=enabled',
- '-D dnscrypt=enabled',
- '-D dnstap=enabled',
- '-D ebpf=enabled',
- '-D ipcipher=enabled',
- '-D ipcrypt2=enabled',
- '-D libedit=enabled',
- '-D libsodium=enabled',
- '-D lmdb=enabled',
- '-D nghttp2=enabled',
- '-D re2=enabled',
- '-D systemd-service=enabled',
- '-D tls-gnutls=enabled',
- '-D dns-over-https=enabled',
- '-D dns-over-http3=enabled',
- '-D dns-over-quic=enabled',
- '-D dns-over-tls=enabled',
- '-D reproducible=true',
- '-D snmp=enabled',
- '-D yaml=enabled'])
-
-DNSDIST_CONFIGURE_MESON_FEATURE_SET_LEAST = " ".join([
- '-D cdb=disabled',
- '-D dnscrypt=disabled',
- '-D dnstap=disabled',
- '-D ebpf=disabled',
- '-D ipcipher=disabled',
- '-D ipcrypt2=disabled',
- '-D libedit=disabled',
- '-D libsodium=disabled',
- '-D lmdb=disabled',
- '-D nghttp2=disabled',
- '-D re2=disabled',
- '-D systemd-service=disabled',
- '-D tls-gnutls=disabled',
- '-D dns-over-https=disabled',
- '-D dns-over-http3=disabled',
- '-D dns-over-quic=disabled',
- '-D dns-over-tls=disabled',
- '-D reproducible=false',
- '-D snmp=disabled',
- '-D yaml=disabled'])
+ tools = f"""AR=llvm-ar-{clang_version} RANLIB=llvm-ranlib-{clang_version}""" if is_compiler_clang() else ""
+ out_of_tree_build = build_dir != ""
+ return " ".join(
+ [
+ tools,
+ get_base_configure_cmd(
+ additional_c_flags="",
+ additional_cxx_flags=additional_flags,
+ additional_ld_flags=additional_ld_flags,
+ enable_systemd=False,
+ enable_sodium=False,
+ out_of_tree_build=out_of_tree_build,
+ ),
+ features_set,
+ unittests,
+ fuzztargets,
+ "--enable-lto=thin",
+ "--prefix=/opt/dnsdist",
+ ]
+ )
+
+
+DNSDIST_CONFIGURE_MESON_FEATURE_SET_FULL = " ".join(
+ [
+ "-D cdb=enabled",
+ "-D dnscrypt=enabled",
+ "-D dnstap=enabled",
+ "-D ebpf=enabled",
+ "-D ipcipher=enabled",
+ "-D ipcrypt2=enabled",
+ "-D libedit=enabled",
+ "-D libsodium=enabled",
+ "-D lmdb=enabled",
+ "-D nghttp2=enabled",
+ "-D re2=enabled",
+ "-D systemd-service=enabled",
+ "-D tls-gnutls=enabled",
+ "-D dns-over-https=enabled",
+ "-D dns-over-http3=enabled",
+ "-D dns-over-quic=enabled",
+ "-D dns-over-tls=enabled",
+ "-D reproducible=true",
+ "-D snmp=enabled",
+ "-D yaml=enabled",
+ ]
+)
+
+DNSDIST_CONFIGURE_MESON_FEATURE_SET_LEAST = " ".join(
+ [
+ "-D cdb=disabled",
+ "-D dnscrypt=disabled",
+ "-D dnstap=disabled",
+ "-D ebpf=disabled",
+ "-D ipcipher=disabled",
+ "-D ipcrypt2=disabled",
+ "-D libedit=disabled",
+ "-D libsodium=disabled",
+ "-D lmdb=disabled",
+ "-D nghttp2=disabled",
+ "-D re2=disabled",
+ "-D systemd-service=disabled",
+ "-D tls-gnutls=disabled",
+ "-D dns-over-https=disabled",
+ "-D dns-over-http3=disabled",
+ "-D dns-over-quic=disabled",
+ "-D dns-over-tls=disabled",
+ "-D reproducible=false",
+ "-D snmp=disabled",
+ "-D yaml=disabled",
+ ]
+)
+
def ci_dnsdist_configure_meson(c, features, additional_flags, additional_ld_flags, build_dir):
- if features == 'full':
- features_set = DNSDIST_CONFIGURE_MESON_FEATURE_SET_FULL
+ if features == "full":
+ features_set = DNSDIST_CONFIGURE_MESON_FEATURE_SET_FULL
else:
- features_set = DNSDIST_CONFIGURE_MESON_FEATURE_SET_LEAST
+ features_set = DNSDIST_CONFIGURE_MESON_FEATURE_SET_LEAST
unittests = get_unit_tests(meson=True)
fuzztargets = get_fuzzing_targets(meson=True)
- tools = f'''AR=llvm-ar-{clang_version} RANLIB=llvm-ranlib-{clang_version}''' if is_compiler_clang() else ''
+ tools = f"""AR=llvm-ar-{clang_version} RANLIB=llvm-ranlib-{clang_version}""" if is_compiler_clang() else ""
cflags = " ".join([get_cflags()])
cxxflags = " ".join([get_cxxflags(), additional_flags])
- env = " ".join([
- tools,
- f'CFLAGS="{cflags}"',
- f'LDFLAGS="{additional_ld_flags}"',
- f'CXXFLAGS="{cxxflags}"',
- f"CC='{get_c_compiler()}'",
- f"CXX='{get_cxx_compiler()}'",
- ])
-
- builder_version = os.getenv('BUILDER_VERSION')
- dist_dir = '/tmp/dnsdist-meson-dist-build'
-
- c.run(f'. {repo_home}/.venv/bin/activate && meson setup {dist_dir} && meson dist -C {dist_dir} --no-tests')
- with c.cd(f'{dist_dir}/meson-dist/'):
- c.run(f'tar xf dnsdist-{builder_version}.tar.xz')
-
- src_dir = f'{dist_dir}/meson-dist/dnsdist-{builder_version}'
- return " ".join([
- f'. {repo_home}/.venv/bin/activate && {env} meson setup {build_dir} {src_dir}',
- features_set,
- unittests,
- fuzztargets,
- "-D hardening-fortify-source=auto",
- "-D auto-var-init=pattern",
- get_coverage(meson=True),
- get_sanitizers(meson=True)
- ])
-
-@task(help={
- 'features': 'What feature-set to build, one of "full" or "least"',
- 'build_dir': 'Where Meson should configure into',
- 'clang': 'Use clang instead of GCC',
- 'ccache': 'Use ccache',
- 'unit_tests': 'Enable unit tests',
- 'coverage': 'Create code coverage files',
-})
-def dev_dnsdist_configure_meson(c, features, build_dir="build", clang=False, ccache=False, unit_tests=False, coverage=False):
- '''
+ env = " ".join(
+ [
+ tools,
+ f'CFLAGS="{cflags}"',
+ f'LDFLAGS="{additional_ld_flags}"',
+ f'CXXFLAGS="{cxxflags}"',
+ f"CC='{get_c_compiler()}'",
+ f"CXX='{get_cxx_compiler()}'",
+ ]
+ )
+
+ builder_version = os.getenv("BUILDER_VERSION")
+ dist_dir = "/tmp/dnsdist-meson-dist-build"
+
+ c.run(f". {repo_home}/.venv/bin/activate && meson setup {dist_dir} && meson dist -C {dist_dir} --no-tests")
+ with c.cd(f"{dist_dir}/meson-dist/"):
+ c.run(f"tar xf dnsdist-{builder_version}.tar.xz")
+
+ src_dir = f"{dist_dir}/meson-dist/dnsdist-{builder_version}"
+ return " ".join(
+ [
+ f". {repo_home}/.venv/bin/activate && {env} meson setup {build_dir} {src_dir}",
+ features_set,
+ unittests,
+ fuzztargets,
+ "-D hardening-fortify-source=auto",
+ "-D auto-var-init=pattern",
+ get_coverage(meson=True),
+ get_sanitizers(meson=True),
+ ]
+ )
+
+
+@task(
+ help={
+ "features": 'What feature-set to build, one of "full" or "least"',
+ "build_dir": "Where Meson should configure into",
+ "clang": "Use clang instead of GCC",
+ "ccache": "Use ccache",
+ "unit_tests": "Enable unit tests",
+ "coverage": "Create code coverage files",
+ }
+)
+def dev_dnsdist_configure_meson(
+ c, features, build_dir="build", clang=False, ccache=False, unit_tests=False, coverage=False
+):
+ """
Configures dnsdist using Meson.
- '''
- additional_ld_flags = ''
- unittests=''
- cc = 'gcc'
- cxx = 'g++'
+ """
+ additional_ld_flags = ""
+ unittests = ""
+ cc = "gcc"
+ cxx = "g++"
if clang:
- additional_ld_flags += '-fuse-ld=lld '
- cc = 'clang'
- cxx = 'clang++'
+ additional_ld_flags += "-fuse-ld=lld "
+ cc = "clang"
+ cxx = "clang++"
- os.environ['COMPILER'] = cc
+ os.environ["COMPILER"] = cc
if ccache:
- cc = f'ccache {cc}'
- cxx = f'ccache {cxx}'
+ cc = f"ccache {cc}"
+ cxx = f"ccache {cxx}"
if unit_tests:
- unittests = '-D unit-tests=true'
-
+ unittests = "-D unit-tests=true"
cflags = " ".join([get_cflags()])
cxxflags = " ".join([get_cxxflags()])
- if features == 'full':
- features_set = DNSDIST_CONFIGURE_MESON_FEATURE_SET_FULL
+ if features == "full":
+ features_set = DNSDIST_CONFIGURE_MESON_FEATURE_SET_FULL
elif features == "least":
- features_set = DNSDIST_CONFIGURE_MESON_FEATURE_SET_LEAST
- cxxflags = " ".join([cxxflags, DNSDIST_CONFIGURE_CXXFLAGS_LEAST])
+ features_set = DNSDIST_CONFIGURE_MESON_FEATURE_SET_LEAST
+ cxxflags = " ".join([cxxflags, DNSDIST_CONFIGURE_CXXFLAGS_LEAST])
else:
raise KeyError(f'features should be one of "full", "least", not "{features}"')
if coverage:
- os.environ['COVERAGE'] = 'yes'
+ os.environ["COVERAGE"] = "yes"
reconf_opt = "--reconfigure" if os.path.exists(build_dir) else ""
- env = " ".join([
- f"CC='{cc}'",
- f"CXX='{cxx}'",
- f'CFLAGS="{cflags}"',
- f'LDFLAGS="{additional_ld_flags}"',
- f'CXXFLAGS="{cxxflags}"',
- ])
- c.run(" ".join([
- f'{env} meson setup {build_dir}',
- reconf_opt,
- features_set,
- unittests,
- "-D hardening-fortify-source=auto",
- "-D auto-var-init=pattern",
- get_coverage(meson=True),
- get_sanitizers(meson=True)
- ]))
+ env = " ".join(
+ [
+ f"CC='{cc}'",
+ f"CXX='{cxx}'",
+ f'CFLAGS="{cflags}"',
+ f'LDFLAGS="{additional_ld_flags}"',
+ f'CXXFLAGS="{cxxflags}"',
+ ]
+ )
+ c.run(
+ " ".join(
+ [
+ f"{env} meson setup {build_dir}",
+ reconf_opt,
+ features_set,
+ unittests,
+ "-D hardening-fortify-source=auto",
+ "-D auto-var-init=pattern",
+ get_coverage(meson=True),
+ get_sanitizers(meson=True),
+ ]
+ )
+ )
+
@task
def ci_auth_make(c):
- c.run(f'make -j{get_build_concurrency()} -k V=1')
+ c.run(f"make -j{get_build_concurrency()} -k V=1")
+
@task
def ci_auth_make_bear(c):
- c.run(f'bear --append -- make -j{get_build_concurrency()} -k V=1')
+ c.run(f"bear --append -- make -j{get_build_concurrency()} -k V=1")
+
def run_ninja(c):
- c.run(f'ninja -j{get_build_concurrency()} --verbose')
+ c.run(f"ninja -j{get_build_concurrency()} --verbose")
+
@task
def ci_auth_build(c, meson=False):
else:
ci_auth_make_bear(c)
+
@task
def ci_rec_make_bear(c):
# Assumed to be running under ./pdns/recursordist/
- c.run(f'bear --append -- make -j{get_build_concurrency()} -k V=1')
+ c.run(f"bear --append -- make -j{get_build_concurrency()} -k V=1")
+
@task
def ci_rec_build(c, meson=False):
else:
ci_rec_make_bear(c)
+
@task
def ci_dnsdist_make(c):
- c.run(f'make -j{get_build_concurrency(4)} -k V=1')
+ c.run(f"make -j{get_build_concurrency(4)} -k V=1")
+
def ci_dnsdist_run_ninja(c):
- c.run(f'. {repo_home}/.venv/bin/activate && ninja -j{get_build_concurrency(4)} --verbose')
+ c.run(f". {repo_home}/.venv/bin/activate && ninja -j{get_build_concurrency(4)} --verbose")
+
@task
def ci_dnsdist_make_bear(c, builder):
- if builder == 'meson':
+ if builder == "meson":
ci_dnsdist_run_ninja(c)
return
# Assumed to be running under ./pdns/dnsdistdist/
- c.run(f'bear --append -- make -j{get_build_concurrency(4)} -k V=1')
+ c.run(f"bear --append -- make -j{get_build_concurrency(4)} -k V=1")
+
@task
def ci_auth_install_remotebackend_test_deps(c):
- c.sudo('apt-get install -y socat')
+ c.sudo("apt-get install -y socat")
+
@task
def ci_auth_run_unit_tests(c, meson=False):
if meson:
suite_timeout_sec = 120
- logfile = 'meson-logs/testlog.txt'
- c.run(f'touch {repo_home}/regression-tests/tests/verify-dnssec-zone/allow-missing {repo_home}/regression-tests.nobackend/rectify-axfr/allow-missing') # FIXME: can this go?
- res = c.run(f'meson test --verbose -t {suite_timeout_sec}', warn=True)
+ logfile = "meson-logs/testlog.txt"
+ c.run(
+ f"touch {repo_home}/regression-tests/tests/verify-dnssec-zone/allow-missing {repo_home}/regression-tests.nobackend/rectify-axfr/allow-missing"
+ ) # FIXME: can this go?
+ res = c.run(f"meson test --verbose -t {suite_timeout_sec}", warn=True)
else:
- logfile = 'pdns/test-suite.log'
- res = c.run('make check', warn=True)
+ logfile = "pdns/test-suite.log"
+ res = c.run("make check", warn=True)
if res.exited != 0:
- c.run(f'cat {logfile}', warn=True)
- c.run('cat ../modules/remotebackend/*.log', warn=True)
+ c.run(f"cat {logfile}", warn=True)
+ c.run("cat ../modules/remotebackend/*.log", warn=True)
raise UnexpectedExit(res)
+
@task
def ci_rec_run_unit_tests(c, meson=False):
if meson:
suite_timeout_sec = 120
- logfile = 'meson-logs/testlog.txt'
- res = c.run(f'meson test --verbose -t {suite_timeout_sec}', warn=True)
+ logfile = "meson-logs/testlog.txt"
+ res = c.run(f"meson test --verbose -t {suite_timeout_sec}", warn=True)
else:
- logfile = 'test-suite.log'
- res = c.run('make check', warn=True)
+ logfile = "test-suite.log"
+ res = c.run("make check", warn=True)
if res.exited != 0:
- c.run(f'cat {logfile}', warn=True)
+ c.run(f"cat {logfile}", warn=True)
raise UnexpectedExit(res)
+
@task
def ci_dnsdist_run_unit_tests(c, builder):
- if builder == 'meson':
+ if builder == "meson":
suite_timeout_sec = 120
- logfile = 'meson-logs/testlog.txt'
- res = c.run(f'. {repo_home}/.venv/bin/activate && meson test --verbose -t {suite_timeout_sec}', warn=True)
+ logfile = "meson-logs/testlog.txt"
+ res = c.run(f". {repo_home}/.venv/bin/activate && meson test --verbose -t {suite_timeout_sec}", warn=True)
else:
- logfile = 'test-suite.log'
- res = c.run('make check', warn=True)
+ logfile = "test-suite.log"
+ res = c.run("make check", warn=True)
if res.exited != 0:
- c.run(f'cat {logfile}', warn=True)
- raise UnexpectedExit(res)
+ c.run(f"cat {logfile}", warn=True)
+ raise UnexpectedExit(res)
+
@task
def ci_make_distdir(c, meson=False):
if not meson:
- c.run('make distdir')
+ c.run("make distdir")
+
@task
def ci_auth_install(c, meson=False):
if not meson:
- c.run('make install') # FIXME: this builds auth docs - again
+ c.run("make install") # FIXME: this builds auth docs - again
+
@task
def ci_rec_install(c, meson=False):
if meson:
c.sudo(f"bash -c 'source {repo_home}/.venv/bin/activate && meson install'")
else:
- c.run('make install')
+ c.run("make install")
+
@task
def ci_dnsdist_install(c, meson=False):
if meson:
c.sudo(f"bash -c 'source {repo_home}/.venv/bin/activate && meson install'")
else:
- c.run('make install')
+ c.run("make install")
+
@task
def add_auth_repo(c, dist_name, dist_release_name, pdns_repo_version):
- c.sudo('apt-get install -y curl gnupg2')
- if pdns_repo_version == 'master':
- c.sudo('curl -s -o /etc/apt/trusted.gpg.d/pdns-repo.asc https://repo.powerdns.com/CBC8B383-pub.asc')
+ c.sudo("apt-get install -y curl gnupg2")
+ if pdns_repo_version == "master":
+ c.sudo("curl -s -o /etc/apt/trusted.gpg.d/pdns-repo.asc https://repo.powerdns.com/CBC8B383-pub.asc")
else:
- c.sudo('curl -s -o /etc/apt/trusted.gpg.d/pdns-repo.asc https://repo.powerdns.com/FD380FBB-pub.asc')
- c.run(f"echo 'deb [arch=amd64] http://repo.powerdns.com/{dist_name} {dist_release_name}-auth-{pdns_repo_version} main' | sudo tee /etc/apt/sources.list.d/pdns.list")
+ c.sudo("curl -s -o /etc/apt/trusted.gpg.d/pdns-repo.asc https://repo.powerdns.com/FD380FBB-pub.asc")
+ c.run(
+ f"echo 'deb [arch=amd64] http://repo.powerdns.com/{dist_name} {dist_release_name}-auth-{pdns_repo_version} main' | sudo tee /etc/apt/sources.list.d/pdns.list"
+ )
c.run("echo 'Package: pdns-*' | sudo tee /etc/apt/preferences.d/pdns")
c.run("echo 'Pin: origin repo.powerdns.com' | sudo tee -a /etc/apt/preferences.d/pdns")
c.run("echo 'Pin-Priority: 600' | sudo tee -a /etc/apt/preferences.d/pdns")
- c.sudo('apt-get update')
+ c.sudo("apt-get update")
+
@task
-def test_api(c, product, backend=''):
- if product == 'recursor':
- with c.cd('regression-tests.api'):
- c.run(f'PDNSRECURSOR=/opt/pdns-recursor/sbin/pdns_recursor ./runtests recursor {backend}')
- elif product == 'auth':
- with c.cd('regression-tests.api'):
- c.run(f'PDNSSERVER=/opt/pdns-auth/sbin/pdns_server PDNSUTIL=/opt/pdns-auth/bin/pdnsutil SDIG=/opt/pdns-auth/bin/sdig MYSQL_HOST={auth_backend_ip_addr} PGHOST={auth_backend_ip_addr} PGPORT=5432 ./runtests authoritative {backend}')
+def test_api(c, product, backend=""):
+ if product == "recursor":
+ with c.cd("regression-tests.api"):
+ c.run(f"PDNSRECURSOR=/opt/pdns-recursor/sbin/pdns_recursor ./runtests recursor {backend}")
+ elif product == "auth":
+ with c.cd("regression-tests.api"):
+ c.run(
+ f"PDNSSERVER=/opt/pdns-auth/sbin/pdns_server PDNSUTIL=/opt/pdns-auth/bin/pdnsutil SDIG=/opt/pdns-auth/bin/sdig MYSQL_HOST={auth_backend_ip_addr} PGHOST={auth_backend_ip_addr} PGPORT=5432 ./runtests authoritative {backend}"
+ )
else:
- raise Failure('unknown product')
+ raise Failure("unknown product")
+
backend_regress_tests = dict(
- bind = [
- 'bind-both',
- 'bind-dnssec-both',
- 'bind-dnssec-nsec3-both',
- 'bind-dnssec-nsec3-optout-both',
- 'bind-dnssec-nsec3-narrow',
- 'bind-dnssec-pkcs11'
- ],
- geoip = [
- 'geoip',
- 'geoip-nsec3-narrow'
+ bind=[
+ "bind-both",
+ "bind-dnssec-both",
+ "bind-dnssec-nsec3-both",
+ "bind-dnssec-nsec3-optout-both",
+ "bind-dnssec-nsec3-narrow",
+ "bind-dnssec-pkcs11",
],
- lua2 = ['lua2', 'lua2-dnssec'],
- tinydns = ['tinydns'],
- remote = [
- 'remotebackend-pipe',
- 'remotebackend-unix',
- 'remotebackend-http',
- 'remotebackend-zeromq',
- 'remotebackend-pipe-dnssec',
- 'remotebackend-unix-dnssec',
- 'remotebackend-http-dnssec',
- 'remotebackend-zeromq-dnssec'
+ geoip=["geoip", "geoip-nsec3-narrow"],
+ lua2=["lua2", "lua2-dnssec"],
+ tinydns=["tinydns"],
+ remote=[
+ "remotebackend-pipe",
+ "remotebackend-unix",
+ "remotebackend-http",
+ "remotebackend-zeromq",
+ "remotebackend-pipe-dnssec",
+ "remotebackend-unix-dnssec",
+ "remotebackend-http-dnssec",
+ "remotebackend-zeromq-dnssec",
],
- lmdb = [
- 'lmdb-nodnssec-both',
- 'lmdb-both',
- 'lmdb-nsec3-both',
- 'lmdb-nsec3-optout-both',
- 'lmdb-nsec3-narrow',
- 'lmdb-nodnssec-variant',
- 'lmdb-variant',
- 'lmdb-nsec3-variant',
- 'lmdb-nsec3-optout-variant',
- 'lmdb-nsec3-narrow-variant'
+ lmdb=[
+ "lmdb-nodnssec-both",
+ "lmdb-both",
+ "lmdb-nsec3-both",
+ "lmdb-nsec3-optout-both",
+ "lmdb-nsec3-narrow",
+ "lmdb-nodnssec-variant",
+ "lmdb-variant",
+ "lmdb-nsec3-variant",
+ "lmdb-nsec3-optout-variant",
+ "lmdb-nsec3-narrow-variant",
],
- gmysql = [
- 'gmysql',
- 'gmysql-nodnssec-both',
- 'gmysql-nsec3-both',
- 'gmysql-nsec3-optout-both',
- 'gmysql-nsec3-narrow',
- 'gmysql_sp-both'
- ],
- gpgsql = [
- 'gpgsql',
- 'gpgsql-nodnssec-both',
- 'gpgsql-nsec3-both',
- 'gpgsql-nsec3-optout-both',
- 'gpgsql-nsec3-narrow',
- 'gpgsql_sp-both'
+ gmysql=[
+ "gmysql",
+ "gmysql-nodnssec-both",
+ "gmysql-nsec3-both",
+ "gmysql-nsec3-optout-both",
+ "gmysql-nsec3-narrow",
+ "gmysql_sp-both",
],
- gsqlite3 = [
- 'gsqlite3',
- 'gsqlite3-nodnssec-both',
- 'gsqlite3-nsec3-both',
- 'gsqlite3-nsec3-optout-both',
- 'gsqlite3-nsec3-narrow'
+ gpgsql=[
+ "gpgsql",
+ "gpgsql-nodnssec-both",
+ "gpgsql-nsec3-both",
+ "gpgsql-nsec3-optout-both",
+ "gpgsql-nsec3-narrow",
+ "gpgsql_sp-both",
],
- godbc_sqlite3 = ['godbc_sqlite3-nodnssec'],
- godbc_mssql = [
- 'godbc_mssql',
- 'godbc_mssql-nodnssec',
- 'godbc_mssql-nsec3',
- 'godbc_mssql-nsec3-optout',
- 'godbc_mssql-nsec3-narrow'
+ gsqlite3=[
+ "gsqlite3",
+ "gsqlite3-nodnssec-both",
+ "gsqlite3-nsec3-both",
+ "gsqlite3-nsec3-optout-both",
+ "gsqlite3-nsec3-narrow",
],
- ldap = [
- 'ldap-tree',
- 'ldap-simple',
- 'ldap-strict'
+ godbc_sqlite3=["godbc_sqlite3-nodnssec"],
+ godbc_mssql=[
+ "godbc_mssql",
+ "godbc_mssql-nodnssec",
+ "godbc_mssql-nsec3",
+ "godbc_mssql-nsec3-optout",
+ "godbc_mssql-nsec3-narrow",
],
- geoip_mmdb = ['geoip'],
+ ldap=["ldap-tree", "ldap-simple", "ldap-strict"],
+ geoip_mmdb=["geoip"],
)
backend_rootzone_tests = dict(
- geoip = False,
- geoip_mmdb = False,
- lua2 = False,
- ldap = False,
- tinydns = False,
- remote = False,
- bind = True,
- lmdb = True,
- gmysql = True,
- gpgsql = True,
- gsqlite3 = True,
- godbc_sqlite3 = True,
- godbc_mssql = True,
+ geoip=False,
+ geoip_mmdb=False,
+ lua2=False,
+ ldap=False,
+ tinydns=False,
+ remote=False,
+ bind=True,
+ lmdb=True,
+ gmysql=True,
+ gpgsql=True,
+ gsqlite3=True,
+ godbc_sqlite3=True,
+ godbc_mssql=True,
)
godbc_mssql_credentials = {"username": "sa", "password": "SAsa12%%-not-a-secret-password"}
-godbc_config = f'''
+godbc_config = f"""
[pdns-mssql-docker]
Driver=FreeTDS
Trace=No
[pdns-sqlite3-2]
Driver = SQLite3
Database = pdns.sqlite32
-'''
+"""
+
def setup_godbc_mssql(c):
with open(os.path.expanduser("~/.odbc.ini"), "a") as f:
f.write(godbc_config)
- c.sudo('sh -c \'echo "Threading=1" | cat /usr/share/tdsodbc/odbcinst.ini - | tee -a /etc/odbcinst.ini\'')
+ c.sudo("sh -c 'echo \"Threading=1\" | cat /usr/share/tdsodbc/odbcinst.ini - | tee -a /etc/odbcinst.ini'")
c.sudo('sed -i "s/libtdsodbc.so/\/usr\/lib\/x86_64-linux-gnu\/odbc\/libtdsodbc.so/g" /etc/odbcinst.ini')
- c.run(f'echo "create database pdns" | isql -v pdns-mssql-docker-nodb {godbc_mssql_credentials["username"]} {godbc_mssql_credentials["password"]}')
+ c.run(
+ f'echo "create database pdns" | isql -v pdns-mssql-docker-nodb {godbc_mssql_credentials["username"]} {godbc_mssql_credentials["password"]}'
+ )
# FIXME: Skip 8bit-txt-unescaped test
- c.run('touch ${PWD}/regression-tests/tests/8bit-txt-unescaped/skip')
+ c.run("touch ${PWD}/regression-tests/tests/8bit-txt-unescaped/skip")
+
def setup_godbc_sqlite3(c):
with open(os.path.expanduser("~/.odbc.ini"), "a") as f:
f.write(godbc_config)
c.sudo('sed -i "s/libsqlite3odbc.so/\/usr\/lib\/x86_64-linux-gnu\/odbc\/libsqlite3odbc.so/g" /etc/odbcinst.ini')
+
def setup_ldap_client(c):
- c.sudo('DEBIAN_FRONTEND=noninteractive apt-get install -y ldap-utils')
- c.sudo(f'sh -c \'echo "{auth_backend_ip_addr} ldapserver" | tee -a /etc/hosts\'')
+ c.sudo("DEBIAN_FRONTEND=noninteractive apt-get install -y ldap-utils")
+ c.sudo(f"sh -c 'echo \"{auth_backend_ip_addr} ldapserver\" | tee -a /etc/hosts'")
+
def setup_softhsm(c):
# Modify the location of the softhsm tokens and configuration directory.
# Enables token generation by non-root users (runner)
- c.run('mkdir -p /opt/pdns-auth/softhsm/tokens')
+ c.run("mkdir -p /opt/pdns-auth/softhsm/tokens")
c.run('echo "directories.tokendir = /opt/pdns-auth/softhsm/tokens" > /opt/pdns-auth/softhsm/softhsm2.conf')
+
@task
def test_auth_backend(c, backend):
- pdns_auth_env_vars = f'PDNS=/opt/pdns-auth/sbin/pdns_server PDNS2=/opt/pdns-auth/sbin/pdns_server SDIG=/opt/pdns-auth/bin/sdig NOTIFY=/opt/pdns-auth/bin/pdns_notify NSEC3DIG=/opt/pdns-auth/bin/nsec3dig SAXFR=/opt/pdns-auth/bin/saxfr ZONE2SQL=/opt/pdns-auth/bin/zone2sql ZONE2LDAP=/opt/pdns-auth/bin/zone2ldap ZONE2JSON=/opt/pdns-auth/bin/zone2json PDNSUTIL=/opt/pdns-auth/bin/pdnsutil PDNSCONTROL=/opt/pdns-auth/bin/pdns_control PDNSSERVER=/opt/pdns-auth/sbin/pdns_server SDIG=/opt/pdns-auth/bin/sdig GMYSQLHOST={auth_backend_ip_addr} GMYSQL2HOST={auth_backend_ip_addr} MYSQL_HOST={auth_backend_ip_addr} PGHOST={auth_backend_ip_addr} PGPORT=5432'
- backend_env_vars = ''
+ pdns_auth_env_vars = f"PDNS=/opt/pdns-auth/sbin/pdns_server PDNS2=/opt/pdns-auth/sbin/pdns_server SDIG=/opt/pdns-auth/bin/sdig NOTIFY=/opt/pdns-auth/bin/pdns_notify NSEC3DIG=/opt/pdns-auth/bin/nsec3dig SAXFR=/opt/pdns-auth/bin/saxfr ZONE2SQL=/opt/pdns-auth/bin/zone2sql ZONE2LDAP=/opt/pdns-auth/bin/zone2ldap ZONE2JSON=/opt/pdns-auth/bin/zone2json PDNSUTIL=/opt/pdns-auth/bin/pdnsutil PDNSCONTROL=/opt/pdns-auth/bin/pdns_control PDNSSERVER=/opt/pdns-auth/sbin/pdns_server SDIG=/opt/pdns-auth/bin/sdig GMYSQLHOST={auth_backend_ip_addr} GMYSQL2HOST={auth_backend_ip_addr} MYSQL_HOST={auth_backend_ip_addr} PGHOST={auth_backend_ip_addr} PGPORT=5432"
+ backend_env_vars = ""
- if backend == 'remote':
+ if backend == "remote":
ci_auth_install_remotebackend_test_deps(c)
- if backend == 'authpy':
- c.sudo(f'sh -c \'echo "{auth_backend_ip_addr} kerberos-server" | tee -a /etc/hosts\'')
- for auth_backend in ('bind', 'lmdb'):
- with c.cd('regression-tests.auth-py'):
- c.run(f'{pdns_auth_env_vars} AUTH_BACKEND={auth_backend} WITHKERBEROS=YES ./runtests')
+ if backend == "authpy":
+ c.sudo(f"sh -c 'echo \"{auth_backend_ip_addr} kerberos-server\" | tee -a /etc/hosts'")
+ for auth_backend in ("bind", "lmdb"):
+ with c.cd("regression-tests.auth-py"):
+ c.run(f"{pdns_auth_env_vars} AUTH_BACKEND={auth_backend} WITHKERBEROS=YES ./runtests")
return
- if backend == 'bind':
+ if backend == "bind":
setup_softhsm(c)
- backend_env_vars = 'SOFTHSM2_CONF=/opt/pdns-auth/softhsm/softhsm2.conf'
+ backend_env_vars = "SOFTHSM2_CONF=/opt/pdns-auth/softhsm/softhsm2.conf"
- if backend == 'godbc_sqlite3':
+ if backend == "godbc_sqlite3":
setup_godbc_sqlite3(c)
- backend_env_vars = 'GODBC_SQLITE3_DSN=pdns-sqlite3-1'
+ backend_env_vars = "GODBC_SQLITE3_DSN=pdns-sqlite3-1"
- if backend == 'godbc_mssql':
+ if backend == "godbc_mssql":
setup_godbc_mssql(c)
- backend_env_vars = f'GODBC_MSSQL_PASSWORD={godbc_mssql_credentials["password"]} GODBC_MSSQL_USERNAME={godbc_mssql_credentials["username"]} GODBC_MSSQL_DSN=pdns-mssql-docker GODBC_MSSQL2_PASSWORD={godbc_mssql_credentials["password"]} GODBC_MSSQL2_USERNAME={godbc_mssql_credentials["username"]} GODBC_MSSQL2_DSN=pdns-mssql-docker'
+ backend_env_vars = f"GODBC_MSSQL_PASSWORD={godbc_mssql_credentials['password']} GODBC_MSSQL_USERNAME={godbc_mssql_credentials['username']} GODBC_MSSQL_DSN=pdns-mssql-docker GODBC_MSSQL2_PASSWORD={godbc_mssql_credentials['password']} GODBC_MSSQL2_USERNAME={godbc_mssql_credentials['username']} GODBC_MSSQL2_DSN=pdns-mssql-docker"
- if backend == 'ldap':
+ if backend == "ldap":
setup_ldap_client(c)
- if backend == 'geoip_mmdb':
- backend_env_vars = 'geoipdatabase=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb'
+ if backend == "geoip_mmdb":
+ backend_env_vars = "geoipdatabase=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb"
- with c.cd('regression-tests'):
- if backend == 'lua2':
- c.run('touch trustedkeys') # avoid silly error during cleanup
+ with c.cd("regression-tests"):
+ if backend == "lua2":
+ c.run("touch trustedkeys") # avoid silly error during cleanup
for variant in backend_regress_tests[backend]:
- c.run(f'{pdns_auth_env_vars} {backend_env_vars} ./start-test-stop 5300 {variant}')
+ c.run(f"{pdns_auth_env_vars} {backend_env_vars} ./start-test-stop 5300 {variant}")
if backend_rootzone_tests[backend]:
- with c.cd('regression-tests.rootzone'):
+ with c.cd("regression-tests.rootzone"):
for variant in backend_regress_tests[backend]:
- c.run(f'{pdns_auth_env_vars} {backend_env_vars} ./start-test-stop 5300 {variant}')
-
- if backend == 'gsqlite3':
- if os.getenv('SKIP_IPV6_TESTS'):
- pdns_auth_env_vars += ' context=noipv6'
- with c.cd('regression-tests.nobackend'):
- c.run(f'{pdns_auth_env_vars} ./runtests')
- c.run('/opt/pdns-auth/bin/pdnsutil test-algorithms')
+ c.run(f"{pdns_auth_env_vars} {backend_env_vars} ./start-test-stop 5300 {variant}")
+
+ if backend == "gsqlite3":
+ if os.getenv("SKIP_IPV6_TESTS"):
+ pdns_auth_env_vars += " context=noipv6"
+ with c.cd("regression-tests.nobackend"):
+ c.run(f"{pdns_auth_env_vars} ./runtests")
+ c.run("/opt/pdns-auth/bin/pdnsutil test-algorithms")
return
+
@task
def test_ixfrdist(c):
- with c.cd('regression-tests.ixfrdist'):
- c.run('IXFRDISTBIN=/opt/pdns-auth/bin/ixfrdist ./runtests')
+ with c.cd("regression-tests.ixfrdist"):
+ c.run("IXFRDISTBIN=/opt/pdns-auth/bin/ixfrdist ./runtests")
+
-@task(optional=['skipXDP'])
+@task(optional=["skipXDP"])
def test_dnsdist(c, skipXDP=False):
- test_env_vars = 'ENABLE_SUDO_TESTS=1' if not skipXDP else ''
- c.run('chmod +x /opt/dnsdist/bin/*')
- c.run('ls -ald /var /var/agentx /var/agentx/master')
- c.run('ls -al /var/agentx/master')
- with c.cd('regression-tests.dnsdist'):
- c.run(f'DNSDISTBIN=/opt/dnsdist/bin/dnsdist LD_LIBRARY_PATH=/opt/dnsdist/lib/ {test_env_vars} ./runtests')
+ test_env_vars = "ENABLE_SUDO_TESTS=1" if not skipXDP else ""
+ c.run("chmod +x /opt/dnsdist/bin/*")
+ c.run("ls -ald /var /var/agentx /var/agentx/master")
+ c.run("ls -al /var/agentx/master")
+ with c.cd("regression-tests.dnsdist"):
+ c.run(f"DNSDISTBIN=/opt/dnsdist/bin/dnsdist LD_LIBRARY_PATH=/opt/dnsdist/lib/ {test_env_vars} ./runtests")
+
@task
def test_regression_recursor(c):
- c.run('/opt/pdns-recursor/sbin/pdns_recursor --version')
- c.run('PDNSRECURSOR=/opt/pdns-recursor/sbin/pdns_recursor RECCONTROL=/opt/pdns-recursor/bin/rec_control ./build-scripts/test-recursor')
+ c.run("/opt/pdns-recursor/sbin/pdns_recursor --version")
+ c.run(
+ "PDNSRECURSOR=/opt/pdns-recursor/sbin/pdns_recursor RECCONTROL=/opt/pdns-recursor/bin/rec_control ./build-scripts/test-recursor"
+ )
+
@task
def test_bulk_recursor(c, size, threads, mthreads, shards, ipv6):
- with c.cd('regression-tests'):
- c.run('curl --no-progress-meter -LO https://umbrella-static.s3.dualstack.us-west-1.amazonaws.com/top-1m.csv.zip')
- c.run('unzip top-1m.csv.zip -d .')
- c.run('chmod +x /opt/pdns-recursor/bin/* /opt/pdns-recursor/sbin/*')
- c.run(f'DNSBULKTEST=/usr/bin/dnsbulktest RECURSOR=/opt/pdns-recursor/sbin/pdns_recursor RECCONTROL=/opt/pdns-recursor/bin/rec_control IPv6={ipv6} THRESHOLD=95 TRACE=no ./recursor-test 5300 {size} {threads} {mthreads} {shards}')
+ with c.cd("regression-tests"):
+ c.run(
+ "curl --no-progress-meter -LO https://umbrella-static.s3.dualstack.us-west-1.amazonaws.com/top-1m.csv.zip"
+ )
+ c.run("unzip top-1m.csv.zip -d .")
+ c.run("chmod +x /opt/pdns-recursor/bin/* /opt/pdns-recursor/sbin/*")
+ c.run(
+ f"DNSBULKTEST=/usr/bin/dnsbulktest RECURSOR=/opt/pdns-recursor/sbin/pdns_recursor RECCONTROL=/opt/pdns-recursor/bin/rec_control IPv6={ipv6} THRESHOLD=95 TRACE=no ./recursor-test 5300 {size} {threads} {mthreads} {shards}"
+ )
+
@task
def install_swagger_tools(c):
- c.run('sudo apt-get update && sudo apt-get install -y npm')
- c.run('sudo mkdir -p /usr/local/lib/node_modules && sudo chmod 777 /usr/local/lib/node_modules')
- c.run('npm install -g @stoplight/spectral-cli')
- c.run('npm install -g api-spec-converter')
+ c.run("sudo apt-get update && sudo apt-get install -y npm")
+ c.run("sudo mkdir -p /usr/local/lib/node_modules && sudo chmod 777 /usr/local/lib/node_modules")
+ c.run("npm install -g @stoplight/spectral-cli")
+ c.run("npm install -g api-spec-converter")
+
@task
def swagger_syntax_check(c):
- c.run('spectral lint --ruleset docs/http-api/swagger/spectral-ruleset.yaml --fail-severity error --display-only-failures docs/http-api/swagger/authoritative-api-swagger.yaml')
- c.run('api-spec-converter docs/http-api/swagger/authoritative-api-swagger.yaml -f swagger_2 -t openapi_3 -s json -c')
+ c.run(
+ "spectral lint --ruleset docs/http-api/swagger/spectral-ruleset.yaml --fail-severity error --display-only-failures docs/http-api/swagger/authoritative-api-swagger.yaml"
+ )
+ c.run(
+ "api-spec-converter docs/http-api/swagger/authoritative-api-swagger.yaml -f swagger_2 -t openapi_3 -s json -c"
+ )
+
@task
def install_coverity_tools(c, project):
- token = os.getenv('COVERITY_TOKEN')
- c.run(f'curl -s https://scan.coverity.com/download/linux64 --data "token={token}&project={project}" | gunzip | sudo tar xvf /dev/stdin --strip-components=1 --no-same-owner -C /usr/local', hide=True)
+ token = os.getenv("COVERITY_TOKEN")
+ c.run(
+ f'curl -s https://scan.coverity.com/download/linux64 --data "token={token}&project={project}" | gunzip | sudo tar xvf /dev/stdin --strip-components=1 --no-same-owner -C /usr/local',
+ hide=True,
+ )
+
@task
def coverity_clang_configure(c):
- c.sudo(f'/usr/local/bin/cov-configure --template --comptype clangcc --compiler clang++-{clang_version}')
+ c.sudo(f"/usr/local/bin/cov-configure --template --comptype clangcc --compiler clang++-{clang_version}")
+
@task
def coverity_make(c):
- c.run('/usr/local/bin/cov-build --dir cov-int make -j8 -k')
+ c.run("/usr/local/bin/cov-build --dir cov-int make -j8 -k")
+
@task
def coverity_tarball(c, tarball):
- c.run(f'tar caf {tarball} cov-int')
+ c.run(f"tar caf {tarball} cov-int")
+
@task
def coverity_upload(c, email, project, tarball):
- token = os.getenv('COVERITY_TOKEN')
- c.run(f'curl --form token={token} \
+ token = os.getenv("COVERITY_TOKEN")
+ c.run(
+ f'curl --form token={token} \
--form email="{email}" \
--form file=@{tarball} \
--form version="$(./builder-support/gen-version)" \
--form description="master build" \
- https://scan.coverity.com/builds?project={project}', hide=True)
+ https://scan.coverity.com/builds?project={project}',
+ hide=True,
+ )
+
@task
def ci_build_and_install_quiche(c, repo):
- with c.cd(f'{repo}/builder-support/helpers/'):
+ with c.cd(f"{repo}/builder-support/helpers/"):
# be careful that rust needs to have been installed system-wide,
# as the one installed in GitHub actions' Ubuntu images in /home/runner/.cargo/bin/cargo
# is not in the path for the root user (and shouldn't be)
- c.run(f'sudo {repo}/builder-support/helpers/install_quiche.sh')
+ c.run(f"sudo {repo}/builder-support/helpers/install_quiche.sh")
# cannot use c.sudo() inside a cd() context, see https://github.com/pyinvoke/invoke/issues/687
- for tentative in ['lib/x86_64-linux-gnu', 'lib/aarch64-linux-gnu', 'lib64', 'lib']:
- tentative_libdir = f'/usr/{tentative}'
- quiche_lib = f'{tentative_libdir}/libdnsdist-quiche.so'
+ for tentative in ["lib/x86_64-linux-gnu", "lib/aarch64-linux-gnu", "lib64", "lib"]:
+ tentative_libdir = f"/usr/{tentative}"
+ quiche_lib = f"{tentative_libdir}/libdnsdist-quiche.so"
if not os.path.isfile(quiche_lib):
continue
- c.run(f'sudo mv {quiche_lib} /usr/lib/libquiche.so')
+ c.run(f"sudo mv {quiche_lib} /usr/lib/libquiche.so")
c.run(f"sudo sed -i 's,^Libs:.*,Libs: -lquiche,g' {tentative_libdir}/pkgconfig/quiche.pc")
- c.run('mkdir -p /opt/dnsdist/lib')
- c.run('cp /usr/lib/libquiche.so /opt/dnsdist/lib/libquiche.so')
+ c.run("mkdir -p /opt/dnsdist/lib")
+ c.run("cp /usr/lib/libquiche.so /opt/dnsdist/lib/libquiche.so")
break
+
@task
def test_install_package(c, product_name, distro_release, content_url, gpgkey_url, package_name, package_version):
- distro, release = distro_release.split('-')[:2]
- repo_domain = content_url.split('/')[2]
- is_rpm = True if distro == 'centos' or distro == 'el' else False
+ distro, release = distro_release.split("-")[:2]
+ repo_domain = content_url.split("/")[2]
+ is_rpm = True if distro == "centos" or distro == "el" else False
if is_rpm:
- image_name = 'oraclelinux'
+ image_name = "oraclelinux"
else:
image_name = distro
# pdns package is called pdns-server for debian/ubuntu
- package_name = 'pdns-server' if package_name == 'pdns' else package_name
+ package_name = "pdns-server" if package_name == "pdns" else package_name
# version of packages from master or not releases have a different order than the package name
- parts = package_version.split('.')
+ parts = package_version.split(".")
if len(parts) > 4:
- if distro == 'el':
- if 'master' in package_version:
+ if distro == "el":
+ if "master" in package_version:
package_version = f"{'.'.join(parts[:3])}.{parts[4]}.{parts[3]}.{'.'.join(parts[5:])}"
else:
package_version = f"{'.'.join(parts[:3])}-{parts[4]}.{parts[3]}.{'.'.join(parts[5:])}"
package_version = f"{'.'.join(parts[:3])}+{parts[4]}.{parts[3]}.{'.'.join(parts[5:])}"
# Add wildcards to work with and without releases
- parts = package_version.split('-')
+ parts = package_version.split("-")
if len(parts) > 1:
package_version = f"{parts[0]}*{parts[1]}" if is_rpm else f"{parts[0]}~{parts[1]}"
- dockerfile_rpm = f'''
+ dockerfile_rpm = f"""
FROM {image_name}:{release}
RUN curl -L {content_url}/repo-files/{distro}-{product_name}.repo -o /etc/yum.repos.d/{distro}-{product_name}.repo
RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-{release}.noarch.rpm
RUN yum install -y {package_name}-{package_version}*
-'''
- dockerfile_deb = f'''
+"""
+ dockerfile_deb = f"""
FROM {image_name}:{release}
RUN apt update && apt install -y curl libluajit-5.1-dev adduser
RUN install -d /etc/apt/keyrings && curl -L {gpgkey_url} -o /etc/apt/keyrings/{product_name}-pub.asc
RUN echo "deb [signed-by=/etc/apt/keyrings/{product_name}-pub.asc] {content_url}/{distro} {release}-{product_name} main" | tee /etc/apt/sources.list.d/pdns.list
RUN bash -c 'echo -e "Package: auth*\\nPin: origin {repo_domain}\\nPin-Priority: 600" | tee /etc/apt/preferences.d/{product_name}'
RUN apt-get update && apt-get install -y {package_name}={package_version}*
-'''
+"""
dockerfile = dockerfile_rpm if is_rpm else dockerfile_deb
- with open('/tmp/Dockerfile', "w") as f:
+ with open("/tmp/Dockerfile", "w") as f:
f.write(dockerfile)
- c.run(f'docker build . -t test-build-{product_name}-{distro_release}:latest -f /tmp/Dockerfile')
+ c.run(f"docker build . -t test-build-{product_name}-{distro_release}:latest -f /tmp/Dockerfile")
+
@task
def github_more_diskspace(c):
- c.run('df -h / /hostroot')
- for path in ['/usr/local/lib/android',
- '/usr/local/.ghcup',
- '/usr/share/dotnet',
- '/usr/share/swift']:
- c.sudo(f'rm -rf {path} /hostroot{path}')
- c.run('df -h / /hostroot')
+ c.run("df -h / /hostroot")
+ for path in ["/usr/local/lib/android", "/usr/local/.ghcup", "/usr/share/dotnet", "/usr/share/swift"]:
+ c.sudo(f"rm -rf {path} /hostroot{path}")
+ c.run("df -h / /hostroot")
+
# this is run always
def setup():
- if '/usr/lib/ccache' not in os.environ['PATH']:
- os.environ['PATH']='/usr/lib/ccache:'+os.environ['PATH']
+ if "/usr/lib/ccache" not in os.environ["PATH"]:
+ os.environ["PATH"] = "/usr/lib/ccache:" + os.environ["PATH"]
+
setup()