initial version

This commit is contained in:
Nikola Mitev 2024-11-08 16:27:00 +00:00
commit 02e2c3592a
5 changed files with 246 additions and 0 deletions

View File

@ -0,0 +1,25 @@
A simple tool for testing DNS, useful if you do not have access to the underlying server.
## Configure DNSTest on Cortes XSOAR
1. Navigate to **Settings** > **Integrations** > **Servers & Services**.
2. Search for DNSTest.
3. Click **Add instance** to create and configure a new integration instance.
| **Parameter** | **Description** | **Required** |
| --- | --- | --- |
| Default domain | The domain to query for during inital testing (default: google.com)| False |
| Timeout | Time in seconds to wait for response from DNS server (default: 1 sec) | False |
4. Click **Test** to verify operation, using default or provided parameters.
## Commands
Run these commands from the Cortex XSOAR CLI
### dns-test-get-resolvers
Returns the nameserver IP addresses configured for the host (e.g. in /etc/resolv.conf)
Example: !dns-test-get-resolvers
### dns-test-resolve-domain
Queries all available resolvers for A, AAAA and CNAME records for the provided domain name and returns the results. Optionally increase the wait time for the query by passing a timeout value greater than 1
Example: !dns-test-resolve-domain domain=test.com [timeout=5]

View File

@ -0,0 +1,148 @@
register_module_line('DNSTest', 'start', __line__())
demisto.debug('pack name = DNSTest, pack version = 1.0.0')
import dns.resolver
from time import sleep
def get_dns_resolvers():
# This function retrieves the DNS resolvers from the server configuration.
resolver = dns.resolver.get_default_resolver()
return resolver.nameservers
def resolve_domain(domain, timeout=1, iterations=100, delay=0):
# This function resolves a given domain using all locally configured resolvers.
resolvers = get_dns_resolvers()
rtypes = ['A', 'AAAA', 'CNAME']
result = {}
for resolver in resolvers:
for rtype in rtypes:
demisto.debug(f'Resolving {domain} using {resolver} for {rtype} records...')
# run every query 100 times to handle cases where the DNS server is not responding.
errors = set()
for i in range(iterations):
answers = []
try:
a = dns.resolver.resolve_at(resolver, domain, rtype, lifetime=timeout)
if a:
answers = [str(answer) for answer in a]
answers.sort()
except dns.resolver.NXDOMAIN:
error = f'Domain {domain} does not exist.'
errors.add(error)
demisto.info(error)
except dns.resolver.NoAnswer:
error = f'No {rtype} records found for domain {domain}.'
errors.add(error)
demisto.info(error)
except dns.resolver.Timeout:
error = f'Timeout occurred while resolving domain {domain}.'
errors.add(error)
demisto.info(error)
except Exception as e:
error = f'An error occurred while resolving domain {domain}: {str(e)}'
errors.add(error)
demisto.info(error)
demisto.info(f'Results for {resolver} and {rtype}: {answers}')
if resolver in result and rtype in result[resolver]:
demisto.debug(f'Previous results for {resolver} and {rtype} already exist.')
if answers:
if not result[resolver][rtype]['answers'] or answers == result[resolver][rtype]['answers']:
demisto.debug(f'incrementing successes')
result[resolver][rtype]['successes'] += 1
else:
demisto.info(f'got different answer for {rtype} from {resolver}: {answerlist}')
result[resolver][rtype].setdefault('variants', []).append(answerlist)
demisto.debug(f'incrementing successes')
result[resolver][rtype]['successes'] += 1
else:
demisto.debug(f'incrementing failures')
result[resolver][rtype]['failures'] += 1
elif resolver in result:
demisto.debug(f'Adding initial record for {rtype} from {resolver}.')
if answers:
result[resolver][rtype] = {'answers': [str(answer) for answer in answers], 'successes': 1}
else:
result[resolver][rtype] = {'answers': [], 'failures': 1, 'successes': 0}
else:
demisto.debug(f'Adding initial result for {resolver}.')
if answers:
result[resolver] = {rtype: {'answers': [str(answer) for answer in answers],'successes': 1}}
else:
result[resolver] = {rtype: {'answers': [], 'failures': 1, 'successes': 0}}
sleep(delay)
if errors:
demisto.debug(f'errors for {resolver} and {rtype}: {errors}')
result[resolver][rtype]['errors'] = list(errors)
return result
def test_module(default_domain, timeout):
"""
This is the test function that runs when the module is loaded for the first time.
It tests the module's functions by calling them with sample inputs.
"""
# Test resolve_domain function
domain = 'google.com'
result = resolve_domain(default_domain, timeout, iterations=1)
# Test get_dns_resolvers function
resolvers = get_dns_resolvers()
if isinstance(resolvers, list) and len(resolvers) > 0:
getresolversok = True
else:
return 'Get resolvers test failed'
if isinstance(result, dict) and len(result) > 0:
resolveok = True
else:
return 'Resolve test failed'
if resolveok and getresolversok:
return 'ok'
def main():
"""
Parse and Validate the commands, parameters, arguments
and processes the api requests.
"""
command = demisto.command()
demisto.info(f'Executing command {command}')
args = demisto.args()
params = demisto.params()
timeout = int(params.get('timeout', 1))
default_domain = params.get('default_domain', 'google.com')
delay = int(params.get('delay', 0))
iterations = int(params.get('iterations', 100))
if command == 'dns-test-resolve-domain':
domain = args.get('domain')
result = resolve_domain(domain, timeout, iterations=iterations, delay=delay)
return_results(result)
elif command == 'dns-test-get-resolvers':
resolvers = get_dns_resolvers()
return_results(resolvers)
elif command == 'test-module':
result = test_module(default_domain, timeout)
return_results(result)
else:
return_error(f'Unsupported command: {command}')
if __name__ in ['__main__', '__builtin__', 'builtins']:
main()
register_module_line('DNSTest', 'end', __line__())

View File

@ -0,0 +1,39 @@
commonfields:
id: DNSTest
version: -1
name: DNSTest
display: DNSTest
category: Utilities
configuration:
- display: DNS query timeout
name: timeout
defaultvalue: "1"
type: 0
required: false
additionalinfo: How long to wait for reply from the DNS server (default 1s)
- display: Default domain
name: default_domain
defaultvalue: "google.com"
type: 0
required: false
additionalinfo: The default domain used to test operation. May need to be configured if the local resolver is not recursive.
script:
script: ""
type: python
commands:
- name: dns-test-resolve-domain
description: Resolve a given domain using all locally configured resolvers
arguments:
- name: domain
description: The domain to resolve
required: true
- name: dns-test-get-resolvers
arguments: []
description: Get the locally configured DNS resolvers
dependencies:
- name: dnspython
version: ">=2.7.0"
dockerimage: demisto/py3-tools:1.0.0.87415
isfetch: false
runonce: true
subtype: python3

3
ReleaseNotes/0_9.md Normal file
View File

@ -0,0 +1,3 @@
Integrations
DNSTest
- initial release

31
pack_metadata.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "DNSTest",
"description": "A tool for troubleshooting and testing local DNS",
"support": "developer",
"currentVersion": "0.9",
"author": "Nik Mitev",
"email": "xsoar@mitev.net",
"devEmail": "xsoar@mitev.net",
"categories": [
"Utilities"
],
"tags": [
"dns",
"troubleshooting"
],
"created": "2024-11-05T00:00:00Z",
"useCases": [
"Troubleshooting DNS issues",
"Testing local DNS configurations"
],
"keywords": [
"dns",
"debugging"
],
"dependencies": {
"dnspython": ">=2.7.0"
},
"githubUser": [
"nikmit"
]
}