海外站点生存指南之自动更换 Cloudflare 解析 IP
如今海外站点生存愈发艰难,但我着实需要它跑些任务,再买个国内服有点浪费,所以暂时先套 CF 苟活,其他人建议尽早转国内备案。实际上,之前已用七牛做了动静分离,并通过 CNAME 接入 CF 实现了 DNS 分运营商解析,尽管如此,抽风还是时有发生,几乎每个月都要手动更换一次 A 记录,次数一多难免烦躁,于是搜寻了一下自动扫可用 IP 的方案。大体观察过后发现直接扫可能比较耗资源,且之后还要手动三网测速,总归不是那么尽如人意。想了想,我其实对可用性要求并不高,不如跟随一个长期使用 CF 且站点稳定的大佬,定时扫描他的站点 IP,如不同则自动更换。那么话不多说,现在就贴一下代码。
保存源码
[collapse title="Python Code"]
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import json
import requests
import logging
import traceback
OBJECT = {
'DOMAIN': 'logi.im', # 你的域名
'SUB_DOMAINS': ['@', 'www']
}
UPSTREAM = '' # 跟随域名
DNSPOD_LOGIN_TOKEN = '' # DNSPOD TOKEN
IFTTT = {
'WEBHOOK_KEY': '', # TG 推送
'EVENT_NAME': ''
}
SERVER_CHAN_SCKEY = '' # 微信推送
LOG_FILE = '/root/cf.log' # 日志文件
LINES = '移动 联通 电信 教育网'.split(' ')
ISPS = {
LINES[0]: [
'218.207.255.254',
'218.205.255.254',
'221.131.255.254'
],
LINES[1]: [
'114.255.255.254',
'163.204.255.254',
'220.251.255.254'
],
LINES[2]: [
'180.149.159.254',
'113.111.255.254',
'116.239.255.254'
],
LINES[3]: [
'183.175.255.254',
'202.199.255.254',
'219.229.255.254'
]
}
logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s')
def post_form_data(url, payload):
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
response = requests.request("POST", url, headers=headers, data=payload)
return json.loads(response.text)
def send_msg(tt, msg=None):
msg = json.dumps(msg)
ev = IFTTT['EVENT_NAME']
wk = IFTTT['WEBHOOK_KEY']
url = 'https://maker.ifttt.com/trigger/{ev}/with/key/{wk}?value1={tt}&value2={msg}'.format_map(vars())
requests.get(url)
scs = SERVER_CHAN_SCKEY
url = 'http://sc.ftqq.com/{scs}.send?text={tt}'.format_map(vars())
post_form_data(url, {'text': tt, 'desp': msg})
def get_upstream_ip(ip):
dn = UPSTREAM
url = 'http://119.29.29.29/d?dn={dn}&ip={ip}'.format_map(vars())
return requests.get(url).text
def get_object_ips():
url = 'https://dnsapi.cn/Record.List'
payload = {
'login_token': DNSPOD_LOGIN_TOKEN,
'format': 'json',
'domain': OBJECT['DOMAIN'],
'record_type': 'A'
}
records = post_form_data(url, payload)['records']
result = {}
for record in records:
if record['name'] in OBJECT['SUB_DOMAINS'] and record['line'] in LINES:
if record['line'] not in result:
result[record['line']] = {}
result[record['line']]['ids'] = ''
result[record['line']]['value'] = record['value']
result[record['line']]['ids'] += record['id'] + ','
for line in result:
result[line]['ids'] = result[line]['ids'][:-1]
return result
def update_object_ips(differences):
url = 'https://dnsapi.cn/Batch.Record.Modify'
result = {}
for line in differences:
payload = {
'login_token': DNSPOD_LOGIN_TOKEN,
'format': 'json',
'record_id': differences[line]['ids'],
'change': 'value',
'change_to': differences[line]['value']
}
result[line] = post_form_data(url, payload)
send_msg('CF IP 更新成功!', json.dumps(result, ensure_ascii=False, encoding='utf-8'))
def compare_upsteam_with_object():
object_ips = get_object_ips()
differences = {}
for line in object_ips:
count = 0
upstream_ip = ''
for IP in ISPS[line]:
upstream_ip = get_upstream_ip(IP).split(';')[0]
if upstream_ip != '' and object_ips[line]['value'] != upstream_ip:
count += 1
if count == len(ISPS[line]): # 同一运营商所有地点的解析全部不同则跟随上游更新
differences[line] = object_ips[line]
differences[line]['value'] = upstream_ip
if len(differences) != 0:
update_object_ips(differences)
try:
compare_upsteam_with_object()
except Exception as e:
send_msg('CF IP 更新失败,请及时处理!')
logging.debug(traceback.format_exc())
[/collapse]
定时调用
脚本基于 Python3
和 DNSPod API
,搭配 crontab
可定时分运营商比对 自身
和 跟随站点
IP,发现多地不同后会更新解析记录为跟随站点,同时推送通知到 TG 和微信。
# 2 小时执行一次
echo -e "0 */2 * * * /root/cf.py\n`crontab -l`" | crontab -
备用方法
最后一并记录 Hostloc 大佬的 IP 扫描器源码,以备不时之需。
首先安装 php7+
和 zmap
。
apt -y install zmap php7.0 php7.0-curl
然后,保存验证源码为 cff.php
。
[collapse title="PHP Code"]
<?PHP
ini_set('memory_limit', '-1');
function partition($list, $p)
{
$listlen = count($list);
$partlen = floor($listlen / $p);
$partrem = $listlen % $p;
$partition = array();
$mark = 0;
for ($px = 0; $px < $p; $px++) {
$incr = ($px < $partrem) ? $partlen + 1 : $partlen;
$partition[$px] = array_slice($list, $mark, $incr);
$mark += $incr;
}
return $partition;
}
if ($argc < 4) {
echo "CloudFlare filter\nVersion: 0.4\n";
echo "Usage: php {$argv[0]} <raw file> <Verification file> <threads> [verify domain] [verify path] [verify value]\n";
echo "Power by Kagurazaka Shira\n";
exit;
}
if (!file_exists("$argv[1]")) {
exit("Invalid input file!\n");
}
if (file_exists("$argv[2]")) {
unlink($argv[2]);
}
$array = file($argv[1], FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); //列表
$childcount = $argv[3]; //线程数
$array = array_unique($array);
$part = array();
$part = partition($array, $childcount); //列表分割
$vname = $argv[2];
$domain = empty($argv[4]) ? "cfv.virtualizor.com" : $argv[4];
$vpath = empty($argv[5]) ? "/srk.css" : $argv[5];
$vvalue = empty($argv[6]) ? "srk_verify" : $argv[6];
for ($i = 0; $i < $childcount; $i++) {
$pid = pcntl_fork();
if ($pid == -1) {
echo "Forking failed on loop $i\n";
exit;
} else if ($pid) {
continue;
} else {
foreach ($part[$i] as $ip) {
$url = "http://{$ip}/cdn-cgi/trace";
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_SSL_VERIFYPEER => false,
// CURLOPT_PROXYTYPE => CURLPROXY_SOCKS5,
// CURLOPT_PROXY => $ip,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
"Host: {$domain}",
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
],
// CURLOPT_USERAGENT => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
]);
$return = curl_exec($ch);
if (empty($return)) {
echo "{$ip} down\n";
curl_close($ch);
continue;
}
// var_dump($return);
if (strpos($return, "h={$domain}")) {
curl_setopt($ch, CURLOPT_URL, "http://{$ip}" . $vpath);
$return = curl_exec($ch);
// var_dump($return);
if (strpos($return, $vvalue) !== false) {
echo "{$ip} success\n";
file_put_contents($vname, $ip . "\n", FILE_APPEND | LOCK_EX);
curl_close($ch);
continue;
}
}
echo "{$ip} bad\n";
curl_close($ch);
}
exit;
}
}
for ($j = 0; $j < $childcount; $j++) {
$pid = pcntl_wait($status);
}
$v_arr = file($vname, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); //列表
$v_arr = array_unique($v_arr);
foreach ($v_arr as &$v) {
$v = ip2long($v);
}
sort($v_arr);
foreach ($v_arr as &$v) {
$v = long2ip($v);
}
file_put_contents($vname, implode("\n", $v_arr));
[/collapse]
最后,调用 zmap
预扫,并使用 cff.php
验证。
#!/usr/bin/env bash
curl -sL https://www.cloudflare.com/ips-v4 -o full.txt
zmap -p 443 --whitelist-file=/root/full.txt -o cdn_ip.txt
php cff.php cdn_ip.txt v.txt 100
实际上,你可能还需手动或通过代码验证三网可用性。
参考文章
当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »