2019-11-10 17:14:25 +00:00
|
|
|
#!/usr/bin/env python3
|
2019-12-02 18:03:08 +00:00
|
|
|
# pylint: disable=C0103
|
2019-11-10 17:14:25 +00:00
|
|
|
|
|
|
|
"""
|
|
|
|
From a list of subdomains, output only
|
|
|
|
the ones resolving to a first-party tracker.
|
|
|
|
"""
|
|
|
|
|
2019-11-14 14:37:32 +00:00
|
|
|
import argparse
|
2019-11-10 17:14:25 +00:00
|
|
|
import sys
|
2019-12-02 18:03:08 +00:00
|
|
|
import progressbar
|
|
|
|
import csv
|
2019-11-14 09:45:06 +00:00
|
|
|
import typing
|
2019-11-10 17:14:25 +00:00
|
|
|
|
2019-11-15 07:57:31 +00:00
|
|
|
import adblockparser
|
2019-11-14 09:45:06 +00:00
|
|
|
|
2019-12-02 18:03:08 +00:00
|
|
|
OPTIONS = {"third-party": True}
|
2019-11-14 14:03:20 +00:00
|
|
|
|
2019-11-14 09:45:06 +00:00
|
|
|
|
2019-12-02 18:03:08 +00:00
|
|
|
def subdomain_matching(subdomain: str) -> bool:
|
|
|
|
url = f"https://{subdomain}/"
|
|
|
|
return rules.should_block(url, OPTIONS)
|
2019-11-14 09:45:06 +00:00
|
|
|
|
|
|
|
|
2019-12-02 18:03:08 +00:00
|
|
|
def get_matching(chain: typing.List[str], no_explicit: bool = False
|
|
|
|
) -> typing.Iterable[str]:
|
|
|
|
initial = chain[0]
|
|
|
|
cname_destinations = chain[1:-1]
|
|
|
|
# a_destination = chain[-1]
|
|
|
|
initial_matching = subdomain_matching(initial)
|
|
|
|
if no_explicit and initial_matching:
|
|
|
|
return
|
|
|
|
cname_matching = any(map(subdomain_matching, cname_destinations))
|
|
|
|
if cname_matching or initial_matching:
|
|
|
|
yield initial
|
2019-11-15 07:57:31 +00:00
|
|
|
|
2019-11-14 09:45:06 +00:00
|
|
|
|
2019-12-02 18:03:08 +00:00
|
|
|
if __name__ == '__main__':
|
2019-11-14 09:45:06 +00:00
|
|
|
|
2019-11-14 14:37:32 +00:00
|
|
|
# Parsing arguments
|
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
description="Filter first-party trackers from a list of subdomains")
|
|
|
|
parser.add_argument(
|
|
|
|
'-i', '--input', type=argparse.FileType('r'), default=sys.stdin,
|
2019-12-02 18:03:08 +00:00
|
|
|
help="Input file with DNS chains")
|
2019-11-14 14:37:32 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'-o', '--output', type=argparse.FileType('w'), default=sys.stdout,
|
|
|
|
help="Outptut file with one tracking subdomain per line")
|
2019-12-02 18:03:08 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'-n', '--no-explicit', action='store_true',
|
|
|
|
help="Don't output domains already blocked with rules without CNAME")
|
2019-11-15 07:57:31 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'-r', '--rules', type=argparse.FileType('r'), default='rules',
|
|
|
|
help="Rules file")
|
2019-11-14 14:37:32 +00:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
2019-12-02 18:03:08 +00:00
|
|
|
# Reading rules
|
|
|
|
rules: adblockparser.AdblockRules = adblockparser.AdblockRules(args.rules)
|
|
|
|
|
2019-11-14 09:45:06 +00:00
|
|
|
# Progress bar
|
|
|
|
widgets = [
|
|
|
|
progressbar.Percentage(),
|
|
|
|
' ', progressbar.SimpleProgress(),
|
|
|
|
' ', progressbar.Bar(),
|
|
|
|
' ', progressbar.Timer(),
|
|
|
|
' ', progressbar.AdaptiveTransferSpeed(unit='req'),
|
|
|
|
' ', progressbar.AdaptiveETA(),
|
|
|
|
]
|
|
|
|
progress = progressbar.ProgressBar(widgets=widgets)
|
2019-11-15 07:57:31 +00:00
|
|
|
if args.input.seekable():
|
|
|
|
progress.max_value = len(args.input.readlines())
|
|
|
|
args.input.seek(0)
|
2019-11-14 09:45:06 +00:00
|
|
|
|
|
|
|
# Cleaning input
|
2019-12-02 18:03:08 +00:00
|
|
|
reader = csv.reader(args.input)
|
2019-11-10 20:59:06 +00:00
|
|
|
|
2019-12-02 18:03:08 +00:00
|
|
|
# Filtering
|
2019-11-14 09:45:06 +00:00
|
|
|
progress.start()
|
2019-12-02 18:03:08 +00:00
|
|
|
for chain in reader:
|
|
|
|
for match in get_matching(chain, no_explicit=args.no_explicit):
|
|
|
|
print(match, file=args.output)
|
2019-11-14 09:45:06 +00:00
|
|
|
progress.update(progress.value + 1)
|
|
|
|
progress.finish()
|