ddns 3.1.6__py2.py3-none-any.whl → 4.0.0b3__py2.py3-none-any.whl

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.

@@ -0,0 +1,301 @@
1
+ Metadata-Version: 2.4
2
+ Name: ddns
3
+ Version: 4.0.0b3
4
+ Summary: automatically update DNS records to dynamic local IP [自动更新DNS记录指向本地IP]
5
+ Home-page: https://ddns.newfuture.cc
6
+ Author: NewFuture
7
+ Author-email: python@newfuture.cc
8
+ License: MIT
9
+ Project-URL: Bug Reports, https://github.com/NewFuture/DDNS/issues
10
+ Project-URL: Source, https://github.com/NewFuture/DDNS
11
+ Keywords: ddns ipv6 ipv4 dns dnspod alidns cloudflare
12
+ Platform: any
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: Intended Audience :: Information Technology
17
+ Classifier: Intended Audience :: System Administrators
18
+ Classifier: Topic :: Internet
19
+ Classifier: Topic :: Internet :: Name Service (DNS)
20
+ Classifier: Topic :: System :: Networking
21
+ Classifier: Topic :: Software Development
22
+ Classifier: License :: OSI Approved :: MIT License
23
+ Classifier: Programming Language :: Python :: 2.7
24
+ Classifier: Programming Language :: Python :: 3
25
+ Requires-Python: >=2.5, <4
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Dynamic: author
29
+ Dynamic: author-email
30
+ Dynamic: classifier
31
+ Dynamic: description
32
+ Dynamic: description-content-type
33
+ Dynamic: home-page
34
+ Dynamic: keywords
35
+ Dynamic: license
36
+ Dynamic: license-file
37
+ Dynamic: platform
38
+ Dynamic: project-url
39
+ Dynamic: requires-python
40
+ Dynamic: summary
41
+
42
+ # [<img src="doc/img/ddns.svg" width="32px" height="32px"/>](https://ddns.newfuture.cc) [DDNS](https://github.com/NewFuture/DDNS)
43
+
44
+ > 自动更新 DNS 解析 到本机 IP 地址,支持 IPv4 和 IPv6,本地(内网)IP 和公网 IP。
45
+ > 代理模式,支持自动创建 DNS 记录。
46
+
47
+ [![Github Release](https://img.shields.io/github/v/release/NewFuture/DDNS?&logo=github&style=flatten
48
+ )](https://github.com/NewFuture/DDNS/releases/latest)
49
+ [![PyPI](https://img.shields.io/pypi/v/ddns.svg?label=ddns&logo=pypi&style=flatten)](https://pypi.org/project/ddns/)
50
+ [![Docker Image Version](https://img.shields.io/docker/v/newfuture/ddns?label=newfuture/ddns&logo=docker&&sort=semver&style=flatten)](https://hub.docker.com/r/newfuture/ddns)
51
+ [![Build Status](https://github.com/NewFuture/DDNS/actions/workflows/build.yml/badge.svg?event=push)](https://github.com/NewFuture/DDNS/actions/workflows/build.yml)
52
+ [![Publish](https://github.com/NewFuture/DDNS/actions/workflows/publish.yml/badge.svg)](https://github.com/NewFuture/DDNS/actions/workflows/publish.yml)
53
+
54
+ ---
55
+
56
+ ## Features
57
+
58
+ - 兼容和跨平台:
59
+ - [Docker (@NN708)](https://hub.docker.com/r/newfuture/ddns) [![Docker Image Size](https://img.shields.io/docker/image-size/newfuture/ddns/latest?logo=docker&style=social)](https://hub.docker.com/r/newfuture/ddns)[![Docker Platforms](https://img.shields.io/badge/arch-amd64%20%7C%20arm64%20%7C%20arm%2Fv7%20%7C%20arm%2Fv6%20%7C%20ppc64le%20%7C%20s390x%20%7C%20386%20%7C%20mips64le-blue?style=social)](https://hub.docker.com/r/newfuture/ddns)
60
+ - [PIP 安装 (兼容Python2)](https://pypi.org/project/ddns/) ![PyPI - Wheel](https://img.shields.io/pypi/wheel/ddns.svg?logo=pypi&style=social) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/ddns.svg?style=social)
61
+ - [二进制文件](https://github.com/NewFuture/DDNS/releases/latest) ![cross platform](https://img.shields.io/badge/system-windows_%7C%20linux_%7C%20mac-success.svg?style=social)
62
+
63
+ - 域名支持:
64
+ - 多个域名支持
65
+ - 多级域名解析
66
+ - 自动创建新 DNS 记录
67
+ - IP 类型:
68
+ - 内网 IPv4 / IPv6
69
+ - 公网 IPv4 / IPv6 (支持自定义 API)
70
+ - 自定义命令(shell)
71
+ - 正则选取支持 (@rufengsuixing)
72
+ - 网络代理:
73
+ - http 代理支持
74
+ - 多代理自动切换
75
+ - 服务商支持:
76
+ - [DNSPOD](https://www.dnspod.cn/)
77
+ - [阿里 DNS](http://www.alidns.com/)
78
+ - [DNS.COM](https://www.dns.com/) (@loftor-git)
79
+ - [DNSPOD 国际版](https://www.dnspod.com/)
80
+ - [CloudFlare](https://www.cloudflare.com/) (@tongyifan)
81
+ - [HE.net](https://dns.he.net/) (@NN708) (不支持自动创建记录)
82
+ - [华为云](https://huaweicloud.com/) (@cybmp3)
83
+ - 其他:
84
+ - 可设置定时任务
85
+ - TTL 配置支持
86
+ - 本地文件缓存(减少 API 请求)
87
+ - 地址变更时触发自定义回调 API(与 DDNS 功能互斥)
88
+
89
+ ## 使用
90
+
91
+ ### ① 安装
92
+
93
+ 根据需要选择一种方式:`二进制`版、`pip`版、`源码`运行,或者 `Docker`。
94
+
95
+ 推荐 Docker 版,兼容性最佳,体积小,性能优化。
96
+
97
+ - #### pip 安装(需要 pip 或 easy_install)
98
+
99
+ 1. 安装 ddns: `pip install ddns` 或 `easy_install ddns`
100
+ 2. 运行: `ddns`
101
+
102
+ - #### 二进制版(单文件,无需 python)
103
+
104
+ - Windows [ddns.exe](https://github.com/NewFuture/DDNS/releases/latest)
105
+ - Linux(仅 Ubuntu 测试) [ddns](https://github.com/NewFuture/DDNS/releases/latest)
106
+ - Mac OSX [ddns-mac](https://github.com/NewFuture/DDNS/releases/latest)
107
+
108
+ - #### 源码运行(无任何依赖,需 python 环境)
109
+
110
+ 1. clone 或者 [下载此仓库](https://github.com/NewFuture/DDNS/archive/master.zip) 并解压
111
+ 2. 运行 ./run.py(windows 双击 `run.bat` 或者运行 `python run.py`)
112
+
113
+ - #### Docker(需要安装 Docker)
114
+
115
+ - 使用环境变量:
116
+
117
+ ```sh
118
+ docker run -d \
119
+ -e DDNS_DNS=dnspod \
120
+ -e DDNS_ID=12345 \
121
+ -e DDNS_TOKEN=mytokenkey \
122
+ -e DDNS_IPV4=ddns.newfuture.cc \
123
+ -e DDNS_IPV6=ddns.newfuture.cc \
124
+ --network host \
125
+ newfuture/ddns
126
+ ```
127
+
128
+ - 使用配置文件(docker 工作目录 `/ddns/`,默认配置位置 `/ddns/config.json`):
129
+
130
+ ```sh
131
+ docker run -d \
132
+ -v /local/config/path/:/ddns/ \
133
+ --network host \
134
+ newfuture/ddns
135
+ ```
136
+
137
+ ### ② 快速配置
138
+
139
+ 1. 申请 api `token`,填写到对应的 `id` 和 `token` 字段:
140
+
141
+ - [DNSPOD(国内版)创建 token](https://support.dnspod.cn/Kb/showarticle/tsid/227/)
142
+ - [阿里云 accesskey](https://help.aliyun.com/document_detail/87745.htm)
143
+ - [DNS.COM API Key/Secret](https://www.dns.com/member/apiSet)
144
+ - [DNSPOD(国际版)](https://www.dnspod.com/docs/info.html#get-the-user-token)
145
+ - [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 权限)
146
+ - [HE.net DDNS 文档](https://dns.he.net/docs.html)(仅需将设置的密码填入 `token` 字段,`id` 字段可留空)
147
+ - [华为 APIKEY 申请](https://console.huaweicloud.com/iam/)(点左边访问密钥,然后点新增访问密钥)
148
+ - 自定义回调的参数填写方式请查看下方的自定义回调配置说明
149
+
150
+ 2. 修改配置文件,`ipv4` 和 `ipv6` 字段,为待更新的域名,详细参照配置说明
151
+
152
+ ## 详细配置
153
+
154
+ 所有字段可通过三种方式进行配置
155
+
156
+ 1. 命令行参数 `ddns --key=value`(`ddns -h` 查看详情),优先级最高
157
+ 2. JSON 配置文件(值为 null 认为是有效值,会覆盖环境变量的设置,如果没有对应的 key 则会尝试使用环境变量)
158
+ 3. 环境变量 DDNS_ 前缀加上 key 全大写或者全小写,点转下划线(`${ddns_id}` 或 `${DDNS_ID}`,`${DDNS_LOG_LEVEL}`)
159
+
160
+ <details open>
161
+ <summary markdown="span">config.json 配置文件</summary>
162
+
163
+ - 首次运行会自动生成一个模板配置文件
164
+ - 可以使用 `-c` 使用指定的配置文件(默认读取当前目录的 config.json)
165
+ - 推荐使用 vscode 等支持 JsonSchema 的编辑器编辑配置文件
166
+
167
+ ```bash
168
+ ddns -c path/to/config.json
169
+ # 或者源码运行
170
+ python run.py -c /path/to/config.json
171
+ ```
172
+
173
+ #### 配置参数表
174
+
175
+ | key | type | required | default | description | tips |
176
+ | :------: | :----------------: | :------: | :---------: | :---------------: | ----------------------------------------------------------------------------------------------------------- |
177
+ | id | string | √ | 无 | api 访问 ID | Cloudflare 为邮箱(使用 Token 时留空)<br>HE.net 可留空<br>华为云为 Access Key ID (AK) |
178
+ | token | string | √ | 无 | api 授权 token | 部分平台叫 secret key,**反馈粘贴时删除** |
179
+ | dns | string | No | `"dnspod"` | dns 服务商 | 阿里 DNS 为 `alidns`,Cloudflare 为 `cloudflare`,dns.com 为 `dnscom`,DNSPOD 国内为 `dnspod`,DNSPOD 国际为 `dnspod_com`,HE.net 为 `he`,华为云为 `huaweidns`,自定义回调为 `callback` |
180
+ | ipv4 | array | No | `[]` | ipv4 域名列表 | 为 `[]` 时,不会获取和更新 IPv4 地址 |
181
+ | ipv6 | array | No | `[]` | ipv6 域名列表 | 为 `[]` 时,不会获取和更新 IPv6 地址 |
182
+ | index4 | string\|int\|array | No | `"default"` | ipv4 获取方式 | 可设置 `网卡`、`内网`、`公网`、`正则` 等方式 |
183
+ | index6 | string\|int\|array | No | `"default"` | ipv6 获取方式 | 可设置 `网卡`、`内网`、`公网`、`正则` 等方式 |
184
+ | ttl | number | No | `null` | DNS 解析 TTL 时间 | 不设置采用 DNS 默认策略 |
185
+ | proxy | string\|array | No | 无 | http 代理 `;` 分割 | 多代理逐个尝试直到成功,`DIRECT` 为直连 |
186
+ | ~~debug~~| bool | No | `false` | 是否开启调试 | v4 中弃用,请改用 log.level=DEBUG |
187
+ | cache | string\|bool | No | `true` | 是否缓存记录 | 正常情况打开避免频繁更新,默认位置为临时目录下 `ddns.cache`,也可以指定一个具体路径 |
188
+ | log | {"level":string,"file":string} | No | `null` | 日志配置(可选) | 日志配置,日志级别和路径(默认命令行),例如:`{ "level": "DEBUG", "file": "dns.log" }` |
189
+
190
+ #### index4 和 index6 参数说明
191
+
192
+ - 数字(`0`,`1`,`2`,`3`等):第 i 个网卡 ip
193
+ - 字符串 `"default"`(或者无此项):系统访问外网默认 IP
194
+ - 字符串 `"public"`:使用公网 ip(使用公网 API 查询,url 的简化模式)
195
+ - 字符串 `"url:xxx"`:打开 URL `xxx`(如:`"url:http://ip.sb"`),从返回的数据提取 IP 地址
196
+ - 字符串 `"regex:xxx"` 正则表达(如 `"regex:192.*"`):提取 `ifconfig`/`ipconfig` 中与之匹配的首个 IP 地址,**注意 json 转义**(`\`要写成`\\`)
197
+ - `"192.*"` 表示 192 开头的所有 ip(注意 `regex:` 不可省略)
198
+ - 如果想匹配 `10.00.xxxx` 应该写成 `"regex:10\\.00\\..*"`(`"\\"` json 转义成 `\`)
199
+ - 字符串 `"cmd:xxxx"`:执行命令 `xxxx` 的 stdout 输出结果作为目标 IP
200
+ - 字符串 `"shell:xxx"`:使用系统 shell 运行 `xxx`,并把结果 stdout 作为目标 IP
201
+ - `false`:强制禁止更新 ipv4 或 ipv6 的 DNS 解析
202
+ - 列表:依次执行列表中的 index 规则,并将最先获得的结果作为目标 IP
203
+ - 例如 `["public", "regex:172\\..*"]` 将先查询公网 API,未获取到 IP 后再从本地寻找 172 开头的 IP
204
+
205
+ #### 自定义回调配置说明
206
+
207
+ - `id` 字段填写回调地址,以 HTTP 或 HTTPS 开头,推荐采用 HTTPS 方式的回调 API ,当 `token` 字段非空且 URL 参数包含下表所示的常量字符串时,会自动替换为实际内容。
208
+ - `token` 字段为 POST 参数,本字段为空或不存在则使用 GET 方式发起回调,回调参数采用 JSON 格式编码,当 JSON 的首层参数值包含下表所示的常量字符串时,会自动替换为实际内容。
209
+
210
+ | 常量名称 | 常量内容 | 说明 |
211
+ | ---------------- | ---------------------- | -------- |
212
+ | `__DOMAIN__` | DDNS 域名 | |
213
+ | `__RECORDTYPE__` | DDNS 记录类型 | |
214
+ | `__TTL__` | DDNS TTL | |
215
+ | `__TIMESTAMP__` | 请求发起时间戳 | 包含小数 |
216
+ | `__IP__` | 获取的对应类型的 IP 地址 | |
217
+
218
+ #### 配置示例
219
+
220
+ ```json
221
+ {
222
+ "$schema": "https://ddns.newfuture.cc/schema/v4.0.json",
223
+ "id": "12345",
224
+ "token": "mytokenkey",
225
+ "dns": "dnspod 或 dnspod_com 或 alidns 或 dnscom 或 cloudflare 或 he 或 huaweidns 或 callback",
226
+ "ipv4": ["ddns.newfuture.cc", "ipv4.ddns.newfuture.cc"],
227
+ "ipv6": ["ddns.newfuture.cc", "ipv6.ddns.newfuture.cc"],
228
+ "index4": 0,
229
+ "index6": "public",
230
+ "ttl": 600,
231
+ "proxy": "127.0.0.1:1080;DIRECT",
232
+ "log": {
233
+ "level": "DEBUG",
234
+ "file": "dns.log"
235
+ }
236
+ }
237
+ ```
238
+ </details>
239
+
240
+ ## 定时任务
241
+
242
+ <details>
243
+ <summary markdown="span">可以通过脚本设置定时任务(默认每 5 分钟检查一次 IP,自动更新)</summary>
244
+
245
+ #### Windows
246
+
247
+ - [推荐] 以系统身份运行,右键“以管理员身份运行”`task.bat`(或者在管理员命令行中运行)
248
+ - 以当前用户身份运行定时任务,双击或运行 `task.bat`(执行时会闪黑框)
249
+
250
+ #### Linux
251
+
252
+ - 使用 init.d 和 crontab:
253
+ ```bash
254
+ sudo ./task.sh
255
+ ```
256
+ - 使用 systemd:
257
+ ```bash
258
+ 安装:
259
+ sudo ./systemd.sh install
260
+ 卸载:
261
+ sudo ./systemd.sh uninstall
262
+ ```
263
+
264
+ 该脚本安装的文件符合 [Filesystem Hierarchy Standard (FHS)](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard):
265
+ 可执行文件所在目录为 `/usr/share/DDNS`
266
+ 配置文件所在目录为 `/etc/DDNS`
267
+
268
+ #### Docker
269
+
270
+ Docker 镜像在无额外参数的情况下,已默认启用每 5 分钟执行一次的定时任务
271
+
272
+ </details>
273
+
274
+ ## FAQ
275
+
276
+ <details>
277
+ <summary markdown="span">Windows Server [SSL: CERTIFICATE_VERIFY_FAILED]</summary>
278
+
279
+ > Windows Server 默认安全策略会禁止任何未添加的信任 SSL 证书,可手动添加一下对应的证书 [#56](https://github.com/NewFuture/DDNS/issues/56#issuecomment-487371078)
280
+
281
+ 使用系统自带的 IE 浏览器访问一次对应的 API 即可
282
+
283
+ - alidns 打开: <https://alidns.aliyuncs.com>
284
+ - cloudflare 打开: <https://api.cloudflare.com>
285
+ - dns.com 打开: <https://www.dns.com>
286
+ - dnspod.cn 打开: <https://dnsapi.cn>
287
+ - dnspod 国际版: <https://api.dnspod.com>
288
+ - 华为 DNS <https://dns.myhuaweicloud.com>
289
+ </details>
290
+
291
+ <details>
292
+ <summary markdown="span">问题排查反馈</summary>
293
+
294
+ 1. 先确认排查是否是系统/网络环境问题
295
+ 2. 在 [issues](https://github.com/NewFuture/DDNS/issues) 中搜索是否有类似问题
296
+ 3. 前两者均无法解决或者确定是 bug,[在此新建 issue](https://github.com/NewFuture/DDNS/issues/new)
297
+ - [ ] 开启 debug 配置
298
+ - [ ] 附上这些内容 **运行版本和方式**、**系统环境**、**出错日志**、**去掉 id/token** 的配置文件
299
+ - [ ] 源码运行注明使用的 python 环境
300
+
301
+ </details>
@@ -0,0 +1,20 @@
1
+ run.py,sha256=VvDzoIUwBOKCW0kD-8b1eyndPO_cbwV2A8J3GUIb-bo,5976
2
+ ddns-4.0.0b3.dist-info/licenses/LICENSE,sha256=MI-ECjp-Vl7WZLiSPY6r5VwrOReNiICVB1QCXiUGt_s,1111
3
+ dns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ dns/alidns.py,sha256=fD8mzr7FJ5QxOWLA3ekoOhjeU1IFepOpxybrgZv2c9k,5782
5
+ dns/callback.py,sha256=V8AG7kxtS0jIkyD9F7xnGJpu1L3E5KUcNDwOnQjcJKs,3208
6
+ dns/cloudflare.py,sha256=OBq4TJwVHHiPMM6FJo-bz20FOHPvA-RSFbuGilw3tVw,5647
7
+ dns/dnscom.py,sha256=Nhyl70JGnZY2IV0j5Ebgy_gYQGFb8hXTshaRG_rC0Ww,5297
8
+ dns/dnspod.py,sha256=giivum1lyxfVtu8d-_muXhMxBcB-DZtpq_IGo7ApdFk,6143
9
+ dns/dnspod_com.py,sha256=9DW7Oz1f2fYSLDDGdwQKMp9OhRJ_CYB8bfzGhf0fIMI,339
10
+ dns/he.py,sha256=X_WODK6vDTpHdAvPP_vUltaeAlHeG2jMPbzFkfYxWOg,2142
11
+ dns/huaweidns.py,sha256=0A2jxcJ5zGaMxMMh7C9SsDewhP2zVRLI8FznB9BBBEc,8748
12
+ util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ util/cache.py,sha256=nt_x_BUORwUAZZW13UNeEBOFrIksWWhdj9k4AGyvnU8,3337
14
+ util/config.py,sha256=0yJ5Yfz6u__wrXm_SMqw6a9OuMf9usHOxXfRlB7C3mo,6836
15
+ util/ip.py,sha256=GZzqrl_nq9JuGHTWhPB0fffpr94ITJA6BCVlS5KlWcE,3836
16
+ ddns-4.0.0b3.dist-info/METADATA,sha256=P87QP5cH45_TvXiJxhOcyMZ0EiZqnxxc_3VogX3RLZE,15269
17
+ ddns-4.0.0b3.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
18
+ ddns-4.0.0b3.dist-info/entry_points.txt,sha256=vpxxBE6Kepc-SHatr-FRRLmZecrTupYejyDB8ovJbLg,34
19
+ ddns-4.0.0b3.dist-info/top_level.txt,sha256=dS3_ClQTIG1ousHykZ1P3AzQa-MKq4jT_rbm21D-KxE,13
20
+ ddns-4.0.0b3.dist-info/RECORD,,
dns/alidns.py CHANGED
@@ -14,14 +14,12 @@ from json import loads as jsondecode
14
14
  from logging import debug, info, warning
15
15
  from datetime import datetime
16
16
 
17
- try:
18
- # python 2
19
- from httplib import HTTPSConnection
20
- from urllib import urlencode, quote_plus, quote
21
- except ImportError:
22
- # python 3
17
+ try: # python 3
23
18
  from http.client import HTTPSConnection
24
19
  from urllib.parse import urlencode, quote_plus, quote
20
+ except ImportError: # python 2
21
+ from httplib import HTTPSConnection
22
+ from urllib import urlencode, quote_plus, quote
25
23
 
26
24
  __author__ = 'New Future'
27
25
  # __all__ = ["request", "ID", "TOKEN", "PROXY"]
dns/callback.py CHANGED
@@ -10,15 +10,13 @@ from json import loads as jsondecode
10
10
  from logging import debug, info, warning
11
11
  from time import time
12
12
 
13
- try:
14
- # python 2
13
+ try: # python 3
14
+ from http.client import HTTPSConnection, HTTPConnection
15
+ from urllib.parse import urlencode, urlparse, parse_qsl
16
+ except ImportError: # python 2
15
17
  from httplib import HTTPSConnection, HTTPConnection
16
18
  from urlparse import urlparse, parse_qsl
17
19
  from urllib import urlencode
18
- except ImportError:
19
- # python 3
20
- from http.client import HTTPSConnection, HTTPConnection
21
- from urllib.parse import urlencode, urlparse, parse_qsl
22
20
 
23
21
  __author__ = '老周部落'
24
22
 
dns/cloudflare.py CHANGED
@@ -9,14 +9,12 @@ https://api.cloudflare.com/#dns-records-for-a-zone-properties
9
9
  from json import loads as jsondecode, dumps as jsonencode
10
10
  from logging import debug, info, warning
11
11
 
12
- try:
13
- # python 2
14
- from httplib import HTTPSConnection
15
- from urllib import urlencode
16
- except ImportError:
17
- # python 3
12
+ try: # python 3
18
13
  from http.client import HTTPSConnection
19
14
  from urllib.parse import urlencode
15
+ except ImportError: # python 2
16
+ from httplib import HTTPSConnection
17
+ from urllib import urlencode
20
18
 
21
19
  __author__ = 'TongYifan'
22
20
 
dns/dnscom.py CHANGED
@@ -13,15 +13,12 @@ from logging import debug, info, warning
13
13
  from time import mktime
14
14
  from datetime import datetime
15
15
 
16
- try:
17
- # python 2
18
- from httplib import HTTPSConnection
19
- from urllib import urlencode
20
- except ImportError:
21
- # python 3
16
+ try: # python 3
22
17
  from http.client import HTTPSConnection
23
18
  from urllib.parse import urlencode
24
-
19
+ except ImportError: # python 2
20
+ from httplib import HTTPSConnection
21
+ from urllib import urlencode
25
22
 
26
23
  __author__ = 'Bigjin'
27
24
  # __all__ = ["request", "ID", "TOKEN", "PROXY"]
dns/dnspod.py CHANGED
@@ -9,14 +9,13 @@ http://www.dnspod.cn/docs/domains.html
9
9
  from json import loads as jsondecode
10
10
  from logging import debug, info, warning
11
11
  from os import environ
12
- try:
13
- # python 2
14
- from httplib import HTTPSConnection
15
- from urllib import urlencode
16
- except ImportError:
17
- # python 3
12
+
13
+ try: # python 3
18
14
  from http.client import HTTPSConnection
19
15
  from urllib.parse import urlencode
16
+ except ImportError: # python 2
17
+ from httplib import HTTPSConnection
18
+ from urllib import urlencode
20
19
 
21
20
  __author__ = 'New Future'
22
21
 
dns/he.py CHANGED
@@ -8,14 +8,12 @@ https://dns.he.net/docs.html
8
8
 
9
9
  from logging import debug, info, warning
10
10
 
11
- try:
12
- # python 2
13
- from httplib import HTTPSConnection
14
- from urllib import urlencode
15
- except ImportError:
16
- # python 3
11
+ try: # python 3
17
12
  from http.client import HTTPSConnection
18
13
  from urllib.parse import urlencode
14
+ except ImportError: # python 2
15
+ from httplib import HTTPSConnection
16
+ from urllib import urlencode
19
17
 
20
18
  __author__ = 'NN708'
21
19
 
dns/huaweidns.py CHANGED
@@ -14,14 +14,13 @@ from json import loads as jsondecode, dumps as jsonencode
14
14
  from logging import debug, info, warning
15
15
  from datetime import datetime
16
16
 
17
- try:
18
- # python 2
19
- from httplib import HTTPSConnection
20
- from urllib import urlencode
21
- except ImportError:
22
- # python 3
17
+ try: # python 3
23
18
  from http.client import HTTPSConnection
24
19
  from urllib.parse import urlencode
20
+ except ImportError: # python 2
21
+ from httplib import HTTPSConnection
22
+ from urllib import urlencode
23
+
25
24
 
26
25
  __author__ = 'New Future'
27
26
  BasicDateFormat = "%Y%m%dT%H%M%SZ"
run.py CHANGED
@@ -5,11 +5,12 @@ DDNS
5
5
  @author: New Future
6
6
  @modified: rufengsuixing
7
7
  """
8
- from __future__ import print_function
9
- from time import ctime, asctime
8
+
9
+ # nuitka-project: --product-version=0.0.0
10
+
10
11
  from os import path, environ, name as os_name
11
12
  from tempfile import gettempdir
12
- from logging import DEBUG, basicConfig, info, warning, error, debug
13
+ from logging import basicConfig, info, warning, error, debug
13
14
  from subprocess import check_output
14
15
 
15
16
  import sys
@@ -18,7 +19,7 @@ from util import ip
18
19
  from util.cache import Cache
19
20
  from util.config import init_config, get_config
20
21
 
21
- __version__ = "v3.1.6@2025-05-29T16:18:28+00:00" # CI 时会被Tag替换
22
+ __version__ = "v4.0.0-beta3@2025-06-04T17:36:22+00:00" # CI 时会被Tag替换
22
23
  __description__ = "automatically update DNS records to dynamic local IP [自动更新DNS记录指向本地IP]"
23
24
  __doc__ = """
24
25
  ddns[%s]
@@ -27,7 +28,7 @@ ddns[%s]
27
28
  Copyright (c) New Future (MIT License)
28
29
  """ % (__version__)
29
30
 
30
- environ["DDNS_VERSION"] = "v3.1.6"
31
+ environ["DDNS_VERSION"] = "v4.0.0-beta3"
31
32
 
32
33
  if getattr(sys, 'frozen', False):
33
34
  # https://github.com/pyinstaller/pyinstaller/wiki/Recipe-OpenSSL-Certificate
@@ -46,7 +47,7 @@ def get_ip(ip_type, index="default"):
46
47
  debug("get_ip(%s, %s)", ip_type, index)
47
48
  if index is False: # disabled
48
49
  return False
49
- elif type(index) is list: # 如果获取到的规则是列表,则依次判断列表中每一个规则,直到找到一个可以正确获取到的IP
50
+ elif isinstance(index, list): # 如果获取到的规则是列表,则依次判断列表中每一个规则,直到获取到IP
50
51
  for i in index:
51
52
  value = get_ip(ip_type, i)
52
53
  if value:
@@ -65,9 +66,8 @@ def get_ip(ip_type, index="default"):
65
66
  else:
66
67
  value = getattr(ip, index + "_v" + ip_type)()
67
68
  except Exception as e:
68
- error(e)
69
- finally:
70
- return value
69
+ error("Failed to get %s address: %s", ip_type, e)
70
+ return value
71
71
 
72
72
 
73
73
  def change_dns_record(dns, proxy_list, **kw):
@@ -77,12 +77,11 @@ def change_dns_record(dns, proxy_list, **kw):
77
77
  else:
78
78
  dns.Config.PROXY = proxy
79
79
  record_type, domain = kw['record_type'], kw['domain']
80
- print('\n%s %s(%s) ==> %s [via %s]' %
81
- (asctime(), domain, record_type, kw['ip'], proxy))
80
+ info("%s(%s) ==> %s [via %s]", domain, record_type, kw['ip'], proxy)
82
81
  try:
83
82
  return dns.update_record(domain, kw['ip'], record_type=record_type)
84
83
  except Exception as e:
85
- error(e)
84
+ error("Failed to update %s record for %s: %s", record_type, domain, e)
86
85
  return False
87
86
 
88
87
 
@@ -103,7 +102,7 @@ def update_ip(ip_type, cache, dns, proxy_list):
103
102
  error('Fail to get %s address!', ipname)
104
103
  return False
105
104
  elif cache and (address == cache[ipname]):
106
- print('.', end=" ") # 缓存命中
105
+ info('%s address not changed, using cache.', ipname)
107
106
  return True
108
107
  record_type = (ip_type == '4') and 'A' or 'AAAA'
109
108
  update_fail = False # https://github.com/NewFuture/DDNS/issues/16
@@ -127,20 +126,21 @@ def main():
127
126
  dns.Config.ID = get_config('id')
128
127
  dns.Config.TOKEN = get_config('token')
129
128
  dns.Config.TTL = get_config('ttl')
130
- if get_config('debug'):
131
- ip.DEBUG = get_config('debug')
132
- basicConfig(
133
- level=DEBUG,
134
- format='%(asctime)s <%(module)s.%(funcName)s> %(lineno)d@%(pathname)s \n[%(levelname)s] %(message)s')
135
- print("DDNS[", __version__, "] run:", os_name, sys.platform)
136
- if get_config("config"):
137
- print("Configuration was loaded from <==",
138
- path.abspath(get_config("config")))
139
- print("=" * 25, ctime(), "=" * 25, sep=' ')
129
+
130
+ basicConfig(
131
+ level=get_config('log.level'),
132
+ format='%(asctime)s [%(levelname)s] %(message)s',
133
+ datefmt='%m-%d %H:%M:%S',
134
+ filename=get_config('log.file'),
135
+ )
136
+
137
+ info("DDNS[ %s ] run: %s %s", __version__, os_name, sys.platform)
138
+ if get_config("config"):
139
+ info('loaded Config from: %s', path.abspath(get_config('config')))
140
140
 
141
141
  proxy = get_config('proxy') or 'DIRECT'
142
142
  proxy_list = proxy if isinstance(
143
- proxy, list) else proxy.strip('; ').replace(',', ';').split(';')
143
+ proxy, list) else proxy.strip(';').replace(',', ';').split(';')
144
144
 
145
145
  cache_config = get_config('cache', True)
146
146
  if cache_config is False:
@@ -151,12 +151,12 @@ def main():
151
151
  cache = Cache(cache_config)
152
152
 
153
153
  if cache is False:
154
- info("Cache is disabled!")
155
- elif get_config("config_modified_time") is None or get_config("config_modified_time") >= cache.time:
156
- warning("Cache file is out of dated.")
154
+ info('Cache is disabled!')
155
+ elif not get_config('config_modified_time') or get_config('config_modified_time') >= cache.time:
156
+ warning('Cache file is out of dated.')
157
157
  cache.clear()
158
158
  else:
159
- debug("Cache is empty.")
159
+ debug('Cache is empty.')
160
160
  update_ip('4', cache, dns, proxy_list)
161
161
  update_ip('6', cache, dns, proxy_list)
162
162
 
util/cache.py CHANGED
@@ -12,11 +12,10 @@ from time import time
12
12
  from logging import info, debug, warning
13
13
 
14
14
  try:
15
- # Python 2
16
- from collections import MutableMapping
17
- except ImportError:
18
- # python3
19
15
  from collections.abc import MutableMapping
16
+ except ImportError:
17
+ # Python 2 imports
18
+ from collections import MutableMapping
20
19
 
21
20
 
22
21
  class Cache(MutableMapping):
util/config.py CHANGED
@@ -1,16 +1,17 @@
1
1
  #!/usr/bin/env python
2
2
  # -*- coding:utf-8 -*-
3
- from argparse import ArgumentParser, ArgumentTypeError, Namespace, RawTextHelpFormatter # noqa: F401
3
+ from argparse import Action, ArgumentParser, Namespace, RawTextHelpFormatter
4
4
  from json import load as loadjson, dump as dumpjson
5
- from logging import error
6
- from os import stat, environ
7
- from time import time
5
+ from os import stat, environ, path
6
+ from logging import error, getLevelName
8
7
 
9
8
  import sys
10
9
 
11
10
 
12
- __cli_args = {} # type: Namespace
11
+ __cli_args = Namespace()
13
12
  __config = {} # type: dict
13
+ log_levels = ['CRITICAL', 'FATAL', 'ERROR',
14
+ 'WARN', 'WARNING', 'INFO', 'DEBUG', 'NOTSET']
14
15
 
15
16
 
16
17
  def str2bool(v):
@@ -24,7 +25,14 @@ def str2bool(v):
24
25
  elif v.lower() in ('no', 'false', 'f', 'n', '0'):
25
26
  return False
26
27
  else:
27
- raise ArgumentTypeError('Boolean value expected.')
28
+ return v
29
+
30
+
31
+ def log_level(value):
32
+ """
33
+ parse string to log level
34
+ """
35
+ return getLevelName(value.upper())
28
36
 
29
37
 
30
38
  def init_config(description, doc, version):
@@ -36,75 +44,77 @@ def init_config(description, doc, version):
36
44
  epilog=doc, formatter_class=RawTextHelpFormatter)
37
45
  parser.add_argument('-v', '--version',
38
46
  action='version', version=version)
39
- parser.add_argument('-c', '--config', help="run with config file [配置文件路径]")
47
+ parser.add_argument('-c', '--config', help='run with config file [配置文件路径]')
40
48
 
41
49
  # 参数定义
42
- parser.add_argument('--dns', help="DNS Provider [DNS服务提供商]", choices=[
50
+ parser.add_argument('--dns', help='DNS Provider [DNS服务提供商]', choices=[
43
51
  'alidns', 'cloudflare', 'dnscom', 'dnspod', 'dnspod_com', 'he', 'huaweidns', 'callback'])
44
- parser.add_argument('--id', help="api ID [授权账户]")
45
- parser.add_argument('--token', help="api token or Secret key [授权访问凭证或密钥]")
46
- parser.add_argument('--ipv4', nargs="*",
47
- help="ipv4 domain list [IPV4域名列表]")
48
- parser.add_argument('--ipv6', nargs="*",
49
- help="ipv6 domain list [IPV6域名列表]")
50
- parser.add_argument('--index4', help="the way to get ipv4 [IPV4 获取方式]")
51
- parser.add_argument('--index6', help="the way to get ipv6 [IPV6获取方式]")
52
- parser.add_argument('--ttl', type=int, help="ttl for DNS [DNS 解析 TTL 时间]")
53
- parser.add_argument('--proxy', nargs="*",
54
- help="https proxy [设置http 代理,多代理逐个尝试直到成功]")
55
- parser.add_argument('--debug', type=str2bool, nargs='?',
56
- const=True, help="debug mode [是否开启调试,默认否]", )
52
+ parser.add_argument('--id', help='api ID [授权账户]')
53
+ parser.add_argument('--token', help='api token or Secret key [授权访问凭证或密钥]')
54
+ parser.add_argument('--index4', nargs='*', action=ExtendAction,
55
+ help='list to get ipv4 [IPV4 获取方式]')
56
+ parser.add_argument('--index6', nargs='*', action=ExtendAction,
57
+ help='list to get ipv6 [IPV6获取方式]')
58
+ parser.add_argument('--ipv4', nargs='*', action=ExtendAction,
59
+ help='ipv4 domain list [IPV4域名列表]')
60
+ parser.add_argument('--ipv6', nargs='*', action=ExtendAction,
61
+ help='ipv6 domain list [IPV6域名列表]')
62
+ parser.add_argument('--ttl', type=int, help='ttl for DNS [DNS 解析 TTL 时间]')
63
+ parser.add_argument('--proxy', nargs='*', action=ExtendAction,
64
+ help='https proxy [设置http 代理,多代理逐个尝试直到成功]')
57
65
  parser.add_argument('--cache', type=str2bool, nargs='?',
58
- const=True, help="enable cache [是否缓存记录,默认是]")
66
+ const=True, help='cache flag [启用缓存,可配配置路径或开关]')
67
+ parser.add_argument('--log.file', metavar='LOG_FILE',
68
+ help='log file [日志文件,默认标准输出]')
69
+ parser.add_argument('--log.level', type=log_level,
70
+ metavar='|'.join(log_levels))
59
71
 
60
72
  __cli_args = parser.parse_args()
61
- is_configfile_optional = get_config("token") or get_config("id")
73
+ is_configfile_required = not get_config("token") and not get_config("id")
62
74
  config_file = get_config("config")
63
- if not is_configfile_optional or config_file is not None:
64
- __load_config(config_file or "config.json", is_configfile_optional)
65
- __cli_args.config = config_file or "config.json"
75
+ if not config_file:
76
+ # 未指定配置文件且需要读取文件时,依次查找
77
+ cfgs = [
78
+ path.abspath('config.json'),
79
+ path.expanduser('~/.ddns/config.json'),
80
+ '/etc/ddns/config.json'
81
+ ]
82
+ config_file = next((cfg for cfg in cfgs if path.isfile(cfg)), cfgs[0])
83
+
84
+ if path.isfile(config_file):
85
+ __load_config(config_file)
86
+ __cli_args.config = config_file
87
+ elif is_configfile_required:
88
+ error('Config file is required, but not found: %s', config_file)
89
+ # 如果需要配置文件但没有指定,则自动生成
90
+ if generate_config(config_file):
91
+ sys.stdout.write(
92
+ 'Default configure file %s is generated.\n' % config_file)
93
+ sys.exit(1)
94
+ else:
95
+ sys.exit('fail to load config from file: %s\n' % config_file)
66
96
 
67
97
 
68
- def __load_config(path="config.json", skip_auto_generation=False):
98
+ def __load_config(config_path):
69
99
  """
70
100
  加载配置
71
101
  """
72
102
  global __config
73
103
  try:
74
- with open(path) as configfile:
104
+ with open(config_path, 'r') as configfile:
75
105
  __config = loadjson(configfile)
76
- __config["config_modified_time"] = stat(path).st_mtime
77
- except IOError:
78
- if skip_auto_generation:
79
- __config["config_modified_time"] = time()
80
- return
81
- error(' Config file `%s` does not exist!' % path)
82
- with open(path, 'w') as configfile:
83
- configure = {
84
- "$schema": "https://ddns.newfuture.cc/schema/v2.8.json",
85
- "id": "YOUR ID or EMAIL for DNS Provider",
86
- "token": "YOUR TOKEN or KEY for DNS Provider",
87
- "dns": "dnspod",
88
- "ipv4": [
89
- "newfuture.cc",
90
- "ddns.newfuture.cc"
91
- ],
92
- "ipv6": [
93
- "newfuture.cc",
94
- "ipv6.ddns.newfuture.cc"
95
- ],
96
- "index4": "default",
97
- "index6": "default",
98
- "ttl": None,
99
- "proxy": None,
100
- "debug": False,
101
- }
102
- dumpjson(configure, configfile, indent=2, sort_keys=True)
103
- sys.stdout.write(
104
- "New template configure file `%s` is generated.\n" % path)
105
- sys.exit(1)
106
- except Exception:
107
- sys.exit('fail to load config from file: %s' % path)
106
+ __config["config_modified_time"] = stat(config_path).st_mtime
107
+ if 'log' in __config:
108
+ if 'level' in __config['log'] and __config['log']['level'] is not None:
109
+ __config['log.level'] = log_level(__config['log']['level'])
110
+ if 'file' in __config['log']:
111
+ __config['log.file'] = __config['log']['file']
112
+ elif 'log.level' in __config:
113
+ __config['log.level'] = log_level(__config['log.level'])
114
+ except Exception as e:
115
+ error('Failed to load config file `%s`: %s', config_path, e)
116
+ raise
117
+ # 重新抛出异常
108
118
 
109
119
 
110
120
  def get_config(key, default=None):
@@ -118,9 +128,66 @@ def get_config(key, default=None):
118
128
  return getattr(__cli_args, key)
119
129
  if key in __config:
120
130
  return __config.get(key)
121
- env_name = 'DDNS_'+key.upper() # type:str
122
- if env_name in environ: # 大写环境变量
131
+ env_name = 'DDNS_' + key.replace('.', '_') # type:str
132
+ if env_name in environ: # 环境变量
123
133
  return environ.get(env_name)
124
- if env_name.lower() in environ: # 小写环境变量
134
+ elif env_name.upper() in environ: # 大写环境变量
135
+ return environ.get(env_name.upper())
136
+ elif env_name.lower() in environ: # 小写环境变量
125
137
  return environ.get(env_name.lower())
126
138
  return default
139
+
140
+
141
+ class ExtendAction(Action):
142
+ """
143
+ 兼容 Python <3.8 的 extend action
144
+ """
145
+
146
+ def __call__(self, parser, namespace, values, option_string=None):
147
+ items = getattr(namespace, self.dest, None)
148
+ if items is None:
149
+ items = []
150
+ # values 可能是单个值或列表
151
+ if isinstance(values, list):
152
+ items.extend(values)
153
+ else:
154
+ items.append(values)
155
+ setattr(namespace, self.dest, items)
156
+
157
+
158
+ def generate_config(config_path):
159
+ """
160
+ 生成配置文件
161
+ """
162
+ configure = {
163
+ '$schema': 'https://ddns.newfuture.cc/schema/v4.0.json',
164
+ 'id': 'YOUR ID or EMAIL for DNS Provider',
165
+ 'token': 'YOUR TOKEN or KEY for DNS Provider',
166
+ 'dns': 'dnspod',
167
+ 'ipv4': [
168
+ 'newfuture.cc',
169
+ 'ddns.newfuture.cc'
170
+ ],
171
+ 'ipv6': [
172
+ 'newfuture.cc',
173
+ 'ipv6.ddns.newfuture.cc'
174
+ ],
175
+ 'index4': 'default',
176
+ 'index6': 'default',
177
+ 'ttl': None,
178
+ 'proxy': None,
179
+ 'log': {
180
+ 'level': 'INFO',
181
+ 'file': None
182
+ }
183
+ }
184
+ try:
185
+ with open(config_path, 'w') as f:
186
+ dumpjson(configure, f, indent=2, sort_keys=True)
187
+ return True
188
+ except IOError:
189
+ error('Cannot open config file to write: `%s`!', config_path)
190
+ return False
191
+ except Exception as e:
192
+ error('Failed to write config file `%s`: %s', config_path, e)
193
+ return False
util/ip.py CHANGED
@@ -4,12 +4,10 @@ from re import compile
4
4
  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
- try:
8
- # python2
9
- from urllib2 import urlopen, Request
10
- except ImportError:
11
- # python3
7
+ try: # python3
12
8
  from urllib.request import urlopen, Request
9
+ except ImportError: # python2
10
+ from urllib2 import urlopen, Request
13
11
 
14
12
  # IPV4正则
15
13
  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])'
@@ -1,288 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: ddns
3
- Version: 3.1.6
4
- Summary: automatically update DNS records to dynamic local IP [自动更新DNS记录指向本地IP]
5
- Home-page: https://ddns.newfuture.cc
6
- Author: NewFuture
7
- Author-email: python@newfuture.cc
8
- License: MIT
9
- Project-URL: Bug Reports, https://github.com/NewFuture/DDNS/issues
10
- Project-URL: Source, https://github.com/NewFuture/DDNS
11
- Keywords: ddns ipv6 ipv4 dns dnspod alidns cloudflare
12
- Platform: any
13
- Classifier: Development Status :: 5 - Production/Stable
14
- Classifier: Intended Audience :: Developers
15
- Classifier: Intended Audience :: End Users/Desktop
16
- Classifier: Intended Audience :: Information Technology
17
- Classifier: Intended Audience :: System Administrators
18
- Classifier: Topic :: Internet
19
- Classifier: Topic :: Internet :: Name Service (DNS)
20
- Classifier: Topic :: System :: Networking
21
- Classifier: Topic :: Software Development
22
- Classifier: License :: OSI Approved :: MIT License
23
- Classifier: Programming Language :: Python :: 2.7
24
- Classifier: Programming Language :: Python :: 3
25
- Requires-Python: >=2.5, <4
26
- Description-Content-Type: text/markdown
27
- License-File: LICENSE
28
- Dynamic: author
29
- Dynamic: author-email
30
- Dynamic: classifier
31
- Dynamic: description
32
- Dynamic: description-content-type
33
- Dynamic: home-page
34
- Dynamic: keywords
35
- Dynamic: license
36
- Dynamic: license-file
37
- Dynamic: platform
38
- Dynamic: project-url
39
- Dynamic: requires-python
40
- Dynamic: summary
41
-
42
- # [DDNS](https://github.com/NewFuture/DDNS)
43
-
44
- > 自动更新 DNS 解析 到本机 IP 地址,支持 ipv4 和 ipv6 以 本地(内网)IP 和 公网 IP。
45
- > 代理模式,支持自动创建域名记录。
46
-
47
- [![PyPI](https://img.shields.io/pypi/v/ddns.svg?label=DDNS&style=social)](https://pypi.org/project/ddns/)
48
- [![Build Status](https://github.com/NewFuture/DDNS/actions/workflows/build.yml/badge.svg?event=push)](https://github.com/NewFuture/DDNS/actions/workflows/build.yml)
49
- [![Publish Status](https://github.com/NewFuture/DDNS/actions/workflows/publish.yml/badge.svg)](https://github.com/NewFuture/DDNS/releases/latest)
50
-
51
- ---
52
-
53
- ## Features
54
-
55
- - 兼容和跨平台:
56
- - [x] 可执行文件(无需 python 环境)
57
- - [x] 多系统兼容 ![cross platform](https://img.shields.io/badge/platform-windows_%7C%20linux_%7C%20osx-success.svg?style=social)
58
- - [x] python3 支持 ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/ddns.svg?style=social)(2.x支持python2和python3)
59
- - [x] PIP 安装 ![PyPI - Wheel](https://img.shields.io/pypi/wheel/ddns.svg?style=social)
60
- - [x] Docker 支持(@NN708)
61
- - 域名支持:
62
- - [x] 多个域名支持
63
- - [x] 多级域名解析
64
- - [x] 自动创建新记录
65
- - IP 类型:
66
- - [x] 内网 IPv4 / IPv6
67
- - [x] 公网 IPv4 / IPv6 (支持自定义 API)
68
- - [x] 自定义命令(shell)
69
- - [x] 正则选取支持(@rufengsuixing)
70
- - 网络代理:
71
- - [x] http 代理支持
72
- - [x] 多代理自动切换
73
- - 服务商支持:
74
- - [x] [DNSPOD](https://www.dnspod.cn/)
75
- - [x] [阿里 DNS](http://www.alidns.com/)
76
- - [x] [DNS.COM](https://www.dns.com/)(@loftor-git)
77
- - [x] [DNSPOD 国际版](https://www.dnspod.com/)
78
- - [x] [CloudFlare](https://www.cloudflare.com/)(@tongyifan)
79
- - [x] [HE.net](https://dns.he.net/)(@NN708) (不支持自动创建记录)
80
- - [x] [华为云](https://huaweicloud.com/)(@cybmp3)
81
- - 其他:
82
- - [x] 可设置定时任务
83
- - [x] TTL 配置支持
84
- - [x] 本地文件缓存(减少 API 请求)
85
- - [x] 地址变更时触发自定义回调API(与 DDNS 功能互斥)
86
-
87
- ## 使用
88
-
89
- ### ① 安装
90
-
91
- 根据需要选择一种方式: `二进制`版,`pip`版,`源码`运行,或者`Docker`
92
-
93
- - #### pip 安装(需要 pip 或 easy_install)
94
- 1. 安装 ddns: `pip install ddns` 或 `easy_install ddns`
95
- 2. 运行: `ddns`
96
- - #### 二进制版(单文件,无需 python)
97
- - Windows [ddns.exe](https://github.com/NewFuture/DDNS/releases/latest)
98
- - Linux (仅 Ubuntu 测试) [ddns](https://github.com/NewFuture/DDNS/releases/latest)
99
- - Mac OSX [ddns-osx](https://github.com/NewFuture/DDNS/releases/latest)
100
- - #### 源码运行(无任何依赖, 需 python 环境)
101
- 1. clone 或者[下载此仓库](https://github.com/NewFuture/DDNS/archive/master.zip)并解压
102
- 2. 运行./run.py (widnows 双击`run.bat`或者运行`python run.py`)
103
- - #### Docker(需要安装 Docker)
104
- - 使用环境变量:
105
- ```
106
- docker run -d \
107
- -e DDNS_DNS=dnspod \
108
- -e DDNS_ID=12345 \
109
- -e DDNS_TOKEN=mytokenkey \
110
- -e DDNS_IPV4=ddns.newfuture.cc \
111
- -e DDNS_IPV6=ddns.newfuture.cc \
112
- --network host \
113
- newfuture/ddns
114
- ```
115
- - 使用配置文件:
116
- ```
117
- docker run -d \
118
- -v /path/to/config.json:/config.json \
119
- --network host \
120
- newfuture/ddns
121
- ```
122
-
123
- ### ② 快速配置
124
-
125
- 1. 申请 api `token`,填写到对应的`id`和`token`字段:
126
-
127
- - [DNSPOD(国内版)创建 token](https://support.dnspod.cn/Kb/showarticle/tsid/227/)
128
- - [阿里云 accesskey](https://help.aliyun.com/document_detail/87745.htm)
129
- - [DNS.COM API Key/Secret](https://www.dns.com/member/apiSet)
130
- - [DNSPOD(国际版)](https://www.dnspod.com/docs/info.html#get-the-user-token)
131
- - [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 权限)
132
- - [HE.net DDNS 文档](https://dns.he.net/docs.html)(仅需将设置的密码填入`token`字段,`id`字段可留空)
133
- - [华为 APIKEY 申请](https://console.huaweicloud.com/iam/)(点左边访问密钥,然后点新增访问密钥)
134
- - 自定义回调的参数填写方式请查看下方的自定义回调配置说明
135
-
136
- 2. 修改配置文件,`ipv4`和`ipv6`字段,为待更新的域名,详细参照配置说明
137
-
138
- ## 详细配置
139
-
140
- 所有字段可通过三种方式进行配置
141
-
142
- 1. 命令行参数 `ddns --key=value` (`ddns -h` 查看详情),优先级最高
143
- 2. JSON配置文件(值为null认为是有效值,会覆盖环境变量的设置,如果没有对应的key则会尝试试用环境变量)
144
- 3. 环境变量DDNS_前缀加上key 全大写或者全小写 (`${ddns_key}` 或 `${DDNS_KEY}`)
145
-
146
- <details open>
147
-
148
- <summary markdown="span">config.json 配置文件
149
- </summary>
150
-
151
- - 首次运行会自动生成一个模板配置文件
152
- - 可以使用 `-c`使用指定的配置文件 (默认读取当前目录的 config.json)
153
- - 推荐使用 vscode 等支持 JsonSchema 的编辑器编辑配置文件
154
-
155
- ```bash
156
- ddns -c path/to/config.json
157
- # 或者源码运行
158
- python run.py -c /path/to/config.json
159
- ```
160
-
161
- #### 配置参数表
162
-
163
- | key | type | required | default | description | tips |
164
- | :----: | :----------------: | :------: | :---------: | :---------------: | ----------------------------------------------------------------------------------------------------------- |
165
- | id | string | √ | 无 | api 访问 ID | Cloudflare 为邮箱(使用 Token 时留空)<br>HE.net 可留空<br>华为云为 Access Key ID (AK) |
166
- | token | string | √ | 无 | api 授权 token | 部分平台叫 secret key , **反馈粘贴时删除** |
167
- | dns | string | No | `"dnspod"` | dns 服务商 | 阿里 DNS 为`alidns`,<br>Cloudflare 为 `cloudflare`,<br>dns.com 为 `dnscom`,<br>DNSPOD 国内为 `dnspod`,<br>DNSPOD 国际版为 `dnspod_com`,<br>HE.net 为`he`,<br>华为 DNS 为`huaweidns`,<br>自定义回调为`callback` |
168
- | ipv4 | array | No | `[]` | ipv4 域名列表 | 为`[]`时,不会获取和更新 IPv4 地址 |
169
- | ipv6 | array | No | `[]` | ipv6 域名列表 | 为`[]`时,不会获取和更新 IPv6 地址 |
170
- | index4 | string\|int\|array | No | `"default"` | ipv4 获取方式 | 可设置`网卡`,`内网`,`公网`,`正则`等方式 |
171
- | index6 | string\|int\|array | No | `"default"` | ipv6 获取方式 | 可设置`网卡`,`内网`,`公网`,`正则`等方式 |
172
- | ttl | number | No | `null` | DNS 解析 TTL 时间 | 不设置采用 DNS 默认策略 |
173
- | proxy | string | No | 无 | http 代理`;`分割 | 多代理逐个尝试直到成功,`DIRECT`为直连 |
174
- | debug | bool | No | `false` | 是否开启调试 | 运行异常时,打开调试输出,方便诊断错误 |
175
- | cache | string\|bool | No | `true` | 是否缓存记录 | 正常情况打开避免频繁更新,默认位置为临时目录下`ddns.cache`,<br>也可以指定一个具体文件实现自定义文件缓存位置 |
176
-
177
- #### index4 和 index6 参数说明
178
-
179
- - 数字(`0`,`1`,`2`,`3`等): 第 i 个网卡 ip
180
- - 字符串`"default"`(或者无此项): 系统访问外网默认 IP
181
- - 字符串`"public"`: 使用公网 ip(使用公网 API 查询,url 的简化模式)
182
- - 字符串`"url:xxx"`: 打开 URL `xxx`(如:`"url:http://ip.sb"`),从返回的数据提取 IP 地址
183
- - 字符串`"regex:xxx"` 正则表达(如`"regex:192.*"`): 提取`ifconfig`/`ipconfig`中与之匹配的首个 IP 地址,**注意 json 转义**(`\`要写成`\\`)
184
- - `"192.*"`表示 192 开头的所有 ip (注意`regex:`不可省略)
185
- - 如果想匹配`10.00.xxxx`应该写成`"regex:10\\.00\\..*"` (`"\\"` json 转义成`\`)
186
- - 字符串`"cmd:xxxx"`: 执行命令`xxxx`的 stdout 输出结果作为目标 IP
187
- - 字符串`"shell:xxx"`: 使用系统 shell 运行`xxx`,并把结果 stdout 作为目标 IP
188
- - `false`: 强制禁止更新 ipv4 或 ipv6 的 DNS 解析
189
- - 列表:依次执行列表中的index规则,并将最先获得的结果作为目标 IP
190
- - 例如`["public", "regex:172\\..*"]`将先查询公网API,未获取到IP后再从本地寻找172开头的IP
191
-
192
- #### 自定义回调配置说明
193
-
194
- - `id` 字段填写回调地址,以 HTTP 或 HTTPS 开头,推荐采用 HTTPS 方式的回调 API ,当 `token` 字段非空且 URL 参数包含下表所示的常量字符串时,常量会被程序替换为实际值
195
- - `token` 字段为 POST 参数,本字段为空或不存在则使用 GET 方式发起回调,回调参数采用 JSON 格式编码,当 JSON 的首层参数值包含下表所示的常量字符串时,常量会被程序替换为实际值
196
-
197
- | 常量名称 | 常量内容 | 说明 |
198
- | ---------------- | ---------------------- | -------- |
199
- | `__DOMAIN__` | DDNS 域名 | |
200
- | `__RECORDTYPE__` | DDNS 记录类型 | |
201
- | `__TTL__` | DDNS TTL | |
202
- | `__TIMESTAMP__` | 请求发起时间戳 | 包含小数 |
203
- | `__IP__` | 获取的对应类型的IP地址 | |
204
-
205
- #### 配置示例
206
-
207
- ```json
208
- {
209
- "$schema": "https://ddns.newfuture.cc/schema/v2.8.json",
210
- "id": "12345",
211
- "token": "mytokenkey",
212
- "dns": "dnspod 或 dnspod_com 或 alidns 或 dnscom 或 cloudflare 或 he 或 huaweidns 或 callback",
213
- "ipv4": ["ddns.newfuture.cc", "ipv4.ddns.newfuture.cc"],
214
- "ipv6": ["ddns.newfuture.cc", "ipv6.ddns.newfuture.cc"],
215
- "index4": 0,
216
- "index6": "public",
217
- "ttl": 600,
218
- "proxy": "127.0.0.1:1080;DIRECT",
219
- "debug": false
220
- }
221
- ```
222
-
223
- </details>
224
-
225
- ## 定时任务
226
-
227
- <details>
228
-
229
- <summary markdown="span">可以通过脚本设置定时任务(默认每5分钟检查一次ip,自动更新)
230
- </summary>
231
-
232
- #### Windows
233
-
234
- - [推荐]以系统身份运行,右键"以管理员身份运行"`task.bat`(或者在管理员命令行中运行)
235
- - 以当前用户身份运行定时任务,双击或者运行`task.bat` (执行时会闪黑框)
236
-
237
- #### Linux
238
-
239
- - 使用init.d和crontab:
240
- `sudo ./task.sh`
241
- - 使用systemd:
242
- ```bash
243
- 安装:
244
- sudo ./systemd.sh install
245
- 卸载:
246
- sudo ./systemd.sh uninstall
247
- ```
248
- 该脚本安装的文件符合 [Filesystem Hierarchy Standard (FHS)](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard):
249
- 可执行文件所在目录为 `/usr/share/DDNS`
250
- 配置文件所在目录为 `/etc/DDNS`
251
-
252
- #### Docker
253
-
254
- Docker镜像在无额外参数的情况下,已默认启用每5分钟执行一次的定时任务
255
- </details>
256
-
257
- ## FAQ
258
-
259
- <details>
260
-
261
- <summary markdown="span"> Windows Server [SSL: CERTIFICATE_VERIFY_FAILED]
262
- </summary>
263
-
264
- > Windows Server 默认安全策略会禁止任何未添加的信任 ssl 证书,可手动添加一下对应的证书 [#56](https://github.com/NewFuture/DDNS/issues/56#issuecomment-487371078)
265
-
266
- 使用系统自带的 IE 浏览器访问一次对应的 API 即可
267
-
268
- - alidns 打开: <https://alidns.aliyuncs.com>
269
- - cloudflare 打开: <https://api.cloudflare.com>
270
- - dns.com 打开: <https://www.dns.com>
271
- - dnspod.cn 打开: <https://dnsapi.cn>
272
- - dnspod 国际版: <https://api.dnspod.com>
273
- - 华为 DNS <https://dns.myhuaweicloud.com>
274
- </details>
275
-
276
- <details>
277
-
278
- <summary markdown="span"> 问题排查反馈
279
- </summary>
280
-
281
- 1. 先确认排查是否是系统/网络环境问题
282
- 2. 在[issues](https://github.com/NewFuture/DDNS/issues)中搜索是否有类似问题
283
- 3. 前两者均无法解决或者确定是 bug,[在此新建 issue](https://github.com/NewFuture/DDNS/issues/new)
284
- - [ ] 开启 debug 配置
285
- - [ ] 附上这些内容 **运行版本和方式**,**系统环境**, **出错日志**,**去掉 id/token**的配置文件
286
- - [ ] 源码运行注明使用的 python 环境
287
-
288
- </details>
@@ -1,20 +0,0 @@
1
- run.py,sha256=2SjiqPYKiiTFff4fYB__OP8XqYXtgdO3vwLYLSe76Jo,6075
2
- ddns-3.1.6.dist-info/licenses/LICENSE,sha256=MI-ECjp-Vl7WZLiSPY6r5VwrOReNiICVB1QCXiUGt_s,1111
3
- dns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- dns/alidns.py,sha256=qA1jm6qYxuLHYWVlCkHOizP7EisCXwSndqKibu_0CbM,5788
5
- dns/callback.py,sha256=gVszmwu2OENYXJKEz1ay1v0H03fBO-REZdwBcxn490U,3214
6
- dns/cloudflare.py,sha256=t_VNM4QfkVVyoLZxHEL0zZrh_1BaEi2tR8Ckx1Z6dek,5653
7
- dns/dnscom.py,sha256=PKKVd4O1696RVSc_abMh5BVs6fiVm86hYZVFTNgt_54,5304
8
- dns/dnspod.py,sha256=c2i6-Gz-So8EzHPMN0u4Eu4nG_VEan_gjp6LoRO0OlQ,6148
9
- dns/dnspod_com.py,sha256=9DW7Oz1f2fYSLDDGdwQKMp9OhRJ_CYB8bfzGhf0fIMI,339
10
- dns/he.py,sha256=iSB9XWAIq_njYbudHpIrI0CZTuxQb46b0ungTVvenNk,2148
11
- dns/huaweidns.py,sha256=MeVTFwLAT6Y0AiDo-6vMUVcBFKxyV5sYJYpvSv_Mw1o,8753
12
- util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- util/cache.py,sha256=NBzQ2JzrbqjbIsFzE0V-sPi7Ds9Oxos9m0JQDsUnAA8,3343
14
- util/config.py,sha256=8zvjdYEzQF7XaQHG7tyjXl6JBAHcOhl9ARCFQkcBeJs,4774
15
- util/ip.py,sha256=IZDcxOeY6IYrrRJbgxfDtA_Ibb842hbTTwW4yhhydyg,3842
16
- ddns-3.1.6.dist-info/METADATA,sha256=MYy_n6PVL5c2Ogj1TXBwXPpxw3Z4lKr7q1lfLyn2fVs,13841
17
- ddns-3.1.6.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
18
- ddns-3.1.6.dist-info/entry_points.txt,sha256=vpxxBE6Kepc-SHatr-FRRLmZecrTupYejyDB8ovJbLg,34
19
- ddns-3.1.6.dist-info/top_level.txt,sha256=dS3_ClQTIG1ousHykZ1P3AzQa-MKq4jT_rbm21D-KxE,13
20
- ddns-3.1.6.dist-info/RECORD,,
File without changes