code: #!/usr/bin/python3
  # -*- coding: utf-8 -*-
  
  import datetime
  import json
  import os
  import random
  import sqlite3
  import sys
  import time
  import urllib
  from importlib import reload
  
  import dns.resolver
  import pandas
  import requests
  
  reload(sys)
  
  #########################################################################################
  #				BEGINNING OF CONFIG SECTION				#
  
  # Set your pi-hole auth token - you can copy it from /etc/pihole/setupVars.conf
  auth = ""
  
  # Set the Fake Query Multiplier | Default: 1 = 10% Fake Querys
  multiplier = 1.5
  
  # Set IP of the machine running this script. The script is optimized for running directly on the pi-hole server,
  # or on another un-attended machine. "127.0.0.1" is valid only when running directly on the pi-hole.
  client = "127.0.0.1"
  
  # Set IP of your pi-hole instance. "127.0.0.1" is valid only when running directly on the pi-hole.
  dns.resolver.nameservers = "127.0.0.1"
  
  # Logging to a file. For easier debugging uncomment the second row.
  log_file = sys.stdout
  
  # Set working directory for the script - the database with top 1M domains will be stored here.
  working_directory = os.path.dirname(os.path.realpath(__file__))
  
  #				  END OF CONFIG SECTION  				#
  #########################################################################################
  
  if auth == "":
      print("Please Set your auth token")
      exit()
  
  def download_domains():
      # Download the Cisco Umbrella list. More info: https://s3-us-west-1.amazonaws.com/umbrella-static/index.html
      try:
          print("Downloading the domain list…")
          urllib.request.urlretrieve("http://s3-us-west-1.amazonaws.com/umbrella-static/top-1m.csv.zip",
                             filename=working_directory + "domains.zip")
      except Exception as e:
          print(e)
          print("Can't download the domain list. Quitting.")
          exit()
  
      # Create a SQLite database and import the domain list
      try:
          db = sqlite3.connect(working_directory + "domains.sqlite")
          db.execute("CREATE TABLE Domains (ID INT PRIMARY KEY, Domain TEXT)")
  
          # Load the CSV into our database
          print("Importing to sqlite…")
          df = pandas.read_csv(working_directory + "domains.zip", compression='zip', names=["ID", "Domain"])
          df.to_sql("Domains", db, if_exists="append", index=False)
  
          db.close()
  
          os.remove(working_directory + "domains.zip")
      except:
          print("Import failed. Quitting.")
          exit()
  
      # Running this on 1st gen Raspberry Pi can take up to 10 minutes. Be patient.
      print("Done.")
  
  
  # A simple loop that makes sure we have an Internet connection - it can take a while for pi-hole to get up and running after a reboot.
  while True:
      try:
          urllib.request.urlopen("https://duckduckgo.com")
          print("Got network connection.")
          break
      except Exception as e:
          print(e)
          print("Network not up yet, retrying in 10 seconds.")
          time.sleep(10)
  
  # Download the top 1M domain list if we don't have it yet.
  exists = os.path.isfile(working_directory + "domains.sqlite")
  if not exists:
      download_domains()
  
  db = sqlite3.connect(working_directory + "domains.sqlite")
  
  while True:
      # We want the fake queries to blend in with the organic traffic expected at each given time of the day, so instead of having a static delay between individual queries,
      # we'll sample the network activity over the past 5 minutes and base the frequency on that. We want to add roughly 10% of additional activity in fake queries.
      time_until = int(time.mktime(datetime.datetime.now().timetuple()))
      time_from = time_until - 300
  
      # This will give us a list of all DNS queries that pi-hole handled in the past 5 minutes.
      while True:
          try:
              all_queries = requests.get(
                  "http://pi.hole/admin/api.php?getAllQueries&from=" + str(time_from) + "&until=" + str(
                      time_until) + "&auth=" + auth)
              break
          except:
              print(" API request failed. Retrying in 15 seconds.")
              time.sleep(15)
  
      parsed_all_queries = json.loads(all_queries.text)
  
      # When determining the rate of DNS queries on the network, we don't want our past fake queries to skew the statistics, therefore we filter out queries made by this machine.
      genuine_queries = []
      try:
          for a in parsed_all_queries["data"]:
              if a[3] != client.replace("127.0.0.1", "localhost"):
                  genuine_queries.append(a)
      except:
          print(" Pi-hole API response in wrong format. Investigate.")
          exit()
  
      # Protection in case the pi-hole logs are empty.
      if len(genuine_queries) == 0:
          genuine_queries.append("Let's not devide by 0")
  
      # We want the types of our fake queries (A/AAA/PTR/…) to proportionally match those of the real traffic.
      query_types = []
      try:
          for a in parsed_all_queries["data"]:
              if a[3] != client.replace("127.0.0.1", "localhost"):
                  query_types.append(a[1])
      except:
          print("Pi-hole API response in wrong format. Investigate.")
          exit()
  
      # Default to A request if pi-hole logs are empty
      if len(query_types) == 0:
          query_types.append("A")
  
      while True:
          # Pick a random domain from the top 1M list
          rand = str(random.randint(1, 1000000))
          cursor = db.cursor()
          cursor.execute("SELECT Domain FROM Domains WHERE ID=" + rand)
          domain = cursor.fetchone()[0]
  
          # Try to resolve the domain - that's why we're here in the first place, isn't it…
          try:
              dns.resolver.resolve(domain, random.choice(query_types))
          except:
              pass
  
          # We want to re-sample our "queries per last 5 min" rate every minute.
          if int(time.mktime(datetime.datetime.now().timetuple())) - time_until > 60:
              break
  
          # Since we want to add only about 10% of extra DNS queries, we multiply the wait time by 10, then add a small random delay.
          sleeper = (300.0 / (len(genuine_queries)) * 10 / multiplier) + random.uniform(0, 2)
          time.sleep(sleeper)
         
  db.close()
  
apis: pandas.read_csv