ddns 3.1.5__py2.py3-none-any.whl → 4.0.0__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.

ddns/util/config.py ADDED
@@ -0,0 +1,317 @@
1
+ # -*- coding:utf-8 -*-
2
+ from argparse import Action, ArgumentParser, Namespace, RawTextHelpFormatter
3
+ from json import load as loadjson, dump as dumpjson
4
+ from os import stat, environ, path
5
+ from logging import critical, error, getLevelName
6
+ from ast import literal_eval
7
+
8
+ import platform
9
+ import sys
10
+
11
+
12
+ __cli_args = Namespace()
13
+ __config = {} # type: dict
14
+ log_levels = [
15
+ "CRITICAL", # 50
16
+ "ERROR", # 40
17
+ "WARNING", # 30
18
+ "INFO", # 20
19
+ "DEBUG", # 10
20
+ "NOTSET", # 0
21
+ ]
22
+
23
+ # 支持数组的参数列表
24
+ ARRAY_PARAMS = ["index4", "index6", "ipv4", "ipv6", "proxy"]
25
+ # 简单数组,支持’,’, ‘;’ 分隔的参数列表
26
+ SIMPLE_ARRAY_PARAMS = ["ipv4", "ipv6", "proxy"]
27
+
28
+
29
+ def str2bool(v):
30
+ """
31
+ parse string to boolean
32
+ """
33
+ if isinstance(v, bool):
34
+ return v
35
+ if v.lower() in ("yes", "true", "t", "y", "1"):
36
+ return True
37
+ elif v.lower() in ("no", "false", "f", "n", "0"):
38
+ return False
39
+ else:
40
+ return v
41
+
42
+
43
+ def log_level(value):
44
+ """
45
+ parse string to log level
46
+ or getattr(logging, value.upper())
47
+ """
48
+ return getLevelName(value.upper())
49
+
50
+
51
+ def parse_array_string(value, enable_simple_split):
52
+ """
53
+ 解析数组字符串
54
+ 仅当 trim 之后以 '[' 开头以 ']' 结尾时,才尝试使用 ast.literal_eval 解析
55
+ 默认返回原始字符串
56
+ """
57
+ if not isinstance(value, str):
58
+ return value
59
+
60
+ trimmed = value.strip()
61
+ if trimmed.startswith("[") and trimmed.endswith("]"):
62
+ try:
63
+ # 尝试使用 ast.literal_eval 解析数组
64
+ parsed_value = literal_eval(trimmed)
65
+ # 确保解析结果是列表或元组
66
+ if isinstance(parsed_value, (list, tuple)):
67
+ return list(parsed_value)
68
+ except (ValueError, SyntaxError) as e:
69
+ # 解析失败时返回原始字符串
70
+ error("Failed to parse array string: %s. Exception: %s", value, e)
71
+ elif enable_simple_split:
72
+ # 尝试使用逗号或分号分隔符解析
73
+ sep = None
74
+ if ',' in trimmed:
75
+ sep = ','
76
+ elif ';' in trimmed:
77
+ sep = ';'
78
+ if sep:
79
+ return [item.strip() for item in trimmed.split(sep) if item.strip()]
80
+ return value
81
+
82
+
83
+ def get_system_info_str():
84
+ system = platform.system()
85
+ release = platform.release()
86
+ machine = platform.machine()
87
+ arch = platform.architecture()[0] # '64bit' or '32bit'
88
+
89
+ return "{}-{} {} ({})".format(system, release, machine, arch)
90
+
91
+
92
+ def get_python_info_str():
93
+ version = platform.python_version()
94
+ branch, py_build_date = platform.python_build()
95
+ return "Python-{} {} ({})".format(version, branch, py_build_date)
96
+
97
+
98
+ def init_config(description, doc, version, date):
99
+ """
100
+ 配置
101
+ """
102
+ global __cli_args
103
+ parser = ArgumentParser(
104
+ description=description, epilog=doc, formatter_class=RawTextHelpFormatter
105
+ )
106
+ sysinfo = get_system_info_str()
107
+ pyinfo = get_python_info_str()
108
+ version_str = "v{} ({})\n{}\n{}".format(version, date, pyinfo, sysinfo)
109
+ parser.add_argument("-v", "--version", action="version", version=version_str)
110
+ parser.add_argument(
111
+ "-c", "--config", metavar="FILE", help="load config file [配置文件路径]"
112
+ )
113
+ parser.add_argument(
114
+ "--debug",
115
+ action="store_true",
116
+ help="debug mode [调试模式等效 --log.level=DEBUG]",
117
+ )
118
+
119
+ # 参数定义
120
+ parser.add_argument(
121
+ "--dns",
122
+ help="DNS provider [DNS服务提供商]",
123
+ choices=[
124
+ "alidns",
125
+ "cloudflare",
126
+ "dnscom",
127
+ "dnspod",
128
+ "dnspod_com",
129
+ "he",
130
+ "huaweidns",
131
+ "callback",
132
+ ],
133
+ )
134
+ parser.add_argument("--id", help="API ID or email [对应账号ID或邮箱]")
135
+ parser.add_argument("--token", help="API token or key [授权凭证或密钥]")
136
+ parser.add_argument(
137
+ "--index4",
138
+ nargs="*",
139
+ action=ExtendAction,
140
+ metavar="RULE",
141
+ help="IPv4 rules [获取IPv4方式, 多次可配置多规则]",
142
+ )
143
+ parser.add_argument(
144
+ "--index6",
145
+ nargs="*",
146
+ action=ExtendAction,
147
+ metavar="RULE",
148
+ help="IPv6 rules [获取IPv6方式, 多次可配置多规则]",
149
+ )
150
+ parser.add_argument(
151
+ "--ipv4",
152
+ nargs="*",
153
+ action=ExtendAction,
154
+ metavar="DOMAIN",
155
+ help="IPv4 domains [IPv4域名列表, 可配置多个域名]",
156
+ )
157
+ parser.add_argument(
158
+ "--ipv6",
159
+ nargs="*",
160
+ action=ExtendAction,
161
+ metavar="DOMAIN",
162
+ help="IPv6 domains [IPv6域名列表, 可配置多个域名]",
163
+ )
164
+ parser.add_argument("--ttl", type=int, help="DNS TTL(s) [设置域名解析过期时间]")
165
+ parser.add_argument(
166
+ "--proxy",
167
+ nargs="*",
168
+ action=ExtendAction,
169
+ help="HTTP proxy [设置http代理,可配多个代理连接]",
170
+ )
171
+ parser.add_argument(
172
+ "--cache",
173
+ type=str2bool,
174
+ nargs="?",
175
+ const=True,
176
+ help="set cache [启用缓存开关,或传入保存路径]",
177
+ )
178
+ parser.add_argument(
179
+ "--no-cache",
180
+ dest="cache",
181
+ action="store_const",
182
+ const=False,
183
+ help="disable cache [关闭缓存等效 --cache=false]",
184
+ )
185
+ parser.add_argument(
186
+ "--log.file", metavar="FILE", help="log file [日志文件,默认标准输出]"
187
+ )
188
+ parser.add_argument("--log.level", type=log_level, metavar="|".join(log_levels))
189
+ parser.add_argument(
190
+ "--log.format", metavar="FORMAT", help="log format [设置日志打印格式]"
191
+ )
192
+ parser.add_argument(
193
+ "--log.datefmt", metavar="FORMAT", help="date format [日志时间打印格式]"
194
+ )
195
+
196
+ __cli_args = parser.parse_args()
197
+ if __cli_args.debug:
198
+ # 如果启用调试模式,则设置日志级别为 DEBUG
199
+ setattr(__cli_args, "log.level", log_level("DEBUG"))
200
+
201
+ is_configfile_required = not get_config("token") and not get_config("id")
202
+ config_file = get_config("config")
203
+ if not config_file:
204
+ # 未指定配置文件且需要读取文件时,依次查找
205
+ cfgs = [
206
+ path.abspath("config.json"),
207
+ path.expanduser("~/.ddns/config.json"),
208
+ "/etc/ddns/config.json",
209
+ ]
210
+ config_file = next((cfg for cfg in cfgs if path.isfile(cfg)), cfgs[0])
211
+
212
+ if path.isfile(config_file):
213
+ __load_config(config_file)
214
+ __cli_args.config = config_file
215
+ elif is_configfile_required:
216
+ error("Config file is required, but not found: %s", config_file)
217
+ # 如果需要配置文件但没有指定,则自动生成
218
+ if generate_config(config_file):
219
+ sys.stdout.write("Default configure file %s is generated.\n" % config_file)
220
+ sys.exit(1)
221
+ else:
222
+ sys.exit("fail to load config from file: %s\n" % config_file)
223
+
224
+
225
+ def __load_config(config_path):
226
+ """
227
+ 加载配置
228
+ """
229
+ global __config
230
+ try:
231
+ with open(config_path, "r") as configfile:
232
+ __config = loadjson(configfile)
233
+ __config["config_modified_time"] = stat(config_path).st_mtime
234
+ if "log" in __config:
235
+ if "level" in __config["log"] and __config["log"]["level"] is not None:
236
+ __config["log.level"] = log_level(__config["log"]["level"])
237
+ if "file" in __config["log"]:
238
+ __config["log.file"] = __config["log"]["file"]
239
+ if "format" in __config["log"]:
240
+ __config["log.format"] = __config["log"]["format"]
241
+ if "datefmt" in __config["log"]:
242
+ __config["log.datefmt"] = __config["log"]["datefmt"]
243
+ elif "log.level" in __config:
244
+ __config["log.level"] = log_level(__config["log.level"])
245
+ except Exception as e:
246
+ critical("Failed to load config file `%s`: %s", config_path, e)
247
+ raise
248
+ # 重新抛出异常
249
+
250
+
251
+ def get_config(key, default=None):
252
+ """
253
+ 读取配置
254
+ 1. 命令行参数
255
+ 2. 配置文件
256
+ 3. 环境变量
257
+ """
258
+ if hasattr(__cli_args, key) and getattr(__cli_args, key) is not None:
259
+ return getattr(__cli_args, key)
260
+ if key in __config:
261
+ return __config.get(key)
262
+ # 检查环境变量
263
+ env_name = "DDNS_" + key.replace(".", "_") # type:str
264
+ variations = [env_name, env_name.upper(), env_name.lower()]
265
+ value = next((environ.get(v) for v in variations if v in environ), None)
266
+
267
+ # 如果找到环境变量值且参数支持数组,尝试解析为数组
268
+ if value is not None and key in ARRAY_PARAMS:
269
+ return parse_array_string(value, key in SIMPLE_ARRAY_PARAMS)
270
+
271
+ return value if value is not None else default
272
+
273
+
274
+ class ExtendAction(Action):
275
+ """
276
+ 兼容 Python <3.8 的 extend action
277
+ """
278
+
279
+ def __call__(self, parser, namespace, values, option_string=None):
280
+ items = getattr(namespace, self.dest, None)
281
+ if items is None:
282
+ items = []
283
+ # values 可能是单个值或列表
284
+ if isinstance(values, list):
285
+ items.extend(values)
286
+ else:
287
+ items.append(values)
288
+ setattr(namespace, self.dest, items)
289
+
290
+
291
+ def generate_config(config_path):
292
+ """
293
+ 生成配置文件
294
+ """
295
+ configure = {
296
+ "$schema": "https://ddns.newfuture.cc/schema/v4.0.json",
297
+ "id": "YOUR ID or EMAIL for DNS Provider",
298
+ "token": "YOUR TOKEN or KEY for DNS Provider",
299
+ "dns": "dnspod",
300
+ "ipv4": ["newfuture.cc", "ddns.newfuture.cc"],
301
+ "ipv6": ["newfuture.cc", "ipv6.ddns.newfuture.cc"],
302
+ "index4": "default",
303
+ "index6": "default",
304
+ "ttl": None,
305
+ "proxy": None,
306
+ "log": {"level": "INFO"},
307
+ }
308
+ try:
309
+ with open(config_path, "w") as f:
310
+ dumpjson(configure, f, indent=2, sort_keys=True)
311
+ return True
312
+ except IOError:
313
+ critical("Cannot open config file to write: `%s`!", config_path)
314
+ return False
315
+ except Exception as e:
316
+ critical("Failed to write config file `%s`: %s", config_path, e)
317
+ return False
@@ -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])'
@@ -0,0 +1,326 @@
1
+ Metadata-Version: 2.4
2
+ Name: ddns
3
+ Version: 4.0.0
4
+ Summary: Dynamic DNS client for multiple providers, supporting IPv4 and IPv6.
5
+ Author-email: NewFuture <python@newfuture.cc>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://ddns.newfuture.cc
8
+ Project-URL: Documentation, https://ddns.newfuture.cc
9
+ Project-URL: Repository, https://github.com/NewFuture/DDNS
10
+ Project-URL: Bug Tracker, https://github.com/NewFuture/DDNS/issues
11
+ Project-URL: Source, https://github.com/NewFuture/DDNS
12
+ Keywords: ddns,ipv6,ipv4,dns,dnspod,alidns,cloudflare
13
+ Platform: any
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: End Users/Desktop
17
+ Classifier: Intended Audience :: Information Technology
18
+ Classifier: Intended Audience :: System Administrators
19
+ Classifier: Topic :: Internet
20
+ Classifier: Topic :: Internet :: Name Service (DNS)
21
+ Classifier: Topic :: System :: Networking
22
+ Classifier: Topic :: Software Development
23
+ Classifier: Programming Language :: Python :: 2.7
24
+ Classifier: Programming Language :: Python :: 3
25
+ Classifier: Programming Language :: Python :: 3.6
26
+ Classifier: Programming Language :: Python :: 3.7
27
+ Classifier: Programming Language :: Python :: 3.8
28
+ Classifier: Programming Language :: Python :: 3.9
29
+ Classifier: Programming Language :: Python :: 3.10
30
+ Classifier: Programming Language :: Python :: 3.11
31
+ Classifier: Programming Language :: Python :: 3.12
32
+ Classifier: Programming Language :: Python :: 3.13
33
+ Requires-Python: >=2.7
34
+ Description-Content-Type: text/markdown
35
+ License-File: LICENSE
36
+ Provides-Extra: dev
37
+ Requires-Dist: black; extra == "dev"
38
+ Requires-Dist: flake8; extra == "dev"
39
+ Dynamic: license-file
40
+
41
+ # [<img src="https://ddns.newfuture.cc/doc/img/ddns.svg" width="32px" height="32px"/>](https://ddns.newfuture.cc) [DDNS](https://github.com/NewFuture/DDNS)
42
+
43
+ > 自动更新 DNS 解析 到本机 IP 地址,支持 IPv4 和 IPv6,本地(内网)IP 和公网 IP。
44
+ > 代理模式,支持自动创建 DNS 记录。
45
+
46
+ [![Github Release](https://img.shields.io/github/v/release/NewFuture/DDNS?&logo=github&style=flatten
47
+ )](https://github.com/NewFuture/DDNS/releases/latest)
48
+ [![PyPI](https://img.shields.io/pypi/v/ddns.svg?label=ddns&logo=pypi&style=flatten)](https://pypi.org/project/ddns/)
49
+ [![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)
50
+ [![Build Status](https://github.com/NewFuture/DDNS/actions/workflows/build.yml/badge.svg?event=push)](https://github.com/NewFuture/DDNS/actions/workflows/build.yml)
51
+ [![Publish](https://github.com/NewFuture/DDNS/actions/workflows/publish.yml/badge.svg)](https://github.com/NewFuture/DDNS/actions/workflows/publish.yml)
52
+
53
+ ---
54
+
55
+ ## Features
56
+
57
+ - 兼容和跨平台:
58
+ - [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%20riscv64-blue?style=social)](https://hub.docker.com/r/newfuture/ddns)
59
+ - [二进制文件](https://github.com/NewFuture/DDNS/releases/latest) ![cross platform](https://img.shields.io/badge/system-windows_%7C%20linux_%7C%20mac-success.svg?style=social)
60
+
61
+ - 配置方式:
62
+ - [命令行参数](https://ddns.newfuture.cc/doc/cli.html)
63
+ - [JSON 配置文件](https://ddns.newfuture.cc/doc/json.html)
64
+ - [环境变量配置](https://ddns.newfuture.cc/doc/env.html)
65
+
66
+ - 域名支持:
67
+ - 多个域名支持
68
+ - 多级域名解析
69
+ - 自动创建新 DNS 记录
70
+ - IP 类型:
71
+ - 内网 IPv4 / IPv6
72
+ - 公网 IPv4 / IPv6 (支持自定义 API)
73
+ - 自定义命令(shell)
74
+ - 正则选取支持 (@rufengsuixing)
75
+ - 网络代理:
76
+ - http 代理支持
77
+ - 多代理自动切换
78
+ - 服务商支持:
79
+ - [DNSPOD](https://www.dnspod.cn/)
80
+ - [阿里 DNS](http://www.alidns.com/)
81
+ - [DNS.COM](https://www.dns.com/) (@loftor-git)
82
+ - [DNSPOD 国际版](https://www.dnspod.com/)
83
+ - [CloudFlare](https://www.cloudflare.com/) (@tongyifan)
84
+ - [HE.net](https://dns.he.net/) (@NN708) (不支持自动创建记录)
85
+ - [华为云](https://huaweicloud.com/) (@cybmp3)
86
+ - 其他:
87
+ - 可设置定时任务
88
+ - TTL 配置支持
89
+ - 本地文件缓存(减少 API 请求)
90
+ - 地址变更时触发自定义回调 API(与 DDNS 功能互斥)
91
+
92
+ ## 使用
93
+
94
+ ### ① 安装
95
+
96
+ 根据需要选择一种方式:`二进制`版、`pip`版、`源码`运行,或者 `Docker`。
97
+
98
+ 推荐 Docker 版,兼容性最佳,体积小,性能优化。
99
+
100
+ - #### pip 安装(需要 pip 或 easy_install)
101
+
102
+ 1. 安装 ddns: `pip install ddns` 或 `easy_install ddns`
103
+ 2. 运行: `ddns`
104
+
105
+ - #### 二进制版(单文件,无需 python)
106
+
107
+ - Windows [ddns.exe](https://github.com/NewFuture/DDNS/releases/latest)
108
+ - Linux(仅 Ubuntu 测试) [ddns](https://github.com/NewFuture/DDNS/releases/latest)
109
+ - Mac OSX [ddns-mac](https://github.com/NewFuture/DDNS/releases/latest)
110
+
111
+ - #### 源码运行(无任何依赖,需 python 环境)
112
+
113
+ 1. clone 或者 [下载此仓库](https://github.com/NewFuture/DDNS/archive/master.zip) 并解压
114
+ 2. 运行 ./run.py(windows 双击 `run.bat` 或者运行 `python run.py`)
115
+
116
+ - #### Docker(需要安装 Docker)
117
+
118
+ - 使用环境变量:
119
+
120
+ ```sh
121
+ docker run -d \
122
+ -e DDNS_DNS=dnspod \
123
+ -e DDNS_ID=12345 \
124
+ -e DDNS_TOKEN=mytokenkey \
125
+ -e DDNS_IPV4=ddns.newfuture.cc \
126
+ -e DDNS_IPV6=ddns.newfuture.cc \
127
+ --network host \
128
+ newfuture/ddns
129
+ ```
130
+
131
+ - 使用配置文件(docker 工作目录 `/ddns/`,默认配置位置 `/ddns/config.json`):
132
+
133
+ ```sh
134
+ docker run -d \
135
+ -v /local/config/path/:/ddns/ \
136
+ --network host \
137
+ newfuture/ddns
138
+ ```
139
+
140
+ 更多详细说明和高级用法请查看 [Docker 使用文档](doc/docker.md)。
141
+
142
+ ### ② 快速配置
143
+
144
+ 1. 申请 api `token`,填写到对应的 `id` 和 `token` 字段:
145
+
146
+ - [DNSPOD(国内版)创建 token](https://support.dnspod.cn/Kb/showarticle/tsid/227/)
147
+ - [阿里云 accesskey](https://help.aliyun.com/document_detail/87745.htm)
148
+ - [DNS.COM API Key/Secret](https://www.dns.com/member/apiSet)
149
+ - [DNSPOD(国际版)](https://www.dnspod.com/docs/info.html#get-the-user-token)
150
+ - [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 权限)
151
+ - [HE.net DDNS 文档](https://dns.he.net/docs.html)(仅需将设置的密码填入 `token` 字段,`id` 字段可留空)
152
+ - [华为 APIKEY 申请](https://console.huaweicloud.com/iam/)(点左边访问密钥,然后点新增访问密钥)
153
+ - 自定义回调的参数填写方式请查看下方的自定义回调配置说明
154
+
155
+ 2. 修改配置文件,`ipv4` 和 `ipv6` 字段,为待更新的域名,详细参照配置说明
156
+
157
+ ## 详细配置
158
+
159
+ 所有字段可通过三种方式进行配置,优先级为:**命令行参数 > JSON配置文件 > 环境变量**
160
+
161
+ 1. [命令行参数](doc/cli.md) `ddns --key=value`(`ddns -h` 查看详情),优先级最高
162
+ 2. [JSON 配置文件](doc/json.md)(值为 null 认为是有效值,会覆盖环境变量的设置,如果没有对应的 key 则会尝试使用环境变量)
163
+ 3. 环境变量 DDNS_ 前缀加上 key 全大写或者全小写,点转下划线(`${ddns_id}` 或 `${DDNS_ID}`,`${DDNS_LOG_LEVEL}`)
164
+
165
+ ### 配置优先级和字段覆盖关系
166
+
167
+ 如果同一个配置项在多个地方设置,将按照以下优先级规则生效:
168
+
169
+ - **命令行参数**:优先级最高,会覆盖其他所有设置
170
+ - **JSON配置文件**:介于命令行和环境变量之间,会覆盖环境变量中的设置
171
+ - **环境变量**:优先级最低,当其他方式未设置时使用
172
+
173
+ **特殊情况**:
174
+ - JSON配置中明确设为`null`的值会覆盖环境变量设置
175
+ - `debug`参数只在命令行中有效,JSON配置文件中的同名设置无效
176
+ - 多值参数(如`ipv4`、`ipv6`等)在命令行中使用方式为重复使用参数,如`--ipv4 domain1 --ipv4 domain2`
177
+
178
+ 各配置方式的详细说明请查看对应文档:[命令行](doc/cli.md)、[JSON配置](doc/json.md)、[环境变量](doc/env.md)
179
+
180
+ > 📖 **环境变量详细配置**: 查看 [环境变量配置文档](doc/env.md) 了解所有环境变量的详细用法和示例
181
+
182
+ <details open>
183
+ <summary markdown="span">config.json 配置文件</summary>
184
+
185
+ - 首次运行会自动生成一个模板配置文件
186
+ - 可以使用 `-c` 使用指定的配置文件(默认读取当前目录的 config.json)
187
+ - 推荐使用 vscode 等支持 JsonSchema 的编辑器编辑配置文件
188
+ - 查看 [JSON配置文件详细文档](doc/json.md) 了解完整的配置选项和示例
189
+
190
+ ```bash
191
+ ddns -c path/to/config.json
192
+ # 或者源码运行
193
+ python run.py -c /path/to/config.json
194
+ ```
195
+
196
+ #### 配置参数表
197
+
198
+ | key | type | required | default | description | tips |
199
+ | :------: | :----------------: | :------: | :---------: | :---------------: | ----------------------------------------------------------------------------------------------------------- |
200
+ | id | string | √ | 无 | api 访问 ID | Cloudflare 为邮箱(使用 Token 时留空)<br>HE.net 可留空<br>华为云为 Access Key ID (AK) |
201
+ | token | string | √ | 无 | api 授权 token | 部分平台叫 secret key,**反馈粘贴时删除** |
202
+ | dns | string | No | `"dnspod"` | dns 服务商 | 阿里 DNS 为 `alidns`,Cloudflare 为 `cloudflare`,dns.com 为 `dnscom`,DNSPOD 国内为 `dnspod`,DNSPOD 国际为 `dnspod_com`,HE.net 为 `he`,华为云为 `huaweidns`,自定义回调为 `callback` |
203
+ | ipv4 | array | No | `[]` | ipv4 域名列表 | 为 `[]` 时,不会获取和更新 IPv4 地址 |
204
+ | ipv6 | array | No | `[]` | ipv6 域名列表 | 为 `[]` 时,不会获取和更新 IPv6 地址 |
205
+ | index4 | string\|int\|array | No | `"default"` | ipv4 获取方式 | 可设置 `网卡`、`内网`、`公网`、`正则` 等方式 |
206
+ | index6 | string\|int\|array | No | `"default"` | ipv6 获取方式 | 可设置 `网卡`、`内网`、`公网`、`正则` 等方式 |
207
+ | ttl | number | No | `null` | DNS 解析 TTL 时间 | 不设置采用 DNS 默认策略 |
208
+ | proxy | string\|array | No | 无 | http 代理 `;` 分割 | 多代理逐个尝试直到成功,`DIRECT` 为直连 |
209
+ | debug | bool | No | `false` | 是否开启调试 | 等同于设置 log.level=DEBUG,仅命令行参数`--debug`有效 |
210
+ | cache | string\|bool | No | `true` | 是否缓存记录 | 正常情况打开避免频繁更新,默认位置为临时目录下 `ddns.cache`,也可以指定一个具体路径 |
211
+ | log | object | No | `null` | 日志配置(可选) | 日志配置对象,支持`level`、`file`、`format`、`datefmt`参数 |
212
+
213
+ #### index4 和 index6 参数说明
214
+
215
+ - 数字(`0`,`1`,`2`,`3`等):第 i 个网卡 ip
216
+ - 字符串 `"default"`(或者无此项):系统访问外网默认 IP
217
+ - 字符串 `"public"`:使用公网 ip(使用公网 API 查询,url 的简化模式)
218
+ - 字符串 `"url:xxx"`:打开 URL `xxx`(如:`"url:http://ip.sb"`),从返回的数据提取 IP 地址
219
+ - 字符串 `"regex:xxx"` 正则表达(如 `"regex:192.*"`):提取 `ifconfig`/`ipconfig` 中与之匹配的首个 IP 地址,**注意 json 转义**(`\`要写成`\\`)
220
+ - `"192.*"` 表示 192 开头的所有 ip(注意 `regex:` 不可省略)
221
+ - 如果想匹配 `10.00.xxxx` 应该写成 `"regex:10\\.00\\..*"`(`"\\"` json 转义成 `\`)
222
+ - 字符串 `"cmd:xxxx"`:执行命令 `xxxx` 的 stdout 输出结果作为目标 IP
223
+ - 字符串 `"shell:xxx"`:使用系统 shell 运行 `xxx`,并把结果 stdout 作为目标 IP
224
+ - `false`:强制禁止更新 ipv4 或 ipv6 的 DNS 解析
225
+ - 列表:依次执行列表中的 index 规则,并将最先获得的结果作为目标 IP
226
+ - 例如 `["public", "regex:172\\..*"]` 将先查询公网 API,未获取到 IP 后再从本地寻找 172 开头的 IP
227
+
228
+ #### 自定义回调配置说明
229
+
230
+ - `id` 字段填写回调地址,以 HTTP 或 HTTPS 开头,推荐采用 HTTPS 方式的回调 API ,当 `token` 字段非空且 URL 参数包含下表所示的常量字符串时,会自动替换为实际内容。
231
+ - `token` 字段为 POST 参数,本字段为空或不存在则使用 GET 方式发起回调,回调参数采用 JSON 格式编码,当 JSON 的首层参数值包含下表所示的常量字符串时,会自动替换为实际内容。
232
+
233
+ | 常量名称 | 常量内容 | 说明 |
234
+ | ---------------- | ---------------------- | -------- |
235
+ | `__DOMAIN__` | DDNS 域名 | |
236
+ | `__RECORDTYPE__` | DDNS 记录类型 | |
237
+ | `__TTL__` | DDNS TTL | |
238
+ | `__TIMESTAMP__` | 请求发起时间戳 | 包含小数 |
239
+ | `__IP__` | 获取的对应类型的 IP 地址 | |
240
+
241
+ #### 配置示例
242
+
243
+ ```json
244
+ {
245
+ "$schema": "https://ddns.newfuture.cc/schema/v4.0.json",
246
+ "id": "12345",
247
+ "token": "mytokenkey",
248
+ "dns": "dnspod 或 dnspod_com 或 alidns 或 dnscom 或 cloudflare 或 he 或 huaweidns 或 callback",
249
+ "ipv4": ["ddns.newfuture.cc", "ipv4.ddns.newfuture.cc"],
250
+ "ipv6": ["ddns.newfuture.cc", "ipv6.ddns.newfuture.cc"],
251
+ "index4": 0,
252
+ "index6": "public",
253
+ "ttl": 600,
254
+ "proxy": "127.0.0.1:1080;DIRECT",
255
+ "log": {
256
+ "level": "DEBUG",
257
+ "file": "dns.log",
258
+ "format": "%(asctime)s %(levelname)s [%(module)s]: %(message)s",
259
+ "datefmt": "%Y-%m-%dT%H:%M:%S"
260
+ }
261
+ }
262
+ ```
263
+ </details>
264
+
265
+ ## 定时任务
266
+
267
+ <details>
268
+ <summary markdown="span">可以通过脚本设置定时任务(默认每 5 分钟检查一次 IP,自动更新)</summary>
269
+
270
+ #### Windows
271
+
272
+ - [推荐] 以系统身份运行,右键“以管理员身份运行”`task.bat`(或者在管理员命令行中运行)
273
+ - 以当前用户身份运行定时任务,双击或运行 `task.bat`(执行时会闪黑框)
274
+
275
+ #### Linux
276
+
277
+ - 使用 init.d 和 crontab:
278
+ ```bash
279
+ sudo ./task.sh
280
+ ```
281
+ - 使用 systemd:
282
+ ```bash
283
+ 安装:
284
+ sudo ./systemd.sh install
285
+ 卸载:
286
+ sudo ./systemd.sh uninstall
287
+ ```
288
+
289
+ 该脚本安装的文件符合 [Filesystem Hierarchy Standard (FHS)](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard):
290
+ 可执行文件所在目录为 `/usr/share/DDNS`
291
+ 配置文件所在目录为 `/etc/DDNS`
292
+
293
+ #### Docker
294
+
295
+ Docker 镜像在无额外参数的情况下,已默认启用每 5 分钟执行一次的定时任务
296
+
297
+ </details>
298
+
299
+ ## FAQ
300
+
301
+ <details>
302
+ <summary markdown="span">Windows Server [SSL: CERTIFICATE_VERIFY_FAILED]</summary>
303
+
304
+ > Windows Server 默认安全策略会禁止任何未添加的信任 SSL 证书,可手动添加一下对应的证书 [#56](https://github.com/NewFuture/DDNS/issues/56#issuecomment-487371078)
305
+
306
+ 使用系统自带的 IE 浏览器访问一次对应的 API 即可
307
+
308
+ - alidns 打开: <https://alidns.aliyuncs.com>
309
+ - cloudflare 打开: <https://api.cloudflare.com>
310
+ - dns.com 打开: <https://www.dns.com>
311
+ - dnspod.cn 打开: <https://dnsapi.cn>
312
+ - dnspod 国际版: <https://api.dnspod.com>
313
+ - 华为 DNS <https://dns.myhuaweicloud.com>
314
+ </details>
315
+
316
+ <details>
317
+ <summary markdown="span">问题排查反馈</summary>
318
+
319
+ 1. 先确认排查是否是系统/网络环境问题
320
+ 2. 在 [issues](https://github.com/NewFuture/DDNS/issues) 中搜索是否有类似问题
321
+ 3. 前两者均无法解决或者确定是 bug,[在此新建 issue](https://github.com/NewFuture/DDNS/issues/new)
322
+ - [ ] 开启 debug 配置
323
+ - [ ] 附上这些内容 **运行版本和方式**、**系统环境**、**出错日志**、**去掉 id/token** 的配置文件
324
+ - [ ] 源码运行注明使用的 python 环境
325
+
326
+ </details>
@@ -0,0 +1,21 @@
1
+ ddns/__init__.py,sha256=S58_4GMV40EfyYnjeBprxbc0O0_JpzYbtrJ5sUtE1mY,503
2
+ ddns/__main__.py,sha256=TIoMSOmdHay9k4m4rr5449y3tk8I60ruub_rBOxjh4M,6148
3
+ ddns/provider/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ ddns/provider/alidns.py,sha256=fD8mzr7FJ5QxOWLA3ekoOhjeU1IFepOpxybrgZv2c9k,5782
5
+ ddns/provider/callback.py,sha256=V8AG7kxtS0jIkyD9F7xnGJpu1L3E5KUcNDwOnQjcJKs,3208
6
+ ddns/provider/cloudflare.py,sha256=OBq4TJwVHHiPMM6FJo-bz20FOHPvA-RSFbuGilw3tVw,5647
7
+ ddns/provider/dnscom.py,sha256=Nhyl70JGnZY2IV0j5Ebgy_gYQGFb8hXTshaRG_rC0Ww,5297
8
+ ddns/provider/dnspod.py,sha256=52Hstd92-BkyEAicW5DkexfJiTww5KcNZkKs20HUIFE,6211
9
+ ddns/provider/dnspod_com.py,sha256=touJksX1aPDAaP9yWRLVzbbbCtP8jzrDZSMSqtRlc0s,336
10
+ ddns/provider/he.py,sha256=X_WODK6vDTpHdAvPP_vUltaeAlHeG2jMPbzFkfYxWOg,2142
11
+ ddns/provider/huaweidns.py,sha256=0A2jxcJ5zGaMxMMh7C9SsDewhP2zVRLI8FznB9BBBEc,8748
12
+ ddns/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ ddns/util/cache.py,sha256=LVH5jGZFimXmD7XCQCtZULhagybM7gSjMo-73yqttiA,3338
14
+ ddns/util/config.py,sha256=KUHd24gLwYOCiIM59euEpJ5fkOSfHNzMSclSWiwNZX4,10165
15
+ ddns/util/ip.py,sha256=GZzqrl_nq9JuGHTWhPB0fffpr94ITJA6BCVlS5KlWcE,3836
16
+ ddns-4.0.0.dist-info/licenses/LICENSE,sha256=MI-ECjp-Vl7WZLiSPY6r5VwrOReNiICVB1QCXiUGt_s,1111
17
+ ddns-4.0.0.dist-info/METADATA,sha256=56Jpj_ZqrWqZScLU3ho-N47pI6Un6SmAmBbsuftAFWs,16831
18
+ ddns-4.0.0.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
19
+ ddns-4.0.0.dist-info/entry_points.txt,sha256=2-VbA-WZcjebkZrGKvUCuBBRYF4xQNMoLIoGaS234WU,44
20
+ ddns-4.0.0.dist-info/top_level.txt,sha256=Se0wn3T8Bc4pj55dGwVrCe8BFwmFCBwQVHF1bTyV0o0,5
21
+ ddns-4.0.0.dist-info/RECORD,,