WIFIHELL - 科技改变生活

 找回密码
 注册WIFIHELL

QQ登录

只需一步,快速开始

开启左侧

简单的 BT Tracker 连接检测

[复制链接]
222ba 发表于 2018-12-3 15:20:41 | 显示全部楼层 |阅读模式

注册WIFIHELL,浏览更多技术贴!

您需要 登录 才可以下载或查看,没有账号?注册WIFIHELL

x
RT @kgen: 大家 BT 下载的时候,不要开着 VPN,发达国家的版权投诉猛如虎。版权方会主动放出一些正版的 BT 种子,然后收集所有连接来源 IP,投诉或索赔。
这对于 VPN 提供商来说也是一个很头疼的问题。现在的 BT 客户端为了避免被封,已经经进化出了各种加密、混淆手段,直接检测 P2P 流量很困难。不过,大多数时候,客户端都在下载是都会同时连接一些 BitTorrent tracker 服务器。这些服务器数量有限且地址、端口号相对固定,可以非常容易地收集,并直接通过 IP 地址和端口号判断而不必对流量内容进行深度检测。探测到了就扔炸弹。是一个简单易行的判断用户是否正在使用 BT 的方法。
这个方法已在某家梯子站上部署了有一段时间了。鉴于它误报率(false postive rate)低但漏报率(false negtive rate)高,检测到的「处罚」会比较严厉——发现连接就立即挂断这条 VPN 连接。效果似乎还不错,仅有的几次投诉经过排查,发现都是在这套机制的某些环节失效时发生的。
当时为了方便操作,写了几个脚本。现在重新整理了一下,放在了 GitHub 上。这个脚本主要用于从 BT 种子文件里收集 trackers 的地址,然后解析域名得到对应的 IP 地址、协议和端口号。脚本本身并不进行检测等操作,只是为了方便配置防火墙而写。
示例先在各处收集一些 BT 种子,这些种子文件中通常会包含一个或多个的 trackers。可使用./trackers.py torrent获得 trackers 的 URL 列表,重复的地址会被自动剔除:
  1. $ ./trackers.py torrent *.torrent > trackers.txt

  2. $ cat trackers.txt
  3. > http://tracker.yify-torrents.com
  4. > udp://tracker.justseed.it:1337
  5. > udp://tracker.openbittorrent.com:80
  6. > http://nyaatorrents.info:3277
  7.   ......
复制代码


防火墙通常需要匹配 IP 地址而非域名,并且一个域名可能对应多个 IP。这个脚本提供了解析 tracker URL 的功能,每个地址会被解析为一个或多个 IP 地址 – 协议 – 端口 的组合:

  1. $ ./trackers.py raw trackers.txt
  2. > 188.166.82.104 tcp 6881
  3. > 94.23.217.90 udp 1337
  4. > 179.43.146.110 udp 80
  5.   ......
复制代码

使用 ipset 来匹配这些连接是一个不错的选择。可以使用./trackers.py ipset直接生成 ipset 规则,这些规则可经由ipset restore导入系统:

  1. $ ./trackers.py ipset trackers trackers.txt > ipset.rules

  2. $ cat ipset.rules
  3. > create -exist trackers hash:ip,port family inet
  4. > add -exist trackers 188.166.82.104,tcp:6881
  5. > add -exist trackers 94.23.217.90,udp:1337
  6. > add -exist trackers 179.43.146.110,udp:80
  7. > ...

  8. # ipset restore -file ipset.rules
复制代码

另外,虽然不是很推荐这么做,从收集 trackers 到导入 ipset 的操作也可以一步完成:

  1. # ./trackers.py torrent *.torrent | ./trackers.py ipset trackers - | ipset restore
复制代码

导入 ipset 后,可以使用 iptables 匹配这个 ipset,进行进一步操作。例如可使用iptables -j LOG记录下连接 trackers 的行为:

  1. # iptables -N TRACKERS
  2. # iptables -A FORWARD -m set --match-set trackers dst,dst -j TRACKERS
  3. # iptables -A TRACKERS -m limit --limit 1/sec --limit-burst 10 \
  4.   -j LOG --log-level info --log-prefix trackers
  5. # iptables -A TRACKERS -j DROP
复制代码
一些发行版本使用了 syslogd 管理日志,可以修改它的配置,将连接记录转发至其他程序进行进一步处理。例如文章开头所说的,通过日志中记录的源 IP 识别出对应的用户,然后将其断线。

