feat(dnsprovider): support custom callback api (#170)

* feat(dnsprovider): support custom callback api

* feat(callback): shouldn't try to parsing return

close #163
This commit is contained in:
老周部落
2020-04-12 13:27:45 +08:00
committed by GitHub
parent dd45dfbc9d
commit ad15059cbf
4 changed files with 135 additions and 5 deletions
+2 -1
View File
@@ -21,7 +21,8 @@ a = Analysis(['../run.py'],
'dns.dnspod_com',
'dns.dnscom',
'dns.cloudflare',
'dns.huaweidns'
'dns.huaweidns',
'dns.callback'
],
hookspath=[],
runtime_hooks=[],
+17 -2
View File
@@ -54,6 +54,7 @@
- [x] 可设置定时任务
- [x] TTL 配置支持
- [x] 本地文件缓存(减少 API 请求)
- [x] 地址变更时触发自定义回调API(与 DDNS 功能互斥)
## 使用
@@ -83,6 +84,7 @@
- [CloudFlare API Key](https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-API-key-) (除了`email + API KEY`,也可使用`Token`需要列出 Zone 权限)
- [HE.net DDNS 文档](https://dns.he.net/docs.html)(仅需将设置的密码填入`token`字段,`id`字段可留空)
- [华为 APIKEY 申请](https://console.huaweicloud.com/iam/)(点左边访问密钥,然后点新增访问密钥)
- 自定义回调的参数填写方式请查看下方的自定义回调配置说明
2. 修改配置文件,`ipv4``ipv6`字段,为待更新的域名,详细参照配置说明
@@ -109,7 +111,7 @@ python run.py -c /path/to/config.json
| :----: | :---------: | :------: | :---------: | :---------------: | ----------------------------------------------------------------------------------------------------------- |
| id | string | √ | 无 | api 访问 ID | Cloudflare 为邮箱(使用 Token 时留空)<br>HE.net 可留空 |
| token | string | √ | 无 | api 授权 token | 部分平台叫 secret key , **反馈粘贴时删除** |
| dns | string | No | `"dnspod"` | dns 服务商 | 阿里`alidns`,<br>dns.com 为`dnscom`,<br>DNSPOD 国际版`dnspod_com`,<br>HE.net 为`he`,华为 DNS 为`huaweidns` |
| dns | string | No | `"dnspod"` | dns 服务商 | 阿里`alidns`,<br>dns.com 为`dnscom`,<br>DNSPOD 国际版`dnspod_com`,<br>HE.net 为`he`,华为 DNS 为`huaweidns`<br>自定义回调为`callback` |
| ipv4 | array | No | `[]` | ipv4 域名列表 | 为`[]`时,不会获取和更新 IPv4 地址 |
| ipv6 | array | No | `[]` | ipv6 域名列表 | 为`[]`时,不会获取和更新 IPv6 地址 |
| index4 | string\|int | No | `"default"` | ipv4 获取方式 | 可设置`网卡`,`内网`,`公网`,`正则`等方式 |
@@ -133,6 +135,19 @@ python run.py -c /path/to/config.json
- 字符串`"shell:xxx"`: 使用系统 shell 运行`xxx`,并把结果 stdout 作为目标 IP
- `false`: 强制禁止更新 ipv4 或 ipv6 的 DNS 解析
#### 自定义回调配置说明
- `id` 字段填写回调地址,以 HTTP 或 HTTPS 开头,推荐采用 HTTPS 方式的回调 API ,当 `token` 字段非空且 URL 参数包含下表所示的常量字符串时,常量会被程序替换为实际值
- `token` 字段为 POST 参数,本字段为空或不存在则使用 GET 方式发起回调,回调参数采用 JSON 格式编码,当 JSON 的首层参数值包含下表所示的常量字符串时,常量会被程序替换为实际值
| 常量名称 | 常量内容 | 说明 |
| ---------------- | ---------------------- | -------- |
| `__DOMAIN__` | DDNS 域名 | |
| `__RECORDTYPE__` | DDNS 记录类型 | |
| `__TTL__` | DDNS TTL | |
| `__TIMESTAMP__` | 请求发起时间戳 | 包含小数 |
| `__IP__` | 获取的对应类型的IP地址 | |
#### 配置示例
```json
@@ -140,7 +155,7 @@ python run.py -c /path/to/config.json
"$schema": "https://ddns.newfuture.cc/schema/v2.8.json",
"id": "12345",
"token": "mytokenkey",
"dns": "dnspod 或 dnspod_com 或 alidns 或 dnscom 或 cloudflare 或 he 或 huaweidns",
"dns": "dnspod 或 dnspod_com 或 alidns 或 dnscom 或 cloudflare 或 he 或 huaweidns 或 callback",
"ipv4": ["ddns.newfuture.cc", "ipv4.ddns.newfuture.cc"],
"ipv6": ["ddns.newfuture.cc", "ipv6.ddns.newfuture.cc"],
"index4": 0,
+113
View File
@@ -0,0 +1,113 @@
# coding=utf-8
"""
Custom Callback API
自定义回调接口解析操作库
@author: 老周部落
"""
from json import loads as jsondecode, dumps as jsonencode
from logging import debug, info, warning
from time import time
try:
# python 2
from httplib import HTTPSConnection, HTTPConnection
from urlparse import urlparse, parse_qsl
from urllib import urlencode
except ImportError:
# python 3
from http.client import HTTPSConnection, HTTPConnection
from urllib.parse import urlencode, urlparse, parse_qsl
__author__ = '老周部落'
class Config:
ID = None # 自定义回调 URL
TOKEN = None # 使用 JSON 编码的 POST 参数
PROXY = None # 代理设置
TTL = None
def request(method, action, param=None, **params):
"""
发送请求数据
"""
if param:
params.update(param)
URLObj = urlparse(Config.ID)
params = dict((k, params[k]) for k in params if params[k] is not None)
info("%s/%s : %s", URLObj.netloc, action, params)
if Config.PROXY:
if URLObj.netloc == "http":
conn = HTTPConnection(Config.PROXY)
else:
conn = HTTPSConnection(Config.PROXY)
conn.set_tunnel(URLObj.netloc, URLObj.port)
else:
if URLObj.netloc == "http":
conn = HTTPConnection(URLObj.netloc, URLObj.port)
else:
conn = HTTPSConnection(URLObj.netloc, URLObj.port)
headers = {}
if method == "GET":
if params:
action += '?' + urlencode(params)
params = ""
else:
headers["Content-Type"] = "application/x-www-form-urlencoded"
params = urlencode(params)
conn.request(method, action, params, headers)
response = conn.getresponse()
res = response.read().decode('utf8')
conn.close()
if response.status < 200 or response.status >= 300:
warning('%s : error[%d]:%s', action, response.status, res)
raise Exception(res)
else:
debug('%s : result:%s', action, res)
return data
def replace_params(domain, record_type, ip, params):
"""
替换定义常量为实际值
"""
dict = {"__DOMAIN__": domain, "__RECORDTYPE__": record_type, "__TTL__": Config.TTL, "__TIMESTAMP__": time(), "__IP__": ip}
for key, value in params.items():
if dict.get(value):
params[key] = dict.get(value)
return params
def update_record(domain, value, record_type="A"):
"""
更新记录
"""
info(">>>>>%s(%s)", domain, record_type)
result = {}
if not Config.TOKEN: # 此处使用 TOKEN 参数透传 POST 参数所用的 JSON
method = "GET"
URLObj = urlparse(Config.ID)
path = URLObj.path
query = dict(parse_qsl(URLObj.query))
params = replace_params(domain, record_type, value, query)
else:
method = "POST"
URLObj = urlparse(Config.ID)
path = URLObj.path
params = replace_params(domain, record_type, value, jsondecode(Config.TOKEN))
res = request(method, path, params)
if res:
result = "Callback Request Success!\n" + res
else:
result = "Callback Request Fail!\n"
return result
+3 -2
View File
@@ -34,7 +34,7 @@
"$id": "/properties/dns",
"type": "string",
"title": "DNS Provider",
"description": "dns服务商:阿里为alidns,DNS.COM为dnscom,DNSPOD国际版为(dnspod_com),cloudflare,HE.net为he",
"description": "dns服务商:阿里为alidns,DNS.COM为dnscom,DNSPOD国际版为(dnspod_com),cloudflare,HE.net为he,华为DNS为huaweidns,自定义回调为callback",
"default": "dnspod",
"examples": [
"dnspod",
@@ -48,7 +48,8 @@
"dnspod_com",
"dnscom",
"he",
"huaweidns"
"huaweidns",
"callback"
]
},
"ipv4": {