ddns 4.1.0b1__tar.gz → 4.1.0b2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ddns might be problematic. Click here for more details.

Files changed (67) hide show
  1. {ddns-4.1.0b1 → ddns-4.1.0b2}/PKG-INFO +22 -13
  2. {ddns-4.1.0b1 → ddns-4.1.0b2}/README.md +21 -12
  3. ddns-4.1.0b2/ddns/__init__.py +12 -0
  4. ddns-4.1.0b2/ddns/__main__.py +118 -0
  5. {ddns-4.1.0b1/ddns/util → ddns-4.1.0b2/ddns}/cache.py +42 -11
  6. {ddns-4.1.0b1/ddns/util → ddns-4.1.0b2/ddns}/ip.py +11 -9
  7. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/__init__.py +24 -4
  8. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/_base.py +78 -198
  9. ddns-4.1.0b2/ddns/provider/_signature.py +113 -0
  10. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/alidns.py +31 -14
  11. ddns-4.1.0b2/ddns/provider/aliesa.py +129 -0
  12. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/callback.py +9 -10
  13. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/cloudflare.py +8 -8
  14. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/dnscom.py +5 -5
  15. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/dnspod.py +3 -4
  16. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/dnspod_com.py +1 -1
  17. ddns-4.1.0b2/ddns/provider/edgeone.py +82 -0
  18. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/he.py +4 -4
  19. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/huaweidns.py +8 -8
  20. ddns-4.1.0b2/ddns/provider/namesilo.py +159 -0
  21. ddns-4.1.0b2/ddns/provider/noip.py +103 -0
  22. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/tencentcloud.py +7 -7
  23. ddns-4.1.0b2/ddns/util/comment.py +88 -0
  24. ddns-4.1.0b2/ddns/util/http.py +228 -0
  25. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns.egg-info/PKG-INFO +22 -13
  26. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns.egg-info/SOURCES.txt +21 -7
  27. ddns-4.1.0b1/tests/test_util_cache.py → ddns-4.1.0b2/tests/test_cache.py +195 -15
  28. ddns-4.1.0b2/tests/test_config_cli.py +547 -0
  29. ddns-4.1.0b2/tests/test_config_config.py +393 -0
  30. ddns-4.1.0b2/tests/test_config_env.py +552 -0
  31. ddns-4.1.0b2/tests/test_config_file.py +572 -0
  32. ddns-4.1.0b2/tests/test_config_init.py +427 -0
  33. ddns-4.1.0b1/tests/test_hmac_sha256_authorization.py → ddns-4.1.0b2/tests/test_provider__signature.py +4 -4
  34. {ddns-4.1.0b1 → ddns-4.1.0b2}/tests/test_provider_alidns.py +33 -33
  35. ddns-4.1.0b2/tests/test_provider_aliesa.py +533 -0
  36. {ddns-4.1.0b1 → ddns-4.1.0b2}/tests/test_provider_base.py +81 -30
  37. ddns-4.1.0b1/tests/test_provider_simple.py → ddns-4.1.0b2/tests/test_provider_base_simple.py +38 -100
  38. {ddns-4.1.0b1 → ddns-4.1.0b2}/tests/test_provider_callback.py +42 -42
  39. {ddns-4.1.0b1 → ddns-4.1.0b2}/tests/test_provider_cloudflare.py +47 -47
  40. {ddns-4.1.0b1 → ddns-4.1.0b2}/tests/test_provider_debug.py +16 -16
  41. {ddns-4.1.0b1 → ddns-4.1.0b2}/tests/test_provider_dnscom.py +37 -39
  42. {ddns-4.1.0b1 → ddns-4.1.0b2}/tests/test_provider_dnspod.py +11 -14
  43. {ddns-4.1.0b1 → ddns-4.1.0b2}/tests/test_provider_dnspod_com.py +15 -15
  44. ddns-4.1.0b2/tests/test_provider_edgeone.py +507 -0
  45. {ddns-4.1.0b1 → ddns-4.1.0b2}/tests/test_provider_he.py +37 -37
  46. {ddns-4.1.0b1 → ddns-4.1.0b2}/tests/test_provider_huaweidns.py +24 -24
  47. ddns-4.1.0b2/tests/test_provider_namesilo.py +317 -0
  48. ddns-4.1.0b2/tests/test_provider_noip.py +386 -0
  49. {ddns-4.1.0b1 → ddns-4.1.0b2}/tests/test_provider_tencentcloud.py +12 -12
  50. ddns-4.1.0b2/tests/test_util_comment.py +248 -0
  51. ddns-4.1.0b2/tests/test_util_http.py +270 -0
  52. ddns-4.1.0b1/ddns/__init__.py +0 -21
  53. ddns-4.1.0b1/ddns/__main__.py +0 -193
  54. ddns-4.1.0b1/ddns/util/config.py +0 -314
  55. ddns-4.1.0b1/ddns/util/http.py +0 -277
  56. ddns-4.1.0b1/tests/test_config_ssl.py +0 -191
  57. ddns-4.1.0b1/tests/test_util_http.py +0 -597
  58. {ddns-4.1.0b1 → ddns-4.1.0b2}/LICENSE +0 -0
  59. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/__builtins__.pyi +0 -0
  60. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/provider/debug.py +0 -0
  61. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns/util/__init__.py +0 -0
  62. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns.egg-info/dependency_links.txt +0 -0
  63. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns.egg-info/entry_points.txt +0 -0
  64. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns.egg-info/requires.txt +0 -0
  65. {ddns-4.1.0b1 → ddns-4.1.0b2}/ddns.egg-info/top_level.txt +0 -0
  66. {ddns-4.1.0b1 → ddns-4.1.0b2}/pyproject.toml +0 -0
  67. {ddns-4.1.0b1 → ddns-4.1.0b2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddns
3
- Version: 4.1.0b1
3
+ Version: 4.1.0b2
4
4
  Summary: Dynamic DNS client for multiple providers, supporting IPv4 and IPv6.
5
5
  Author-email: NewFuture <python@newfuture.cc>
6
6
  License-Expression: MIT
@@ -84,12 +84,16 @@ Dynamic: license-file
84
84
  - 服务商支持:
85
85
  - [DNSPOD](https://www.dnspod.cn/) ([配置指南](doc/providers/dnspod.md))
86
86
  - [阿里 DNS](http://www.alidns.com/) ([配置指南](doc/providers/alidns.md)) ⚡
87
- - [DNS.COM](https://www.dns.com/) (@loftor-git)
88
- - [DNSPOD 国际版](https://www.dnspod.com/)
89
- - [CloudFlare](https://www.cloudflare.com/) (@tongyifan)
90
- - [HE.net](https://dns.he.net/) (@NN708) (不支持自动创建记录)
91
- - [华为云](https://huaweicloud.com/) (@cybmp3)
87
+ - [阿里云边缘安全加速(ESA)](https://esa.console.aliyun.com/) ([配置指南](doc/providers/aliesa.md)) ⚡
88
+ - [DNS.COM](https://www.dns.com/) ([配置指南](doc/providers/51dns.md)) (@loftor-git)
89
+ - [DNSPOD 国际版](https://www.dnspod.com/) ([配置指南](doc/providers/dnspod_com.md))
90
+ - [CloudFlare](https://www.cloudflare.com/) ([配置指南](doc/providers/cloudflare.md)) (@tongyifan)
91
+ - [HE.net](https://dns.he.net/) ([配置指南](doc/providers/he.md)) (@NN708) (不支持自动创建记录)
92
+ - [华为云](https://huaweicloud.com/) ([配置指南](doc/providers/huaweidns.md)) (@cybmp3) ⚡
93
+ - [NameSilo](https://www.namesilo.com/) ([配置指南](doc/providers/namesilo.md))
92
94
  - [腾讯云](https://cloud.tencent.com/) ([配置指南](doc/providers/tencentcloud.md)) ⚡
95
+ - [腾讯云 EdgeOne](https://cloud.tencent.com/product/teo) ([配置指南](doc/providers/edgeone.md)) ⚡
96
+ - [No-IP](https://www.noip.com/) ([配置指南](doc/providers/noip.md))
93
97
  - 自定义回调 API ([配置指南](doc/providers/callback.md))
94
98
 
95
99
  > ⚡ 标记的服务商使用高级 HMAC-SHA256 签名认证,提供企业级安全保障
@@ -161,12 +165,16 @@ Dynamic: license-file
161
165
 
162
166
  - **DNSPOD(中国版)**: [创建 token](https://support.dnspod.cn/Kb/showarticle/tsid/227/) | [详细配置文档](doc/providers/dnspod.md)
163
167
  - **阿里云 DNS**: [申请 accesskey](https://help.aliyun.com/document_detail/87745.htm) | [详细配置文档](doc/providers/alidns.md)
164
- - **DNS.COM**: [API Key/Secret](https://www.dns.com/member/apiSet)
165
- - **DNSPOD(国际版)**: [获取 token](https://www.dnspod.com/docs/info.html#get-the-user-token)
166
- - **CloudFlare**: [API Key](https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-API-key-)(除了 `email + API KEY`,也可使用 `Token`,**需要list Zone 权限**)
167
- - **HE.net**: [DDNS 文档](https://dns.he.net/docs.html)(仅需将设置的密码填入 `token` 字段,`id` 字段可留空)
168
- - **华为云 DNS**: [APIKEY 申请](https://console.huaweicloud.com/iam/)(点左边访问密钥,然后点新增访问密钥)
168
+ - **阿里云边缘安全加速(ESA)**: [申请 accesskey](https://help.aliyun.com/document_detail/87745.htm) | [详细配置文档](doc/providers/aliesa.md)
169
+ - **51DNS(dns.com)**: [API Key/Secret](https://www.dns.com/member/apiSet) | [详细配置文档](doc/providers/51dns.md)
170
+ - **DNSPOD(国际版)**: [获取 token](https://www.dnspod.com/docs/info.html#get-the-user-token) | [详细配置文档](doc/providers/dnspod_com.md)
171
+ - **CloudFlare**: [API Key](https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-API-key-)(除了 `email + API KEY`,也可使用 `Token`,**需要list Zone 权限**) | [详细配置文档](doc/providers/cloudflare.md)
172
+ - **HE.net**: [DDNS 文档](https://dns.he.net/docs.html)(仅需将设置的密码填入 `token` 字段,`id` 字段可留空) | [详细配置文档](doc/providers/he.md)
173
+ - **华为云 DNS**: [APIKEY 申请](https://console.huaweicloud.com/iam/)(点左边访问密钥,然后点新增访问密钥) | [详细配置文档](doc/providers/huaweidns.md)
174
+ - **NameSilo**: [API Key](https://www.namesilo.com/account/api-manager)(API Manager 中获取 API Key) | [详细配置文档](doc/providers/namesilo.md)
169
175
  - **腾讯云 DNS**: [详细配置文档](doc/providers/tencentcloud.md)
176
+ - **腾讯云 EdgeOne**: [详细配置文档](doc/providers/edgeone.md)
177
+ - **No-IP**: [用户名和密码](https://www.noip.com/)(使用 No-IP 账户的用户名和密码) | [详细配置文档](doc/providers/noip.md)
170
178
  - **自定义回调**: 参数填写方式请查看下方的自定义回调配置说明
171
179
 
172
180
  2. 修改配置文件,`ipv4` 和 `ipv6` 字段,为待更新的域名,详细参照配置说明
@@ -217,7 +225,7 @@ python -m ddns -c /path/to/config.json
217
225
  | :----: | :----------------: | :------: | :---------: | :----------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
218
226
  | id | string | √ | 无 | api 访问 ID | Cloudflare 为邮箱(使用 Token 时留空)<br>HE.net 可留空<br>华为云为 Access Key ID (AK) |
219
227
  | token | string | √ | 无 | api 授权 token | 部分平台叫 secret key,**反馈粘贴时删除** |
220
- | dns | string | No | `"dnspod"` | dns 服务商 | 阿里 DNS 为 `alidns`,Cloudflare 为 `cloudflare`,dns.com 为 `dnscom`,DNSPOD 国内为 `dnspod`,DNSPOD 国际为 `dnspod_com`,HE.net 为 `he`,华为云为 `huaweidns`,腾讯云为 `tencentcloud`,自定义回调为 `callback`。部分服务商有[详细配置文档](doc/providers/) |
228
+ | dns | string | No | `"dnspod"` | dns 服务商 | 阿里 DNS 为 `alidns`,阿里ESA为 `aliesa`,Cloudflare 为 `cloudflare`,dns.com 为 `dnscom`,DNSPOD 国内为 `dnspod`,DNSPOD 国际为 `dnspod_com`,HE.net 为 `he`,华为云为 `huaweidns`,NameSilo 为 `namesilo`,腾讯云为 `tencentcloud`,腾讯云EdgeOne为 `edgeone`,No-IP 为 `noip`,自定义回调为 `callback`。部分服务商有[详细配置文档](doc/providers/) |
221
229
  | ipv4 | array | No | `[]` | ipv4 域名列表 | 为 `[]` 时,不会获取和更新 IPv4 地址 |
222
230
  | ipv6 | array | No | `[]` | ipv6 域名列表 | 为 `[]` 时,不会获取和更新 IPv6 地址 |
223
231
  | index4 | string\|int\|array | No | `"default"` | ipv4 获取方式 | 可设置 `网卡`、`内网`、`公网`、`正则` 等方式 |
@@ -266,7 +274,7 @@ python -m ddns -c /path/to/config.json
266
274
  "$schema": "https://ddns.newfuture.cc/schema/v4.0.json",
267
275
  "id": "12345",
268
276
  "token": "mytokenkey",
269
- "dns": "dnspod 或 dnspod_com 或 alidns 或 dnscom 或 cloudflare 或 he 或 huaweidns 或 tencentcloud 或 callback",
277
+ "dns": "dnspod 或 dnspod_com 或 alidns 或 aliesa 或 dnscom 或 cloudflare 或 he 或 huaweidns 或 namesilo 或 tencentcloud 或 noip 或 callback",
270
278
  "ipv4": ["ddns.newfuture.cc", "ipv4.ddns.newfuture.cc"],
271
279
  "ipv6": ["ddns.newfuture.cc", "ipv6.ddns.newfuture.cc"],
272
280
  "index4": 0,
@@ -330,6 +338,7 @@ Docker 镜像在无额外参数的情况下,已默认启用每 5 分钟执行
330
338
  使用系统自带的 IE 浏览器访问一次对应的 API 即可
331
339
 
332
340
  - alidns 打开: <https://alidns.aliyuncs.com>
341
+ - aliesa 打开: <https://esa.cn-hangzhou.aliyuncs.com>
333
342
  - cloudflare 打开: <https://api.cloudflare.com>
334
343
  - dns.com 打开: <https://www.dns.com>
335
344
  - dnspod.cn 打开: <https://dnsapi.cn>
@@ -39,12 +39,16 @@
39
39
  - 服务商支持:
40
40
  - [DNSPOD](https://www.dnspod.cn/) ([配置指南](doc/providers/dnspod.md))
41
41
  - [阿里 DNS](http://www.alidns.com/) ([配置指南](doc/providers/alidns.md)) ⚡
42
- - [DNS.COM](https://www.dns.com/) (@loftor-git)
43
- - [DNSPOD 国际版](https://www.dnspod.com/)
44
- - [CloudFlare](https://www.cloudflare.com/) (@tongyifan)
45
- - [HE.net](https://dns.he.net/) (@NN708) (不支持自动创建记录)
46
- - [华为云](https://huaweicloud.com/) (@cybmp3)
42
+ - [阿里云边缘安全加速(ESA)](https://esa.console.aliyun.com/) ([配置指南](doc/providers/aliesa.md)) ⚡
43
+ - [DNS.COM](https://www.dns.com/) ([配置指南](doc/providers/51dns.md)) (@loftor-git)
44
+ - [DNSPOD 国际版](https://www.dnspod.com/) ([配置指南](doc/providers/dnspod_com.md))
45
+ - [CloudFlare](https://www.cloudflare.com/) ([配置指南](doc/providers/cloudflare.md)) (@tongyifan)
46
+ - [HE.net](https://dns.he.net/) ([配置指南](doc/providers/he.md)) (@NN708) (不支持自动创建记录)
47
+ - [华为云](https://huaweicloud.com/) ([配置指南](doc/providers/huaweidns.md)) (@cybmp3) ⚡
48
+ - [NameSilo](https://www.namesilo.com/) ([配置指南](doc/providers/namesilo.md))
47
49
  - [腾讯云](https://cloud.tencent.com/) ([配置指南](doc/providers/tencentcloud.md)) ⚡
50
+ - [腾讯云 EdgeOne](https://cloud.tencent.com/product/teo) ([配置指南](doc/providers/edgeone.md)) ⚡
51
+ - [No-IP](https://www.noip.com/) ([配置指南](doc/providers/noip.md))
48
52
  - 自定义回调 API ([配置指南](doc/providers/callback.md))
49
53
 
50
54
  > ⚡ 标记的服务商使用高级 HMAC-SHA256 签名认证,提供企业级安全保障
@@ -116,12 +120,16 @@
116
120
 
117
121
  - **DNSPOD(中国版)**: [创建 token](https://support.dnspod.cn/Kb/showarticle/tsid/227/) | [详细配置文档](doc/providers/dnspod.md)
118
122
  - **阿里云 DNS**: [申请 accesskey](https://help.aliyun.com/document_detail/87745.htm) | [详细配置文档](doc/providers/alidns.md)
119
- - **DNS.COM**: [API Key/Secret](https://www.dns.com/member/apiSet)
120
- - **DNSPOD(国际版)**: [获取 token](https://www.dnspod.com/docs/info.html#get-the-user-token)
121
- - **CloudFlare**: [API Key](https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-API-key-)(除了 `email + API KEY`,也可使用 `Token`,**需要list Zone 权限**)
122
- - **HE.net**: [DDNS 文档](https://dns.he.net/docs.html)(仅需将设置的密码填入 `token` 字段,`id` 字段可留空)
123
- - **华为云 DNS**: [APIKEY 申请](https://console.huaweicloud.com/iam/)(点左边访问密钥,然后点新增访问密钥)
123
+ - **阿里云边缘安全加速(ESA)**: [申请 accesskey](https://help.aliyun.com/document_detail/87745.htm) | [详细配置文档](doc/providers/aliesa.md)
124
+ - **51DNS(dns.com)**: [API Key/Secret](https://www.dns.com/member/apiSet) | [详细配置文档](doc/providers/51dns.md)
125
+ - **DNSPOD(国际版)**: [获取 token](https://www.dnspod.com/docs/info.html#get-the-user-token) | [详细配置文档](doc/providers/dnspod_com.md)
126
+ - **CloudFlare**: [API Key](https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-API-key-)(除了 `email + API KEY`,也可使用 `Token`,**需要list Zone 权限**) | [详细配置文档](doc/providers/cloudflare.md)
127
+ - **HE.net**: [DDNS 文档](https://dns.he.net/docs.html)(仅需将设置的密码填入 `token` 字段,`id` 字段可留空) | [详细配置文档](doc/providers/he.md)
128
+ - **华为云 DNS**: [APIKEY 申请](https://console.huaweicloud.com/iam/)(点左边访问密钥,然后点新增访问密钥) | [详细配置文档](doc/providers/huaweidns.md)
129
+ - **NameSilo**: [API Key](https://www.namesilo.com/account/api-manager)(API Manager 中获取 API Key) | [详细配置文档](doc/providers/namesilo.md)
124
130
  - **腾讯云 DNS**: [详细配置文档](doc/providers/tencentcloud.md)
131
+ - **腾讯云 EdgeOne**: [详细配置文档](doc/providers/edgeone.md)
132
+ - **No-IP**: [用户名和密码](https://www.noip.com/)(使用 No-IP 账户的用户名和密码) | [详细配置文档](doc/providers/noip.md)
125
133
  - **自定义回调**: 参数填写方式请查看下方的自定义回调配置说明
126
134
 
127
135
  2. 修改配置文件,`ipv4` 和 `ipv6` 字段,为待更新的域名,详细参照配置说明
@@ -172,7 +180,7 @@ python -m ddns -c /path/to/config.json
172
180
  | :----: | :----------------: | :------: | :---------: | :----------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
173
181
  | id | string | √ | 无 | api 访问 ID | Cloudflare 为邮箱(使用 Token 时留空)<br>HE.net 可留空<br>华为云为 Access Key ID (AK) |
174
182
  | token | string | √ | 无 | api 授权 token | 部分平台叫 secret key,**反馈粘贴时删除** |
175
- | dns | string | No | `"dnspod"` | dns 服务商 | 阿里 DNS 为 `alidns`,Cloudflare 为 `cloudflare`,dns.com 为 `dnscom`,DNSPOD 国内为 `dnspod`,DNSPOD 国际为 `dnspod_com`,HE.net 为 `he`,华为云为 `huaweidns`,腾讯云为 `tencentcloud`,自定义回调为 `callback`。部分服务商有[详细配置文档](doc/providers/) |
183
+ | dns | string | No | `"dnspod"` | dns 服务商 | 阿里 DNS 为 `alidns`,阿里ESA为 `aliesa`,Cloudflare 为 `cloudflare`,dns.com 为 `dnscom`,DNSPOD 国内为 `dnspod`,DNSPOD 国际为 `dnspod_com`,HE.net 为 `he`,华为云为 `huaweidns`,NameSilo 为 `namesilo`,腾讯云为 `tencentcloud`,腾讯云EdgeOne为 `edgeone`,No-IP 为 `noip`,自定义回调为 `callback`。部分服务商有[详细配置文档](doc/providers/) |
176
184
  | ipv4 | array | No | `[]` | ipv4 域名列表 | 为 `[]` 时,不会获取和更新 IPv4 地址 |
177
185
  | ipv6 | array | No | `[]` | ipv6 域名列表 | 为 `[]` 时,不会获取和更新 IPv6 地址 |
178
186
  | index4 | string\|int\|array | No | `"default"` | ipv4 获取方式 | 可设置 `网卡`、`内网`、`公网`、`正则` 等方式 |
@@ -221,7 +229,7 @@ python -m ddns -c /path/to/config.json
221
229
  "$schema": "https://ddns.newfuture.cc/schema/v4.0.json",
222
230
  "id": "12345",
223
231
  "token": "mytokenkey",
224
- "dns": "dnspod 或 dnspod_com 或 alidns 或 dnscom 或 cloudflare 或 he 或 huaweidns 或 tencentcloud 或 callback",
232
+ "dns": "dnspod 或 dnspod_com 或 alidns 或 aliesa 或 dnscom 或 cloudflare 或 he 或 huaweidns 或 namesilo 或 tencentcloud 或 noip 或 callback",
225
233
  "ipv4": ["ddns.newfuture.cc", "ipv4.ddns.newfuture.cc"],
226
234
  "ipv6": ["ddns.newfuture.cc", "ipv6.ddns.newfuture.cc"],
227
235
  "index4": 0,
@@ -285,6 +293,7 @@ Docker 镜像在无额外参数的情况下,已默认启用每 5 分钟执行
285
293
  使用系统自带的 IE 浏览器访问一次对应的 API 即可
286
294
 
287
295
  - alidns 打开: <https://alidns.aliyuncs.com>
296
+ - aliesa 打开: <https://esa.cn-hangzhou.aliyuncs.com>
288
297
  - cloudflare 打开: <https://api.cloudflare.com>
289
298
  - dns.com 打开: <https://www.dns.com>
290
299
  - dnspod.cn 打开: <https://dnsapi.cn>
@@ -0,0 +1,12 @@
1
+ # -*- coding:utf-8 -*-
2
+ """
3
+ ddns Package
4
+ """
5
+
6
+ __description__ = "automatically update DNS records to my IP [域名自动指向本机IP]"
7
+
8
+ # 编译时,版本会被替换
9
+ __version__ = "4.1.0b2"
10
+
11
+ # 时间也会被替换掉
12
+ build_date = "2025-07-15T02:27:02Z"
@@ -0,0 +1,118 @@
1
+ # -*- coding:utf-8 -*-
2
+ """
3
+ DDNS
4
+ @author: NewFuture, rufengsuixing
5
+ """
6
+
7
+ from io import TextIOWrapper
8
+ from subprocess import check_output
9
+ from logging import getLogger
10
+ import sys
11
+
12
+ from .__init__ import __version__, __description__, build_date
13
+ from .config import load_config, Config # noqa: F401
14
+ from .provider import get_provider_class, SimpleProvider
15
+ from . import ip
16
+ from .cache import Cache
17
+
18
+ logger = getLogger()
19
+ # Set user agent for All Providers
20
+ SimpleProvider.user_agent = SimpleProvider.user_agent.format(version=__version__)
21
+
22
+
23
+ def get_ip(ip_type, rules):
24
+ """
25
+ get IP address
26
+ """
27
+ if rules is False: # disabled
28
+ return False
29
+ for i in rules:
30
+ try:
31
+ logger.debug("get_ip:(%s, %s)", ip_type, i)
32
+ if str(i).isdigit(): # 数字 local eth
33
+ return getattr(ip, "local_v" + ip_type)(i)
34
+ elif i.startswith("cmd:"): # cmd
35
+ return str(check_output(i[4:]).strip().decode("utf-8"))
36
+ elif i.startswith("shell:"): # shell
37
+ return str(check_output(i[6:], shell=True).strip().decode("utf-8"))
38
+ elif i.startswith("url:"): # 自定义 url
39
+ return getattr(ip, "public_v" + ip_type)(i[4:])
40
+ elif i.startswith("regex:"): # 正则 regex
41
+ return getattr(ip, "regex_v" + ip_type)(i[6:])
42
+ else:
43
+ return getattr(ip, i + "_v" + ip_type)()
44
+ except Exception as e:
45
+ logger.error("Failed to get %s address: %s", ip_type, e)
46
+ return None
47
+
48
+
49
+ def update_ip(dns, cache, index_rule, domains, record_type, config):
50
+ # type: (SimpleProvider, Cache | None, list[str]|bool, list[str], str, Config) -> bool | None
51
+ """
52
+ 更新IP并变更DNS记录
53
+ """
54
+ if not domains:
55
+ return None
56
+
57
+ ip_type = "4" if record_type == "A" else "6"
58
+ address = get_ip(ip_type, index_rule)
59
+ if not address:
60
+ logger.error("Fail to get %s address!", ip_type)
61
+ return False
62
+
63
+ update_success = False
64
+
65
+ for domain in domains:
66
+ domain = domain.lower()
67
+ cache_key = "{}:{}".format(domain, record_type)
68
+ if cache and cache.get(cache_key) == address:
69
+ logger.info("%s[%s] address not changed, using cache: %s", domain, record_type, address)
70
+ update_success = True
71
+ else:
72
+ try:
73
+ result = dns.set_record(domain, address, record_type=record_type, ttl=config.ttl, line=config.line)
74
+ if result:
75
+ logger.warning("set %s[IPv%s]: %s successfully.", domain, ip_type, address)
76
+ update_success = True
77
+ if isinstance(cache, dict):
78
+ cache[cache_key] = address
79
+ else:
80
+ logger.error("Failed to update %s record for %s", record_type, domain)
81
+ except Exception as e:
82
+ logger.exception("Failed to update %s record for %s: %s", record_type, domain, e)
83
+ return update_success
84
+
85
+
86
+ def run(config):
87
+ # type: (Config) -> bool
88
+ """
89
+ Run the DDNS update process
90
+ """
91
+ # 设置IP模块的SSL验证配置
92
+ ip.ssl_verify = config.ssl
93
+
94
+ # dns provider class
95
+ provider_class = get_provider_class(config.dns)
96
+ dns = provider_class(
97
+ config.id, config.token, endpoint=config.endpoint, logger=logger, proxy=config.proxy, verify_ssl=config.ssl
98
+ )
99
+ cache = Cache.new(config.cache, config.md5(), logger)
100
+ return (
101
+ update_ip(dns, cache, config.index4, config.ipv4, "A", config) is not False
102
+ and update_ip(dns, cache, config.index6, config.ipv6, "AAAA", config) is not False
103
+ )
104
+
105
+
106
+ def main():
107
+ encode = sys.stdout.encoding
108
+ if encode is not None and encode.lower() != "utf-8" and hasattr(sys.stdout, "buffer"):
109
+ # 兼容windows 和部分ASCII编码的老旧系统
110
+ sys.stdout = TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
111
+ sys.stderr = TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
112
+ logger.name = "ddns"
113
+ config = load_config(__description__, __version__, build_date)
114
+ run(config)
115
+
116
+
117
+ if __name__ == "__main__":
118
+ main()
@@ -4,10 +4,11 @@ cache module
4
4
  文件缓存
5
5
  """
6
6
 
7
+ from logging import getLogger, Logger # noqa: F401
7
8
  from os import path, stat
8
- from pickle import dump, load
9
+ from json import load, dump
10
+ from tempfile import gettempdir
9
11
  from time import time
10
- from logging import getLogger, Logger # noqa: F401
11
12
 
12
13
 
13
14
  class Cache(dict):
@@ -40,18 +41,20 @@ class Cache(dict):
40
41
  file = self.__filename
41
42
 
42
43
  self.__logger.debug("load cache data from %s", file)
43
- if file and path.isfile(file):
44
- with open(file, "rb") as data:
45
- try:
44
+ if file:
45
+ try:
46
+ with open(file, "r") as data:
46
47
  loaded_data = load(data)
47
48
  self.clear()
48
49
  self.update(loaded_data)
49
50
  self.__time = stat(file).st_mtime
50
51
  return self
51
- except ValueError:
52
- pass
53
- except Exception as e:
54
- self.__logger.warning(e)
52
+ except (IOError, OSError):
53
+ self.__logger.info("cache file not exist or cannot be opened")
54
+ except ValueError:
55
+ pass
56
+ except Exception as e:
57
+ self.__logger.warning(e)
55
58
  else:
56
59
  self.__logger.info("cache file not exist")
57
60
 
@@ -63,10 +66,10 @@ class Cache(dict):
63
66
  def sync(self):
64
67
  """Sync the write buffer with the cache files and clear the buffer."""
65
68
  if self.__changed and self.__filename:
66
- with open(self.__filename, "wb") as data:
69
+ with open(self.__filename, "w") as data:
67
70
  # 只保存非私有字段(不以__开头的字段)
68
71
  filtered_data = {k: v for k, v in super(Cache, self).items() if not k.startswith("__")}
69
- dump(filtered_data, data)
72
+ dump(filtered_data, data, separators=(",", ":"))
70
73
  self.__logger.debug("save cache data to %s", self.__filename)
71
74
  self.__time = time()
72
75
  self.__changed = False
@@ -150,3 +153,31 @@ class Cache(dict):
150
153
 
151
154
  def __del__(self):
152
155
  self.close()
156
+
157
+ @staticmethod
158
+ def new(config_cache, hash, logger):
159
+ # type: (str|bool, str, Logger) -> Cache|None
160
+ """
161
+ new cache from a file path.
162
+ :param path: Path to the cache file.
163
+ :param logger: Optional logger for debug messages.
164
+ :return: Cache instance with loaded data.
165
+ """
166
+ if config_cache is False:
167
+ cache = None
168
+ elif config_cache is True:
169
+ cache_path = path.join(gettempdir(), "ddns.%s.cache" % hash)
170
+ cache = Cache(cache_path, logger)
171
+ else:
172
+ cache = Cache(config_cache, logger)
173
+
174
+ if cache is None:
175
+ logger.debug("Cache is disabled!")
176
+ elif cache.time + 72 * 3600 < time(): # 72小时有效期
177
+ logger.info("Cache file is outdated.")
178
+ cache.clear()
179
+ elif len(cache) == 0:
180
+ logger.debug("Cache is empty.")
181
+ else:
182
+ logger.debug("Cache loaded with %d entries.", len(cache))
183
+ return cache
@@ -5,10 +5,10 @@ from os import name as os_name, popen
5
5
  from socket import socket, getaddrinfo, gethostname, AF_INET, AF_INET6, SOCK_DGRAM
6
6
  from logging import debug, error
7
7
 
8
- try: # python3
9
- from urllib.request import urlopen, Request
10
- except ImportError: # python2
11
- from urllib2 import urlopen, Request # type: ignore[import-untyped] # noqa: F401
8
+ from .util.http import send_http_request
9
+
10
+ # 模块级别的SSL验证配置,默认使用auto模式
11
+ ssl_verify = "auto"
12
12
 
13
13
  # IPV4正则
14
14
  IPV4_REG = r"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])"
@@ -48,13 +48,15 @@ def local_v4(i=0): # 本地ipv4地址
48
48
  def _open(url, reg):
49
49
  try:
50
50
  debug("open: %s", url)
51
- res = (
52
- urlopen(Request(url, headers={"User-Agent": "Mozilla/5.0 ddns"}), timeout=60)
53
- .read()
54
- .decode("utf8", "ignore")
51
+ response = send_http_request(
52
+ method="GET", url=url, headers={"User-Agent": "Mozilla/5.0 ddns"}, verify_ssl=ssl_verify
55
53
  )
54
+ res = response.body
56
55
  debug("response: %s", res)
57
- return compile(reg).search(res).group()
56
+ match = compile(reg).search(res)
57
+ if match:
58
+ return match.group()
59
+ error("No match found in response: %s", res)
58
60
  except Exception as e:
59
61
  error(e)
60
62
 
@@ -1,15 +1,21 @@
1
1
  # coding=utf-8
2
- from ._base import SimpleProvider # noqa: F401
2
+ from ._base import SimpleProvider
3
3
  from .alidns import AlidnsProvider
4
+ from .aliesa import AliesaProvider
4
5
  from .callback import CallbackProvider
5
6
  from .cloudflare import CloudflareProvider
7
+ from .debug import DebugProvider
6
8
  from .dnscom import DnscomProvider
7
9
  from .dnspod import DnspodProvider
8
10
  from .dnspod_com import DnspodComProvider
11
+ from .edgeone import EdgeOneProvider
9
12
  from .he import HeProvider
10
13
  from .huaweidns import HuaweiDNSProvider
14
+ from .namesilo import NamesiloProvider
15
+ from .noip import NoipProvider
11
16
  from .tencentcloud import TencentCloudProvider
12
- from .debug import DebugProvider
17
+
18
+ __all__ = ["SimpleProvider", "get_provider_class"]
13
19
 
14
20
 
15
21
  def get_provider_class(provider_name):
@@ -32,15 +38,22 @@ def get_provider_class(provider_name):
32
38
  "tencentcloud": TencentCloudProvider,
33
39
  "tencent": TencentCloudProvider, # 兼容tencent
34
40
  "qcloud": TencentCloudProvider, # 兼容qcloud
41
+ # tencent cloud edgeone
42
+ "edgeone": EdgeOneProvider,
43
+ "teo": EdgeOneProvider, # 兼容teo (EdgeOne产品的API名称)
44
+ "tencentedgeone": EdgeOneProvider, # 兼容tencentedgeone
35
45
  # cloudflare
36
46
  "cloudflare": CloudflareProvider,
37
47
  # aliyun alidns
38
48
  "alidns": AlidnsProvider,
39
49
  "aliyun": AlidnsProvider, # 兼容aliyun
50
+ # aliyun esa
51
+ "aliesa": AliesaProvider,
52
+ "esa": AliesaProvider, # 兼容esa
40
53
  # dns.com
41
54
  "dnscom": DnscomProvider,
42
- "51dns": DnscomProvider, # 兼容旧的51dns
43
- "dns_com": DnscomProvider, # 兼容旧的dns_com
55
+ "51dns": DnscomProvider, # 兼容51dns
56
+ "dns_com": DnscomProvider, # 兼容dns_com
44
57
  # he.net
45
58
  "he": HeProvider,
46
59
  "he_net": HeProvider, # 兼容he.net
@@ -48,6 +61,13 @@ def get_provider_class(provider_name):
48
61
  "huaweidns": HuaweiDNSProvider,
49
62
  "huawei": HuaweiDNSProvider, # 兼容huawei
50
63
  "huaweicloud": HuaweiDNSProvider,
64
+ # namesilo
65
+ "namesilo": NamesiloProvider,
66
+ "namesilo_com": NamesiloProvider, # 兼容namesilo.com
67
+ # no-ip
68
+ "noip": NoipProvider,
69
+ "no-ip": NoipProvider, # 兼容no-ip
70
+ "noip_com": NoipProvider, # 兼容noip.com
51
71
  # callback
52
72
  "callback": CallbackProvider,
53
73
  "webhook": CallbackProvider, # 兼容