https://github.com/sorz/bt-tracker-helper/blob/master/trackers.py
  1. #!/usr/bin/env python3
  2. from urllib.parse import urlparse
  3. from socket import getaddrinfo, AF_INET, SOL_TCP
  4. from functools import lru_cache
  5. import argparse
  6. import sys


  7. def _bencode_read_string(f):
  8.     """Read a Bencoded byte string from current position of f."""
  9.     length = 0
  10.     s = f.read(1)
  11.     while s != b':':
  12.         if not s.isdigit():
  13.             raise ValueError('Length of string expected but %s found.' % s)
  14.         length = length * 10 + int(s.decode())
  15.         s = f.read(1)
  16.     if length == 0:
  17.         raise ValueError("Length of string expected but ':' found.")
  18.     return f.read(length).decode()


  19. def parse_torrent(f):
  20.     """Parse a torrent file, return a list of all trackers on it."""
  21.     trackers = []
  22.     if f.read(1) != b'd':
  23.         raise ValueError('Torrent not start with a dictionary.')
  24.         
  25.     key = _bencode_read_string(f)
  26.     if key == 'announce':
  27.         trackers.append(_bencode_read_string(f))
  28.         key = _bencode_read_string(f)
  29.      
  30.     if key == 'announce-list':
  31.         if f.read(1) != b'l':
  32.             raise ValueError('"announce-list" not contain a list.')
  33.         
  34.         while f.read(1) == b'l':
  35.             trackers.append(_bencode_read_string(f))
  36.             if f.read(1) != b'e':
  37.                 raise ValueError('Item of "announce-list" contain '
  38.                                  'multiple value.')
  39.     return trackers


  40. def _tracker_conn_info(url):
  41.     """Given a URL of trackers, return a list of
  42.     tuples [IP address, protocol (tcp or udp), port].
  43.     """
  44.     url = urlparse(url)
  45.     addrs = nslookup(url.hostname)
  46.     proto = url.scheme
  47.     if proto == 'http':
  48.         proto = 'tcp'
  49.     elif proto != 'udp':
  50.         raise ValueError('Unknown tracker protocol: %s' % proto)
  51.     port = url.port
  52.     if url.port is None:
  53.         port = 6881

  54.     return [(addr, proto, port) for addr in addrs]


  55. @lru_cache()
  56. def nslookup(domain):
  57.     """Look up DNS for the domain name, return a list of IPv4 addresses."""
  58.     infos = getaddrinfo(domain, 1, AF_INET, proto=SOL_TCP)
  59.     return [info[4][0] for info in infos]


  60. def generate_rules(f, func_print):
  61.     """Read tracker URLs from f, print firewall rules
  62.     via func_print(addr, proto, port).
  63.     """
  64.     trackers = set()
  65.     for url in f:
  66.         url = url.strip()
  67.         if url[0] in '#;"':
  68.             continue
  69.         try:
  70.             new_trackers = set(_tracker_conn_info(url)) - trackers
  71.         except (ValueError, OSError) as e:
  72.             print('Failed to resolve ', url, ':', e, file=sys.stderr)
  73.             continue

  74.         for tracker in new_trackers:
  75.             func_print(*tracker)
  76.             
  77.         trackers |= new_trackers


  78. def action_torrent(args):
  79.     trackers = set()
  80.     for f in args.files:
  81.         try:
  82.             new_trackers = set(parse_torrent(f)) - trackers
  83.             f.close()
  84.             for tracker in new_trackers:
  85.                 print(tracker)
  86.             trackers |= new_trackers
  87.         except (ValueError, UnicodeDecodeError, IOError) as e:
  88.             print('Failed to parse file', f.name, ':', e, file=sys.stderr)


  89. def action_ipset(args):
  90.     print('create -exist %s hash:ip,port family inet' % args.setname)
  91.     def print_command(addr, proto, port):
  92.         print('add -exist %s %s,%s:%s' % (args.setname, addr, proto, port))
  93.     generate_rules(args.trackers, print_command)


  94. def action_iptables(args):
  95.     table = ''
  96.     if args.table is not None:
  97.         table = ' -t %s' % args.table
  98.     def print_command(addr, proto, port):
  99.         print('iptables%s -A %s -d %s -p %s --dport %s -j %s' % \
  100.               (table, args.chain, addr, proto, port, args.target))
  101.     generate_rules(args.trackers, print_command)


  102. def action_raw(args):
  103.     generate_rules(args.trackers, print)


  104. def parse_args():
  105.     parser = argparse.ArgumentParser(description='BitTorrent Trackers '
  106.                                                  'firewall helper.')
  107.     subparsers = parser.add_subparsers(dest='action')
  108.    
  109.     tor = subparsers.add_parser('torrent',
  110.                                 help='parse tracker URLs from torrent files.')
  111.     tor.add_argument('files', nargs='+', metavar='FILE',
  112.                      type=argparse.FileType('rb'),
  113.                      help='a torrent file.')
  114.                      
  115.     TRACKERS_KWARGS = dict(metavar='TRACKER-LIST',
  116.                            type=argparse.FileType('r'),
  117.                            help='a file contains tracert URLs line by line, '
  118.                                 'or "-" for stdin.')

  119.     ips = subparsers.add_parser('ipset', help='generate ipset restore file.')
  120.     ips.add_argument('setname', metavar='SETNAME',
  121.                      help='the setname of ipset.')
  122.     ips.add_argument('trackers', **TRACKERS_KWARGS)

  123.     ipt = subparsers.add_parser('iptables', help='generate iptables commands.')
  124.     ipt.add_argument('-t', '--table', metavar='TABLE',
  125.                      help='which iptables tables add rules to.')
  126.     ipt.add_argument('chain', metavar='CHAIN',
  127.                      help='which iptables chain add rules to.')
  128.     ipt.add_argument('target', metavar='TARGET',
  129.                      help='which iptables target rules jump to.')
  130.     ipt.add_argument('trackers', **TRACKERS_KWARGS)

  131.     raw = subparsers.add_parser('raw', help='print plain server connection info.')
  132.     raw.add_argument('trackers', **TRACKERS_KWARGS)

  133.     return parser.parse_args()


  134. def main():
  135.     args = parse_args()

  136.     if args.action == 'torrent':
  137.         action_torrent(args)
  138.     elif args.action == 'ipset':
  139.         action_ipset(args)
  140.     elif args.action == 'iptables':
  141.         action_iptables(args)
  142.     elif args.action == 'raw':
  143.         action_raw(args)
  144.     else:
  145.         print('Usage: %s -h/--help' % sys.argv[0], file=sys.stderr)
  146.         sys.exit(2)


  147. if __name__ == '__main__':
  148.     main()
复制代码


https://blog.sorz.org/p/bt-tracker-helper/



WIFIHELL | 万丰乐活 2020开启新的征程,好货不断!
关闭

站点推荐上一条 /1 下一条

万丰乐活

GMT+8, 2024-4-20 21:50

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表