mirror of
https://github.com/Kkevsterrr/geneva
synced 2025-01-17 22:40:28 +01:00
123 lines
4.7 KiB
Python
123 lines
4.7 KiB
Python
"""
|
|
Client
|
|
|
|
Run by the evaluator, tries to make a GET request to a given server
|
|
"""
|
|
|
|
import argparse
|
|
import logging
|
|
import os
|
|
import random
|
|
import socket
|
|
import sys
|
|
import time
|
|
import traceback
|
|
import urllib.request
|
|
|
|
import dns.resolver
|
|
import requests
|
|
|
|
import actions.utils
|
|
from plugins.plugin_client import ClientPlugin
|
|
|
|
|
|
class DNSClient(ClientPlugin):
|
|
"""
|
|
Defines the DNS client.
|
|
"""
|
|
name = "dns"
|
|
|
|
def __init__(self, args):
|
|
"""
|
|
Initializes the DNS client.
|
|
"""
|
|
ClientPlugin.__init__(self)
|
|
self.args = args
|
|
|
|
@staticmethod
|
|
def get_args(command):
|
|
"""
|
|
Defines required args for this plugin
|
|
"""
|
|
super_args = ClientPlugin.get_args(command)
|
|
parser = argparse.ArgumentParser(description='DNS Client')
|
|
|
|
parser.add_argument('--use-tcp', action='store_true', help='leverage TCP for this plugin')
|
|
parser.add_argument('--dns-server', action='store', default="8.8.8.8", help='domain server to connect to')
|
|
parser.add_argument('--query', action='store', default="facebook.com", help='censored domain to query')
|
|
parser.add_argument('--timeout', action='store', default="3", type=int, help='how long in seconds the client should wait for a response')
|
|
parser.add_argument('--port', action='store', default="53", type=int, help='port the DNS server is running on (must be 53)')
|
|
|
|
args, _ = parser.parse_known_args(command)
|
|
args = vars(args)
|
|
|
|
super_args.update(args)
|
|
return super_args
|
|
|
|
def run(self, args, logger, engine=None):
|
|
"""
|
|
Try to make a forbidden DNS query.
|
|
"""
|
|
fitness = 0
|
|
to_lookup = args.get("query", "facebook.com")
|
|
dns_server = args.get("dns_server", "8.8.8.8")
|
|
use_tcp = args.get("use_tcp", False)
|
|
assert dns_server, "Cannot launch DNS test with no DNS server"
|
|
assert to_lookup, "Cannot launch DNS test with no server to query"
|
|
fitness = -1000
|
|
try:
|
|
fitness = self.dns_test(to_lookup, dns_server, args["output_directory"], args["environment_id"], logger, timeout=args.get("timeout", 3), use_tcp=use_tcp)
|
|
except Exception:
|
|
logger.exception("Exception caught in DNS test to resolver %s.", dns_server)
|
|
fitness += -100
|
|
|
|
# When performing a DNS test, a timeout is indistinguishable from
|
|
# a reset, which means we can't tell if the strategy broke the packet
|
|
# stream, or if the censor caught us. Strategies that break the stream
|
|
# should be punished more harshly, so raise the fitness slightly
|
|
# if the engine detected censorship for failed DNS tests.
|
|
if use_tcp and engine and engine.censorship_detected and fitness < 0:
|
|
fitness += 10
|
|
return fitness * 4
|
|
|
|
def dns_test(self, to_lookup, dns_server, output_dir, environment_id, logger, timeout=3, use_tcp=False):
|
|
"""
|
|
Makes a DNS query to a given censored domain.
|
|
"""
|
|
# Make the path an absolute path
|
|
if not output_dir.startswith("/"):
|
|
output_dir = os.path.join(actions.utils.PROJECT_ROOT, output_dir)
|
|
|
|
resolver = dns.resolver.Resolver()
|
|
protocol = "UDP"
|
|
if use_tcp:
|
|
protocol = "TCP"
|
|
|
|
logger.debug("Querying %s to DNS server %s over %s" % (to_lookup, dns_server, protocol))
|
|
resolver.nameservers = [dns_server]
|
|
# Setup the timeout and lifetime for this resolver
|
|
resolver.timeout = timeout
|
|
resolver.lifetime = 3
|
|
|
|
try:
|
|
answer = resolver.query(to_lookup, "A", tcp=use_tcp)[0]
|
|
logger.debug("Got IP address: %s" % answer)
|
|
# At this point, we've been given an IP address by the DNS resolver, but we don't
|
|
# yet know if this IP address is a bogus injected response, or legitimate. Further,
|
|
# because we are likely running this code from within a censored regime which might
|
|
# employ secondary censorship at the IP level, we cannot check if this IP is legit
|
|
# here. Instead, we write it out to a file for the evaluator to extract and check for us.
|
|
with open(os.path.join(output_dir, "flags", environment_id)+".dnsresult", "w") as dnsfile:
|
|
dnsfile.write(str(answer))
|
|
# For now, set fitness to a positive metric, though the evaluator will lower it if
|
|
# the IP address we were given was bogus.
|
|
fitness = 100
|
|
except dns.exception.Timeout:
|
|
logger.error("DNS query timed out.")
|
|
fitness = -100
|
|
except dns.resolver.NoNameservers:
|
|
logger.error("DNS server failed to respond")
|
|
fitness = -100
|
|
|
|
return fitness
|