diff --git a/.build/ddns.spec b/.build/ddns.spec index 5a58016..dd997d6 100644 --- a/.build/ddns.spec +++ b/.build/ddns.spec @@ -21,7 +21,8 @@ a = Analysis(['../run.py'], 'dns.dnspod_com', 'dns.dnscom', 'dns.cloudflare', - 'dns.huaweidns' + 'dns.huaweidns', + 'dns.callback' ], hookspath=[], runtime_hooks=[], diff --git a/README.md b/README.md index c1e2204..487c52e 100644 --- a/README.md +++ b/README.md @@ -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 时留空)
HE.net 可留空 | | token | string | √ | 无 | api 授权 token | 部分平台叫 secret key , **反馈粘贴时删除** | -| dns | string | No | `"dnspod"` | dns 服务商 | 阿里`alidns`,
dns.com 为`dnscom`,
DNSPOD 国际版`dnspod_com`,
HE.net 为`he`,华为 DNS 为`huaweidns` | +| dns | string | No | `"dnspod"` | dns 服务商 | 阿里`alidns`,
dns.com 为`dnscom`,
DNSPOD 国际版`dnspod_com`,
HE.net 为`he`,华为 DNS 为`huaweidns`,
自定义回调为`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, diff --git a/dns/callback.py b/dns/callback.py new file mode 100644 index 0000000..57f55f6 --- /dev/null +++ b/dns/callback.py @@ -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 diff --git a/schema/v2.8.json b/schema/v2.8.json index c2cdf8a..aad3a2c 100644 --- a/schema/v2.8.json +++ b/schema/v2.8.json @@ -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": {