├── .gitignore ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── etc └── cfddns.yml └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .env 3 | .envrc 4 | domains 5 | cfddns.yml 6 | 7 | # Created by https://www.gitignore.io/api/python 8 | # Edit at https://www.gitignore.io/?templates=python 9 | 10 | ### Python ### 11 | # Byte-compiled / optimized / DLL files 12 | __pycache__/ 13 | *.py[cod] 14 | *$py.class 15 | 16 | # C extensions 17 | *.so 18 | 19 | # Distribution / packaging 20 | .Python 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | wheels/ 33 | share/python-wheels/ 34 | *.egg-info/ 35 | .installed.cfg 36 | *.egg 37 | MANIFEST 38 | 39 | # PyInstaller 40 | # Usually these files are written by a python script from a template 41 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 42 | *.manifest 43 | *.spec 44 | 45 | # Installer logs 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # Unit test / coverage reports 50 | htmlcov/ 51 | .tox/ 52 | .nox/ 53 | .coverage 54 | .coverage.* 55 | .cache 56 | nosetests.xml 57 | coverage.xml 58 | *.cover 59 | .hypothesis/ 60 | .pytest_cache/ 61 | 62 | # Translations 63 | *.mo 64 | *.pot 65 | 66 | # Django stuff: 67 | *.log 68 | local_settings.py 69 | db.sqlite3 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # IPython 88 | profile_default/ 89 | ipython_config.py 90 | 91 | # pyenv 92 | .python-version 93 | 94 | # celery beat schedule file 95 | celerybeat-schedule 96 | 97 | # SageMath parsed files 98 | *.sage.py 99 | 100 | # Environments 101 | .env 102 | .venv 103 | env/ 104 | venv/ 105 | ENV/ 106 | env.bak/ 107 | venv.bak/ 108 | 109 | # Spyder project settings 110 | .spyderproject 111 | .spyproject 112 | 113 | # Rope project settings 114 | .ropeproject 115 | 116 | # mkdocs documentation 117 | /site 118 | 119 | # mypy 120 | .mypy_cache/ 121 | .dmypy.json 122 | dmypy.json 123 | 124 | # Pyre type checker 125 | .pyre/ 126 | 127 | ### Python Patch ### 128 | .venv/ 129 | 130 | # End of https://www.gitignore.io/api/python 131 | 132 | 133 | # Added by cargo 134 | 135 | /target 136 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | ## Build 4 | 5 | ```bash 6 | cargo build 7 | ``` 8 | 9 | ## Run 10 | 11 | ```bash 12 | cargo run -- -c ./cfddns.yml ./domains 13 | ``` 14 | 15 | ## Publish 16 | 17 | ```bash 18 | # cargo install cargo-release 19 | cargo release --execute 20 | ``` 21 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.17.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "0.7.18" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anyhow" 31 | version = "1.0.56" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" 34 | 35 | [[package]] 36 | name = "async-trait" 37 | version = "0.1.53" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" 40 | dependencies = [ 41 | "proc-macro2", 42 | "quote", 43 | "syn", 44 | ] 45 | 46 | [[package]] 47 | name = "atty" 48 | version = "0.2.14" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 51 | dependencies = [ 52 | "hermit-abi", 53 | "libc", 54 | "winapi", 55 | ] 56 | 57 | [[package]] 58 | name = "autocfg" 59 | version = "1.1.0" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 62 | 63 | [[package]] 64 | name = "backtrace" 65 | version = "0.3.64" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" 68 | dependencies = [ 69 | "addr2line", 70 | "cc", 71 | "cfg-if 1.0.0", 72 | "libc", 73 | "miniz_oxide", 74 | "object", 75 | "rustc-demangle", 76 | ] 77 | 78 | [[package]] 79 | name = "base64" 80 | version = "0.13.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 83 | 84 | [[package]] 85 | name = "bitflags" 86 | version = "1.3.2" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 89 | 90 | [[package]] 91 | name = "bumpalo" 92 | version = "3.9.1" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" 95 | 96 | [[package]] 97 | name = "bytes" 98 | version = "1.1.0" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 101 | 102 | [[package]] 103 | name = "cc" 104 | version = "1.0.73" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 107 | 108 | [[package]] 109 | name = "cfddns" 110 | version = "1.6.2" 111 | dependencies = [ 112 | "anyhow", 113 | "clap", 114 | "cloudflare", 115 | "lettre", 116 | "once_cell", 117 | "regex", 118 | "reqwest", 119 | "serde", 120 | "serde_json", 121 | "serde_yaml", 122 | "tokio", 123 | ] 124 | 125 | [[package]] 126 | name = "cfg-if" 127 | version = "0.1.10" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 130 | 131 | [[package]] 132 | name = "cfg-if" 133 | version = "1.0.0" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 136 | 137 | [[package]] 138 | name = "chrono" 139 | version = "0.4.19" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 142 | dependencies = [ 143 | "libc", 144 | "num-integer", 145 | "num-traits", 146 | "serde", 147 | "time", 148 | "winapi", 149 | ] 150 | 151 | [[package]] 152 | name = "clap" 153 | version = "3.1.6" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123" 156 | dependencies = [ 157 | "atty", 158 | "bitflags", 159 | "clap_derive", 160 | "indexmap", 161 | "lazy_static", 162 | "os_str_bytes", 163 | "strsim", 164 | "termcolor", 165 | "textwrap", 166 | ] 167 | 168 | [[package]] 169 | name = "clap_derive" 170 | version = "3.1.4" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16" 173 | dependencies = [ 174 | "heck", 175 | "proc-macro-error", 176 | "proc-macro2", 177 | "quote", 178 | "syn", 179 | ] 180 | 181 | [[package]] 182 | name = "cloudflare" 183 | version = "0.9.1" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "28a69d53c61ddd5611ad0203c60aa02074d67f91bfb98db2225833f6bdc2f1a4" 186 | dependencies = [ 187 | "anyhow", 188 | "async-trait", 189 | "base64", 190 | "cfg-if 0.1.10", 191 | "chrono", 192 | "http", 193 | "percent-encoding 1.0.1", 194 | "reqwest", 195 | "serde", 196 | "serde_json", 197 | "serde_qs", 198 | "serde_with", 199 | "url", 200 | "uuid", 201 | ] 202 | 203 | [[package]] 204 | name = "core-foundation" 205 | version = "0.9.3" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 208 | dependencies = [ 209 | "core-foundation-sys", 210 | "libc", 211 | ] 212 | 213 | [[package]] 214 | name = "core-foundation-sys" 215 | version = "0.8.3" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 218 | 219 | [[package]] 220 | name = "darling" 221 | version = "0.13.1" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4" 224 | dependencies = [ 225 | "darling_core", 226 | "darling_macro", 227 | ] 228 | 229 | [[package]] 230 | name = "darling_core" 231 | version = "0.13.1" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324" 234 | dependencies = [ 235 | "fnv", 236 | "ident_case", 237 | "proc-macro2", 238 | "quote", 239 | "strsim", 240 | "syn", 241 | ] 242 | 243 | [[package]] 244 | name = "darling_macro" 245 | version = "0.13.1" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b" 248 | dependencies = [ 249 | "darling_core", 250 | "quote", 251 | "syn", 252 | ] 253 | 254 | [[package]] 255 | name = "data-encoding" 256 | version = "2.3.2" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" 259 | 260 | [[package]] 261 | name = "encoding_rs" 262 | version = "0.8.30" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" 265 | dependencies = [ 266 | "cfg-if 1.0.0", 267 | ] 268 | 269 | [[package]] 270 | name = "error-chain" 271 | version = "0.12.4" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" 274 | dependencies = [ 275 | "backtrace", 276 | "version_check", 277 | ] 278 | 279 | [[package]] 280 | name = "fastrand" 281 | version = "1.7.0" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" 284 | dependencies = [ 285 | "instant", 286 | ] 287 | 288 | [[package]] 289 | name = "fnv" 290 | version = "1.0.7" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 293 | 294 | [[package]] 295 | name = "foreign-types" 296 | version = "0.3.2" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 299 | dependencies = [ 300 | "foreign-types-shared", 301 | ] 302 | 303 | [[package]] 304 | name = "foreign-types-shared" 305 | version = "0.1.1" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 308 | 309 | [[package]] 310 | name = "form_urlencoded" 311 | version = "1.0.1" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 314 | dependencies = [ 315 | "matches", 316 | "percent-encoding 2.1.0", 317 | ] 318 | 319 | [[package]] 320 | name = "futures-channel" 321 | version = "0.3.21" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" 324 | dependencies = [ 325 | "futures-core", 326 | ] 327 | 328 | [[package]] 329 | name = "futures-core" 330 | version = "0.3.21" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" 333 | 334 | [[package]] 335 | name = "futures-io" 336 | version = "0.3.21" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" 339 | 340 | [[package]] 341 | name = "futures-sink" 342 | version = "0.3.21" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" 345 | 346 | [[package]] 347 | name = "futures-task" 348 | version = "0.3.21" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" 351 | 352 | [[package]] 353 | name = "futures-util" 354 | version = "0.3.21" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" 357 | dependencies = [ 358 | "futures-core", 359 | "futures-io", 360 | "futures-task", 361 | "memchr", 362 | "pin-project-lite", 363 | "pin-utils", 364 | "slab", 365 | ] 366 | 367 | [[package]] 368 | name = "getrandom" 369 | version = "0.2.5" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" 372 | dependencies = [ 373 | "cfg-if 1.0.0", 374 | "libc", 375 | "wasi 0.10.2+wasi-snapshot-preview1", 376 | ] 377 | 378 | [[package]] 379 | name = "gimli" 380 | version = "0.26.1" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" 383 | 384 | [[package]] 385 | name = "h2" 386 | version = "0.3.12" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b" 389 | dependencies = [ 390 | "bytes", 391 | "fnv", 392 | "futures-core", 393 | "futures-sink", 394 | "futures-util", 395 | "http", 396 | "indexmap", 397 | "slab", 398 | "tokio", 399 | "tokio-util", 400 | "tracing", 401 | ] 402 | 403 | [[package]] 404 | name = "hashbrown" 405 | version = "0.11.2" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 408 | 409 | [[package]] 410 | name = "heck" 411 | version = "0.4.0" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 414 | 415 | [[package]] 416 | name = "hermit-abi" 417 | version = "0.1.19" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 420 | dependencies = [ 421 | "libc", 422 | ] 423 | 424 | [[package]] 425 | name = "hostname" 426 | version = "0.3.1" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" 429 | dependencies = [ 430 | "libc", 431 | "match_cfg", 432 | "winapi", 433 | ] 434 | 435 | [[package]] 436 | name = "http" 437 | version = "0.2.6" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" 440 | dependencies = [ 441 | "bytes", 442 | "fnv", 443 | "itoa", 444 | ] 445 | 446 | [[package]] 447 | name = "http-body" 448 | version = "0.4.4" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" 451 | dependencies = [ 452 | "bytes", 453 | "http", 454 | "pin-project-lite", 455 | ] 456 | 457 | [[package]] 458 | name = "httparse" 459 | version = "1.6.0" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" 462 | 463 | [[package]] 464 | name = "httpdate" 465 | version = "1.0.2" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 468 | 469 | [[package]] 470 | name = "hyper" 471 | version = "0.14.18" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" 474 | dependencies = [ 475 | "bytes", 476 | "futures-channel", 477 | "futures-core", 478 | "futures-util", 479 | "h2", 480 | "http", 481 | "http-body", 482 | "httparse", 483 | "httpdate", 484 | "itoa", 485 | "pin-project-lite", 486 | "socket2", 487 | "tokio", 488 | "tower-service", 489 | "tracing", 490 | "want", 491 | ] 492 | 493 | [[package]] 494 | name = "hyper-tls" 495 | version = "0.5.0" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 498 | dependencies = [ 499 | "bytes", 500 | "hyper", 501 | "native-tls", 502 | "tokio", 503 | "tokio-native-tls", 504 | ] 505 | 506 | [[package]] 507 | name = "ident_case" 508 | version = "1.0.1" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 511 | 512 | [[package]] 513 | name = "idna" 514 | version = "0.2.3" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 517 | dependencies = [ 518 | "matches", 519 | "unicode-bidi", 520 | "unicode-normalization", 521 | ] 522 | 523 | [[package]] 524 | name = "indexmap" 525 | version = "1.8.0" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" 528 | dependencies = [ 529 | "autocfg", 530 | "hashbrown", 531 | ] 532 | 533 | [[package]] 534 | name = "instant" 535 | version = "0.1.12" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 538 | dependencies = [ 539 | "cfg-if 1.0.0", 540 | ] 541 | 542 | [[package]] 543 | name = "ipnet" 544 | version = "2.4.0" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" 547 | 548 | [[package]] 549 | name = "itoa" 550 | version = "1.0.1" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" 553 | 554 | [[package]] 555 | name = "js-sys" 556 | version = "0.3.56" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" 559 | dependencies = [ 560 | "wasm-bindgen", 561 | ] 562 | 563 | [[package]] 564 | name = "lazy_static" 565 | version = "1.4.0" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 568 | 569 | [[package]] 570 | name = "lettre" 571 | version = "0.10.0-rc.4" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "71d8da8f34d086b081c9cc3b57d3bb3b51d16fc06b5c848a188e2f14d58ac2a5" 574 | dependencies = [ 575 | "base64", 576 | "fastrand", 577 | "futures-util", 578 | "hostname", 579 | "httpdate", 580 | "idna", 581 | "mime", 582 | "native-tls", 583 | "nom", 584 | "once_cell", 585 | "quoted_printable", 586 | "regex", 587 | "rustls", 588 | "rustls-pemfile", 589 | "webpki-roots", 590 | ] 591 | 592 | [[package]] 593 | name = "libc" 594 | version = "0.2.121" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" 597 | 598 | [[package]] 599 | name = "linked-hash-map" 600 | version = "0.5.4" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" 603 | 604 | [[package]] 605 | name = "lock_api" 606 | version = "0.4.6" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" 609 | dependencies = [ 610 | "scopeguard", 611 | ] 612 | 613 | [[package]] 614 | name = "log" 615 | version = "0.4.16" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" 618 | dependencies = [ 619 | "cfg-if 1.0.0", 620 | ] 621 | 622 | [[package]] 623 | name = "match_cfg" 624 | version = "0.1.0" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" 627 | 628 | [[package]] 629 | name = "matches" 630 | version = "0.1.9" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" 633 | 634 | [[package]] 635 | name = "memchr" 636 | version = "2.4.1" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 639 | 640 | [[package]] 641 | name = "mime" 642 | version = "0.3.16" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 645 | 646 | [[package]] 647 | name = "minimal-lexical" 648 | version = "0.2.1" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 651 | 652 | [[package]] 653 | name = "miniz_oxide" 654 | version = "0.4.4" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 657 | dependencies = [ 658 | "adler", 659 | "autocfg", 660 | ] 661 | 662 | [[package]] 663 | name = "mio" 664 | version = "0.8.2" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" 667 | dependencies = [ 668 | "libc", 669 | "log", 670 | "miow", 671 | "ntapi", 672 | "wasi 0.11.0+wasi-snapshot-preview1", 673 | "winapi", 674 | ] 675 | 676 | [[package]] 677 | name = "miow" 678 | version = "0.3.7" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 681 | dependencies = [ 682 | "winapi", 683 | ] 684 | 685 | [[package]] 686 | name = "native-tls" 687 | version = "0.2.8" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" 690 | dependencies = [ 691 | "lazy_static", 692 | "libc", 693 | "log", 694 | "openssl", 695 | "openssl-probe", 696 | "openssl-sys", 697 | "schannel", 698 | "security-framework", 699 | "security-framework-sys", 700 | "tempfile", 701 | ] 702 | 703 | [[package]] 704 | name = "nom" 705 | version = "7.1.1" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" 708 | dependencies = [ 709 | "memchr", 710 | "minimal-lexical", 711 | ] 712 | 713 | [[package]] 714 | name = "ntapi" 715 | version = "0.3.7" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" 718 | dependencies = [ 719 | "winapi", 720 | ] 721 | 722 | [[package]] 723 | name = "num-integer" 724 | version = "0.1.44" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 727 | dependencies = [ 728 | "autocfg", 729 | "num-traits", 730 | ] 731 | 732 | [[package]] 733 | name = "num-traits" 734 | version = "0.2.14" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 737 | dependencies = [ 738 | "autocfg", 739 | ] 740 | 741 | [[package]] 742 | name = "num_cpus" 743 | version = "1.13.1" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 746 | dependencies = [ 747 | "hermit-abi", 748 | "libc", 749 | ] 750 | 751 | [[package]] 752 | name = "object" 753 | version = "0.27.1" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" 756 | dependencies = [ 757 | "memchr", 758 | ] 759 | 760 | [[package]] 761 | name = "once_cell" 762 | version = "1.10.0" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" 765 | 766 | [[package]] 767 | name = "openssl" 768 | version = "0.10.38" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" 771 | dependencies = [ 772 | "bitflags", 773 | "cfg-if 1.0.0", 774 | "foreign-types", 775 | "libc", 776 | "once_cell", 777 | "openssl-sys", 778 | ] 779 | 780 | [[package]] 781 | name = "openssl-probe" 782 | version = "0.1.5" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 785 | 786 | [[package]] 787 | name = "openssl-sys" 788 | version = "0.9.72" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" 791 | dependencies = [ 792 | "autocfg", 793 | "cc", 794 | "libc", 795 | "pkg-config", 796 | "vcpkg", 797 | ] 798 | 799 | [[package]] 800 | name = "os_str_bytes" 801 | version = "6.0.0" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" 804 | dependencies = [ 805 | "memchr", 806 | ] 807 | 808 | [[package]] 809 | name = "parking_lot" 810 | version = "0.12.0" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" 813 | dependencies = [ 814 | "lock_api", 815 | "parking_lot_core", 816 | ] 817 | 818 | [[package]] 819 | name = "parking_lot_core" 820 | version = "0.9.1" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" 823 | dependencies = [ 824 | "cfg-if 1.0.0", 825 | "libc", 826 | "redox_syscall", 827 | "smallvec", 828 | "windows-sys", 829 | ] 830 | 831 | [[package]] 832 | name = "percent-encoding" 833 | version = "1.0.1" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 836 | 837 | [[package]] 838 | name = "percent-encoding" 839 | version = "2.1.0" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 842 | 843 | [[package]] 844 | name = "pin-project-lite" 845 | version = "0.2.8" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" 848 | 849 | [[package]] 850 | name = "pin-utils" 851 | version = "0.1.0" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 854 | 855 | [[package]] 856 | name = "pkg-config" 857 | version = "0.3.24" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" 860 | 861 | [[package]] 862 | name = "proc-macro-error" 863 | version = "1.0.4" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 866 | dependencies = [ 867 | "proc-macro-error-attr", 868 | "proc-macro2", 869 | "quote", 870 | "syn", 871 | "version_check", 872 | ] 873 | 874 | [[package]] 875 | name = "proc-macro-error-attr" 876 | version = "1.0.4" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 879 | dependencies = [ 880 | "proc-macro2", 881 | "quote", 882 | "version_check", 883 | ] 884 | 885 | [[package]] 886 | name = "proc-macro2" 887 | version = "1.0.36" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 890 | dependencies = [ 891 | "unicode-xid", 892 | ] 893 | 894 | [[package]] 895 | name = "quote" 896 | version = "1.0.17" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" 899 | dependencies = [ 900 | "proc-macro2", 901 | ] 902 | 903 | [[package]] 904 | name = "quoted_printable" 905 | version = "0.4.5" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | checksum = "3fee2dce59f7a43418e3382c766554c614e06a552d53a8f07ef499ea4b332c0f" 908 | 909 | [[package]] 910 | name = "redox_syscall" 911 | version = "0.2.12" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0" 914 | dependencies = [ 915 | "bitflags", 916 | ] 917 | 918 | [[package]] 919 | name = "regex" 920 | version = "1.5.5" 921 | source = "registry+https://github.com/rust-lang/crates.io-index" 922 | checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" 923 | dependencies = [ 924 | "aho-corasick", 925 | "memchr", 926 | "regex-syntax", 927 | ] 928 | 929 | [[package]] 930 | name = "regex-syntax" 931 | version = "0.6.25" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 934 | 935 | [[package]] 936 | name = "remove_dir_all" 937 | version = "0.5.3" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 940 | dependencies = [ 941 | "winapi", 942 | ] 943 | 944 | [[package]] 945 | name = "reqwest" 946 | version = "0.11.10" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" 949 | dependencies = [ 950 | "base64", 951 | "bytes", 952 | "encoding_rs", 953 | "futures-core", 954 | "futures-util", 955 | "h2", 956 | "http", 957 | "http-body", 958 | "hyper", 959 | "hyper-tls", 960 | "ipnet", 961 | "js-sys", 962 | "lazy_static", 963 | "log", 964 | "mime", 965 | "native-tls", 966 | "percent-encoding 2.1.0", 967 | "pin-project-lite", 968 | "serde", 969 | "serde_json", 970 | "serde_urlencoded", 971 | "tokio", 972 | "tokio-native-tls", 973 | "url", 974 | "wasm-bindgen", 975 | "wasm-bindgen-futures", 976 | "web-sys", 977 | "winreg", 978 | ] 979 | 980 | [[package]] 981 | name = "ring" 982 | version = "0.16.20" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 985 | dependencies = [ 986 | "cc", 987 | "libc", 988 | "once_cell", 989 | "spin", 990 | "untrusted", 991 | "web-sys", 992 | "winapi", 993 | ] 994 | 995 | [[package]] 996 | name = "rustc-demangle" 997 | version = "0.1.21" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 1000 | 1001 | [[package]] 1002 | name = "rustls" 1003 | version = "0.20.4" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" 1006 | dependencies = [ 1007 | "log", 1008 | "ring", 1009 | "sct", 1010 | "webpki", 1011 | ] 1012 | 1013 | [[package]] 1014 | name = "rustls-pemfile" 1015 | version = "0.2.1" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" 1018 | dependencies = [ 1019 | "base64", 1020 | ] 1021 | 1022 | [[package]] 1023 | name = "rustversion" 1024 | version = "1.0.6" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" 1027 | 1028 | [[package]] 1029 | name = "ryu" 1030 | version = "1.0.9" 1031 | source = "registry+https://github.com/rust-lang/crates.io-index" 1032 | checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" 1033 | 1034 | [[package]] 1035 | name = "schannel" 1036 | version = "0.1.19" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 1039 | dependencies = [ 1040 | "lazy_static", 1041 | "winapi", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "scopeguard" 1046 | version = "1.1.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1049 | 1050 | [[package]] 1051 | name = "sct" 1052 | version = "0.7.0" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" 1055 | dependencies = [ 1056 | "ring", 1057 | "untrusted", 1058 | ] 1059 | 1060 | [[package]] 1061 | name = "security-framework" 1062 | version = "2.6.1" 1063 | source = "registry+https://github.com/rust-lang/crates.io-index" 1064 | checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" 1065 | dependencies = [ 1066 | "bitflags", 1067 | "core-foundation", 1068 | "core-foundation-sys", 1069 | "libc", 1070 | "security-framework-sys", 1071 | ] 1072 | 1073 | [[package]] 1074 | name = "security-framework-sys" 1075 | version = "2.6.1" 1076 | source = "registry+https://github.com/rust-lang/crates.io-index" 1077 | checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" 1078 | dependencies = [ 1079 | "core-foundation-sys", 1080 | "libc", 1081 | ] 1082 | 1083 | [[package]] 1084 | name = "serde" 1085 | version = "1.0.136" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 1088 | dependencies = [ 1089 | "serde_derive", 1090 | ] 1091 | 1092 | [[package]] 1093 | name = "serde_derive" 1094 | version = "1.0.136" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" 1097 | dependencies = [ 1098 | "proc-macro2", 1099 | "quote", 1100 | "syn", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "serde_json" 1105 | version = "1.0.79" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" 1108 | dependencies = [ 1109 | "itoa", 1110 | "ryu", 1111 | "serde", 1112 | ] 1113 | 1114 | [[package]] 1115 | name = "serde_qs" 1116 | version = "0.4.6" 1117 | source = "registry+https://github.com/rust-lang/crates.io-index" 1118 | checksum = "35965fa1d2413717053d67c2df1f5c3e1763fbf77200ea7e767523707bd5a0af" 1119 | dependencies = [ 1120 | "data-encoding", 1121 | "error-chain", 1122 | "percent-encoding 1.0.1", 1123 | "serde", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "serde_urlencoded" 1128 | version = "0.7.1" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1131 | dependencies = [ 1132 | "form_urlencoded", 1133 | "itoa", 1134 | "ryu", 1135 | "serde", 1136 | ] 1137 | 1138 | [[package]] 1139 | name = "serde_with" 1140 | version = "1.12.0" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "ec1e6ec4d8950e5b1e894eac0d360742f3b1407a6078a604a731c4b3f49cefbc" 1143 | dependencies = [ 1144 | "rustversion", 1145 | "serde", 1146 | "serde_with_macros", 1147 | ] 1148 | 1149 | [[package]] 1150 | name = "serde_with_macros" 1151 | version = "1.5.1" 1152 | source = "registry+https://github.com/rust-lang/crates.io-index" 1153 | checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e" 1154 | dependencies = [ 1155 | "darling", 1156 | "proc-macro2", 1157 | "quote", 1158 | "syn", 1159 | ] 1160 | 1161 | [[package]] 1162 | name = "serde_yaml" 1163 | version = "0.8.23" 1164 | source = "registry+https://github.com/rust-lang/crates.io-index" 1165 | checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" 1166 | dependencies = [ 1167 | "indexmap", 1168 | "ryu", 1169 | "serde", 1170 | "yaml-rust", 1171 | ] 1172 | 1173 | [[package]] 1174 | name = "signal-hook-registry" 1175 | version = "1.4.0" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 1178 | dependencies = [ 1179 | "libc", 1180 | ] 1181 | 1182 | [[package]] 1183 | name = "slab" 1184 | version = "0.4.5" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" 1187 | 1188 | [[package]] 1189 | name = "smallvec" 1190 | version = "1.8.0" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" 1193 | 1194 | [[package]] 1195 | name = "socket2" 1196 | version = "0.4.4" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" 1199 | dependencies = [ 1200 | "libc", 1201 | "winapi", 1202 | ] 1203 | 1204 | [[package]] 1205 | name = "spin" 1206 | version = "0.5.2" 1207 | source = "registry+https://github.com/rust-lang/crates.io-index" 1208 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1209 | 1210 | [[package]] 1211 | name = "strsim" 1212 | version = "0.10.0" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1215 | 1216 | [[package]] 1217 | name = "syn" 1218 | version = "1.0.89" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54" 1221 | dependencies = [ 1222 | "proc-macro2", 1223 | "quote", 1224 | "unicode-xid", 1225 | ] 1226 | 1227 | [[package]] 1228 | name = "tempfile" 1229 | version = "3.3.0" 1230 | source = "registry+https://github.com/rust-lang/crates.io-index" 1231 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 1232 | dependencies = [ 1233 | "cfg-if 1.0.0", 1234 | "fastrand", 1235 | "libc", 1236 | "redox_syscall", 1237 | "remove_dir_all", 1238 | "winapi", 1239 | ] 1240 | 1241 | [[package]] 1242 | name = "termcolor" 1243 | version = "1.1.3" 1244 | source = "registry+https://github.com/rust-lang/crates.io-index" 1245 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 1246 | dependencies = [ 1247 | "winapi-util", 1248 | ] 1249 | 1250 | [[package]] 1251 | name = "textwrap" 1252 | version = "0.15.0" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" 1255 | 1256 | [[package]] 1257 | name = "time" 1258 | version = "0.1.43" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 1261 | dependencies = [ 1262 | "libc", 1263 | "winapi", 1264 | ] 1265 | 1266 | [[package]] 1267 | name = "tinyvec" 1268 | version = "1.5.1" 1269 | source = "registry+https://github.com/rust-lang/crates.io-index" 1270 | checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" 1271 | dependencies = [ 1272 | "tinyvec_macros", 1273 | ] 1274 | 1275 | [[package]] 1276 | name = "tinyvec_macros" 1277 | version = "0.1.0" 1278 | source = "registry+https://github.com/rust-lang/crates.io-index" 1279 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1280 | 1281 | [[package]] 1282 | name = "tokio" 1283 | version = "1.17.0" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" 1286 | dependencies = [ 1287 | "bytes", 1288 | "libc", 1289 | "memchr", 1290 | "mio", 1291 | "num_cpus", 1292 | "once_cell", 1293 | "parking_lot", 1294 | "pin-project-lite", 1295 | "signal-hook-registry", 1296 | "socket2", 1297 | "tokio-macros", 1298 | "winapi", 1299 | ] 1300 | 1301 | [[package]] 1302 | name = "tokio-macros" 1303 | version = "1.7.0" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" 1306 | dependencies = [ 1307 | "proc-macro2", 1308 | "quote", 1309 | "syn", 1310 | ] 1311 | 1312 | [[package]] 1313 | name = "tokio-native-tls" 1314 | version = "0.3.0" 1315 | source = "registry+https://github.com/rust-lang/crates.io-index" 1316 | checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 1317 | dependencies = [ 1318 | "native-tls", 1319 | "tokio", 1320 | ] 1321 | 1322 | [[package]] 1323 | name = "tokio-util" 1324 | version = "0.6.9" 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" 1326 | checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" 1327 | dependencies = [ 1328 | "bytes", 1329 | "futures-core", 1330 | "futures-sink", 1331 | "log", 1332 | "pin-project-lite", 1333 | "tokio", 1334 | ] 1335 | 1336 | [[package]] 1337 | name = "tower-service" 1338 | version = "0.3.1" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 1341 | 1342 | [[package]] 1343 | name = "tracing" 1344 | version = "0.1.32" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" 1347 | dependencies = [ 1348 | "cfg-if 1.0.0", 1349 | "pin-project-lite", 1350 | "tracing-core", 1351 | ] 1352 | 1353 | [[package]] 1354 | name = "tracing-core" 1355 | version = "0.1.23" 1356 | source = "registry+https://github.com/rust-lang/crates.io-index" 1357 | checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" 1358 | dependencies = [ 1359 | "lazy_static", 1360 | ] 1361 | 1362 | [[package]] 1363 | name = "try-lock" 1364 | version = "0.2.3" 1365 | source = "registry+https://github.com/rust-lang/crates.io-index" 1366 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1367 | 1368 | [[package]] 1369 | name = "unicode-bidi" 1370 | version = "0.3.7" 1371 | source = "registry+https://github.com/rust-lang/crates.io-index" 1372 | checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" 1373 | 1374 | [[package]] 1375 | name = "unicode-normalization" 1376 | version = "0.1.19" 1377 | source = "registry+https://github.com/rust-lang/crates.io-index" 1378 | checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" 1379 | dependencies = [ 1380 | "tinyvec", 1381 | ] 1382 | 1383 | [[package]] 1384 | name = "unicode-xid" 1385 | version = "0.2.2" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1388 | 1389 | [[package]] 1390 | name = "untrusted" 1391 | version = "0.7.1" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 1394 | 1395 | [[package]] 1396 | name = "url" 1397 | version = "2.2.2" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 1400 | dependencies = [ 1401 | "form_urlencoded", 1402 | "idna", 1403 | "matches", 1404 | "percent-encoding 2.1.0", 1405 | ] 1406 | 1407 | [[package]] 1408 | name = "uuid" 1409 | version = "0.8.2" 1410 | source = "registry+https://github.com/rust-lang/crates.io-index" 1411 | checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" 1412 | dependencies = [ 1413 | "getrandom", 1414 | "serde", 1415 | ] 1416 | 1417 | [[package]] 1418 | name = "vcpkg" 1419 | version = "0.2.15" 1420 | source = "registry+https://github.com/rust-lang/crates.io-index" 1421 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1422 | 1423 | [[package]] 1424 | name = "version_check" 1425 | version = "0.9.4" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1428 | 1429 | [[package]] 1430 | name = "want" 1431 | version = "0.3.0" 1432 | source = "registry+https://github.com/rust-lang/crates.io-index" 1433 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1434 | dependencies = [ 1435 | "log", 1436 | "try-lock", 1437 | ] 1438 | 1439 | [[package]] 1440 | name = "wasi" 1441 | version = "0.10.2+wasi-snapshot-preview1" 1442 | source = "registry+https://github.com/rust-lang/crates.io-index" 1443 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1444 | 1445 | [[package]] 1446 | name = "wasi" 1447 | version = "0.11.0+wasi-snapshot-preview1" 1448 | source = "registry+https://github.com/rust-lang/crates.io-index" 1449 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1450 | 1451 | [[package]] 1452 | name = "wasm-bindgen" 1453 | version = "0.2.79" 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" 1455 | checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" 1456 | dependencies = [ 1457 | "cfg-if 1.0.0", 1458 | "wasm-bindgen-macro", 1459 | ] 1460 | 1461 | [[package]] 1462 | name = "wasm-bindgen-backend" 1463 | version = "0.2.79" 1464 | source = "registry+https://github.com/rust-lang/crates.io-index" 1465 | checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" 1466 | dependencies = [ 1467 | "bumpalo", 1468 | "lazy_static", 1469 | "log", 1470 | "proc-macro2", 1471 | "quote", 1472 | "syn", 1473 | "wasm-bindgen-shared", 1474 | ] 1475 | 1476 | [[package]] 1477 | name = "wasm-bindgen-futures" 1478 | version = "0.4.29" 1479 | source = "registry+https://github.com/rust-lang/crates.io-index" 1480 | checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" 1481 | dependencies = [ 1482 | "cfg-if 1.0.0", 1483 | "js-sys", 1484 | "wasm-bindgen", 1485 | "web-sys", 1486 | ] 1487 | 1488 | [[package]] 1489 | name = "wasm-bindgen-macro" 1490 | version = "0.2.79" 1491 | source = "registry+https://github.com/rust-lang/crates.io-index" 1492 | checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" 1493 | dependencies = [ 1494 | "quote", 1495 | "wasm-bindgen-macro-support", 1496 | ] 1497 | 1498 | [[package]] 1499 | name = "wasm-bindgen-macro-support" 1500 | version = "0.2.79" 1501 | source = "registry+https://github.com/rust-lang/crates.io-index" 1502 | checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" 1503 | dependencies = [ 1504 | "proc-macro2", 1505 | "quote", 1506 | "syn", 1507 | "wasm-bindgen-backend", 1508 | "wasm-bindgen-shared", 1509 | ] 1510 | 1511 | [[package]] 1512 | name = "wasm-bindgen-shared" 1513 | version = "0.2.79" 1514 | source = "registry+https://github.com/rust-lang/crates.io-index" 1515 | checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" 1516 | 1517 | [[package]] 1518 | name = "web-sys" 1519 | version = "0.3.56" 1520 | source = "registry+https://github.com/rust-lang/crates.io-index" 1521 | checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" 1522 | dependencies = [ 1523 | "js-sys", 1524 | "wasm-bindgen", 1525 | ] 1526 | 1527 | [[package]] 1528 | name = "webpki" 1529 | version = "0.22.0" 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" 1531 | checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" 1532 | dependencies = [ 1533 | "ring", 1534 | "untrusted", 1535 | ] 1536 | 1537 | [[package]] 1538 | name = "webpki-roots" 1539 | version = "0.22.2" 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" 1541 | checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" 1542 | dependencies = [ 1543 | "webpki", 1544 | ] 1545 | 1546 | [[package]] 1547 | name = "winapi" 1548 | version = "0.3.9" 1549 | source = "registry+https://github.com/rust-lang/crates.io-index" 1550 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1551 | dependencies = [ 1552 | "winapi-i686-pc-windows-gnu", 1553 | "winapi-x86_64-pc-windows-gnu", 1554 | ] 1555 | 1556 | [[package]] 1557 | name = "winapi-i686-pc-windows-gnu" 1558 | version = "0.4.0" 1559 | source = "registry+https://github.com/rust-lang/crates.io-index" 1560 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1561 | 1562 | [[package]] 1563 | name = "winapi-util" 1564 | version = "0.1.5" 1565 | source = "registry+https://github.com/rust-lang/crates.io-index" 1566 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1567 | dependencies = [ 1568 | "winapi", 1569 | ] 1570 | 1571 | [[package]] 1572 | name = "winapi-x86_64-pc-windows-gnu" 1573 | version = "0.4.0" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1576 | 1577 | [[package]] 1578 | name = "windows-sys" 1579 | version = "0.32.0" 1580 | source = "registry+https://github.com/rust-lang/crates.io-index" 1581 | checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" 1582 | dependencies = [ 1583 | "windows_aarch64_msvc", 1584 | "windows_i686_gnu", 1585 | "windows_i686_msvc", 1586 | "windows_x86_64_gnu", 1587 | "windows_x86_64_msvc", 1588 | ] 1589 | 1590 | [[package]] 1591 | name = "windows_aarch64_msvc" 1592 | version = "0.32.0" 1593 | source = "registry+https://github.com/rust-lang/crates.io-index" 1594 | checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" 1595 | 1596 | [[package]] 1597 | name = "windows_i686_gnu" 1598 | version = "0.32.0" 1599 | source = "registry+https://github.com/rust-lang/crates.io-index" 1600 | checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" 1601 | 1602 | [[package]] 1603 | name = "windows_i686_msvc" 1604 | version = "0.32.0" 1605 | source = "registry+https://github.com/rust-lang/crates.io-index" 1606 | checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" 1607 | 1608 | [[package]] 1609 | name = "windows_x86_64_gnu" 1610 | version = "0.32.0" 1611 | source = "registry+https://github.com/rust-lang/crates.io-index" 1612 | checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" 1613 | 1614 | [[package]] 1615 | name = "windows_x86_64_msvc" 1616 | version = "0.32.0" 1617 | source = "registry+https://github.com/rust-lang/crates.io-index" 1618 | checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" 1619 | 1620 | [[package]] 1621 | name = "winreg" 1622 | version = "0.10.1" 1623 | source = "registry+https://github.com/rust-lang/crates.io-index" 1624 | checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" 1625 | dependencies = [ 1626 | "winapi", 1627 | ] 1628 | 1629 | [[package]] 1630 | name = "yaml-rust" 1631 | version = "0.4.5" 1632 | source = "registry+https://github.com/rust-lang/crates.io-index" 1633 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 1634 | dependencies = [ 1635 | "linked-hash-map", 1636 | ] 1637 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cfddns" 3 | version = "1.6.2" 4 | edition = "2021" 5 | license-file = "LICENSE" 6 | homepage = "https://github.com/uetchy/cfddns" 7 | description = "Yet another DDNS client for Cloudflare" 8 | authors = ["Yasuaki Uechi "] 9 | 10 | [dependencies] 11 | anyhow = "1.0.56" 12 | tokio = { version = "1.17.0", features = ["full"] } 13 | serde = { version = "1.0" } 14 | serde_json = "1.0" 15 | serde_yaml = "0.8" 16 | once_cell = "1.10" 17 | regex = "1.5.5" 18 | reqwest = { version = "0.11.10" } 19 | clap = { version = "3.1.6", features = ["derive"] } 20 | cloudflare = "0.9.1" 21 | lettre = { version = "0.10.0-rc.4", features = [ 22 | "builder", 23 | "smtp-transport", 24 | "rustls-tls", 25 | ] } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yasuaki Uechi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 21 | OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cfddns 2 | 3 | Yet another DDNS client for Cloudflare written in Rust. 4 | 5 | [![Packaging status](https://repology.org/badge/vertical-allrepos/cfddns.svg)](https://repology.org/project/cfddns/versions) 6 | 7 | ## Usage 8 | 9 | ``` 10 | cfddns -c 11 | ``` 12 | 13 | ```bash 14 | cat << EOD > domains 15 | example.com 16 | mail.example.com 17 | example.org 18 | EOD 19 | 20 | cat < cfddns.yml 21 | token: "" 22 | interval: 900 # in seconds (optional) 23 | endpoint: "https://api.ipify.org" # external ip provider (optional) 24 | EOD 25 | 26 | cfddns -c cfddns.yml domains 27 | ``` 28 | 29 | ## Install 30 | 31 | ### Arch Linux 32 | 33 | Install `cfddns` from [AUR](https://aur.archlinux.org/packages/cfddns/). 34 | 35 | ```bash 36 | yay -S cfddns 37 | ``` 38 | 39 | ```bash 40 | vim /etc/cfddns/cfddns.yml # replace `token` value with yours 41 | vim /etc/cfddns/domains 42 | 43 | systemctl enable --now cfddns 44 | ``` 45 | 46 | ### Cargo 47 | 48 | ``` 49 | cargo install cfddns 50 | ``` 51 | 52 | ### Build from source 53 | 54 | ```bash 55 | git clone https://github.com/uetchy/cfddns.git && cd cfddns 56 | cargo build --release 57 | cp target/release/cfddns /usr/local/bin 58 | ``` 59 | 60 | ## Contribute 61 | 62 | ### Tasks 63 | 64 | - Report a bug 65 | - Create and maintain `cfddns` package for your favorite Linux distribution 66 | -------------------------------------------------------------------------------- /etc/cfddns.yml: -------------------------------------------------------------------------------- 1 | token: "" # required 2 | # interval: 600 # in seconds (optional) 3 | # endpoint: "https://api.ipify.org" # external ip provider (optional) 4 | # notification: 5 | # enabled: true # default: false 6 | # from: cfddns@localhost # default: undefined 7 | # to: admin@example.com # default: undefined 8 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | use std::net::Ipv4Addr; 3 | use std::path::PathBuf; 4 | use std::str::FromStr; 5 | use std::time::Duration; 6 | 7 | use anyhow::{anyhow, bail, Result}; 8 | use clap::Parser; 9 | use cloudflare::endpoints::dns::{DnsContent, DnsRecord}; 10 | use cloudflare::endpoints::{dns, zone}; 11 | use cloudflare::framework::{ 12 | async_api::{ApiClient, Client}, 13 | auth::Credentials, 14 | Environment, HttpApiClientConfig, OrderDirection, 15 | }; 16 | use lettre::transport::smtp; 17 | use lettre::{Message, SmtpTransport, Transport}; 18 | use serde::{Deserialize, Serialize}; 19 | use serde_yaml; 20 | use tokio::{task, time}; 21 | 22 | // stolen from https://docs.rs/once_cell/latest/once_cell/index.html 23 | // MIT @ Aleksey Kladov 24 | macro_rules! regex { 25 | ($re:literal $(,)?) => {{ 26 | static RE: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); 27 | RE.get_or_init(|| regex::Regex::new($re).unwrap()) 28 | }}; 29 | } 30 | 31 | #[derive(Debug, Serialize, Deserialize)] 32 | struct NotificationConfig { 33 | /// Enable email notification 34 | enabled: bool, 35 | 36 | /// Sender address 37 | from: String, 38 | 39 | /// Recipient address 40 | to: String, 41 | } 42 | 43 | #[derive(Debug, Serialize, Deserialize)] 44 | struct Config { 45 | /// Cloudflare token (required) 46 | token: String, 47 | 48 | /// Update interval in seconds (default: 600) 49 | interval: Option, 50 | 51 | /// External IP address provider (default: "https://api.ipify.org") 52 | endpoint: Option, 53 | 54 | /// Email notification config (default: undefined) 55 | notification: Option, 56 | } 57 | 58 | #[derive(Parser, Debug)] 59 | #[clap( 60 | author, 61 | version, 62 | about, 63 | long_about = "Yet another DDNS client for Cloudflare 64 | [MANUAL] https://github.com/uetchy/cfddns#readme" 65 | )] 66 | struct Args { 67 | /// Path to domain list file 68 | domains: PathBuf, 69 | 70 | /// Path to config file 71 | #[clap(short, long)] 72 | config: PathBuf, 73 | } 74 | 75 | #[derive(Debug)] 76 | pub struct MuxWriter { 77 | pub buf: Vec, 78 | pub should_notify: bool, 79 | } 80 | 81 | impl MuxWriter { 82 | pub fn new() -> Self { 83 | Self { 84 | buf: vec![], 85 | should_notify: false, 86 | } 87 | } 88 | 89 | pub fn mark(&mut self) { 90 | self.should_notify = true; 91 | } 92 | 93 | pub fn write(&mut self, data: String) { 94 | println!("{}", data); 95 | self.buf.push(data); 96 | } 97 | 98 | pub fn drain(&mut self) -> (String, bool) { 99 | let result = self.buf.join("\n").clone(); 100 | let should_notify = self.should_notify; 101 | self.buf.clear(); 102 | self.should_notify = false; 103 | (result, should_notify) 104 | } 105 | } 106 | 107 | #[tokio::main] 108 | async fn main() -> Result<()> { 109 | // parse args 110 | let args = Args::parse(); 111 | let config_path = args.config; 112 | let domain_list_path = args.domains; 113 | 114 | // load config 115 | let config = load_config(&config_path)?; 116 | let token = config.token.clone(); 117 | let interval = config.interval.unwrap_or(600); 118 | let endpoint = config 119 | .endpoint 120 | .clone() 121 | .unwrap_or_else(|| "https://api.ipify.org".to_string()); 122 | 123 | // instantiate cloudflare client 124 | let credentials: Credentials = Credentials::UserAuthToken { token }; 125 | let api_client = Client::new( 126 | credentials, 127 | HttpApiClientConfig::default(), 128 | Environment::Production, 129 | )?; 130 | 131 | // preload zone list 132 | let mut zone_name_id_map: HashMap = HashMap::new(); 133 | let zones = list_zones(&api_client).await?; 134 | for zone in zones { 135 | zone_name_id_map.insert(zone.name, zone.id); 136 | } 137 | 138 | // load domain list 139 | let domain_list = load_domain_list(&domain_list_path)? 140 | .iter() 141 | .map(|x| { 142 | let (fqdn, zone_name) = split_hostname(x).unwrap(); 143 | (fqdn, zone_name_id_map.get(&zone_name).unwrap().to_owned()) 144 | }) 145 | .collect::>(); 146 | 147 | let mut writer = MuxWriter::new(); 148 | 149 | let forever = task::spawn(async move { 150 | let mut interval = time::interval(Duration::from_secs(interval)); 151 | 152 | loop { 153 | interval.tick().await; 154 | 155 | println!("Started checking DNS records..."); 156 | 157 | let (response, should_notify): (String, bool) = 158 | match populate_ips(&domain_list, &api_client, &endpoint, &mut writer).await { 159 | Ok(()) => writer.drain(), 160 | Err(err) => (format!("{}", err), true), 161 | }; 162 | 163 | // notify the result 164 | if let Some(nc) = &config.notification { 165 | if nc.enabled && should_notify { 166 | println!("Sending an email with config: {:?}", nc); 167 | match send_mail(&nc.from, &nc.to, "cfddns", &response) { 168 | Ok(_) => {} 169 | Err(err) => println!("{}", err), 170 | } 171 | } 172 | } 173 | } 174 | }); 175 | 176 | forever.await.unwrap(); 177 | 178 | Ok(()) 179 | } 180 | 181 | async fn populate_ips( 182 | domain_list: &Vec<(String, String)>, 183 | api_client: &ApiClientType, 184 | endpoint: &str, 185 | writer: &mut MuxWriter, 186 | ) -> Result<()> { 187 | let global_ipv4_addr = get_global_ipv4_addr(&endpoint).await?; 188 | writer.write(format!("Current IP address: {}", global_ipv4_addr)); 189 | 190 | // Build zone id cache 191 | let unique_zone_ids: HashSet = domain_list.iter().map(|x| x.1.clone()).collect(); 192 | let mut fqdn_record_cache: HashMap = HashMap::new(); 193 | for zone_id in unique_zone_ids { 194 | let records = list_dns_records(&zone_id, api_client).await?; 195 | for record in records.into_iter().filter(|x| match x.content { 196 | DnsContent::A { content: _ } => true, 197 | _ => false, 198 | }) { 199 | fqdn_record_cache.insert(record.name.clone(), record); 200 | } 201 | } 202 | 203 | for (fqdn, zone_id) in domain_list { 204 | let record = fqdn_record_cache 205 | .iter() 206 | .find(|x| x.0.eq(fqdn)) 207 | .and_then(|x| Some(x.1)); 208 | 209 | if let Some(record) = record { 210 | let record_ip = match record.content { 211 | DnsContent::A { content: ip } => ip, 212 | _ => bail!("Invalid content type"), 213 | }; 214 | 215 | if record_ip.ne(&global_ipv4_addr) { 216 | writer.mark(); 217 | writer.write(format!( 218 | "Updating A record for {}: {} -> {}", 219 | fqdn, record_ip, global_ipv4_addr 220 | )); 221 | update_dns_record( 222 | zone_id, 223 | &record.id, 224 | dns::UpdateDnsRecordParams { 225 | ttl: Some(1), 226 | proxied: Some(false), 227 | name: fqdn, 228 | content: dns::DnsContent::A { 229 | content: global_ipv4_addr, 230 | }, 231 | }, 232 | api_client, 233 | ) 234 | .await?; 235 | } else { 236 | writer.write(format!("Unchanged {} ({})", fqdn, record_ip)); 237 | } 238 | } else { 239 | writer.mark(); 240 | writer.write(format!( 241 | "Creating A record for {} ({})", 242 | fqdn, global_ipv4_addr 243 | )); 244 | create_dns_record( 245 | zone_id, 246 | dns::CreateDnsRecordParams { 247 | ttl: Some(1), 248 | priority: None, 249 | proxied: Some(false), 250 | name: fqdn, 251 | content: dns::DnsContent::A { 252 | content: global_ipv4_addr, 253 | }, 254 | }, 255 | api_client, 256 | ) 257 | .await?; 258 | } 259 | } 260 | 261 | Ok(()) 262 | } 263 | 264 | fn load_config(config_path: &PathBuf) -> Result { 265 | let f = std::fs::File::open(config_path)?; 266 | let config: Config = serde_yaml::from_reader(f)?; 267 | Ok(config) 268 | } 269 | 270 | /// returns `(fqdn, zone)` 271 | fn split_hostname(name: &str) -> Option<(String, String)> { 272 | let re = regex!(r"^(?:[^.]+\.)*?((?:[^.]+\.?){2})$"); 273 | let cap = re.captures(name).and_then(|cap| { 274 | let fqdn = cap.get(0)?.as_str().to_owned(); 275 | let zone = cap.get(1)?.as_str().to_owned(); 276 | Some((fqdn, zone)) 277 | }); 278 | cap 279 | } 280 | 281 | async fn get_global_ipv4_addr(endpoint: &str) -> Result { 282 | let body = reqwest::get(endpoint).await?.text().await?; 283 | match Ipv4Addr::from_str(&body) { 284 | Ok(res) => Ok(res), 285 | Err(err) => bail!(err), 286 | } 287 | } 288 | 289 | fn load_domain_list(path: &PathBuf) -> Result> { 290 | let data = std::fs::read_to_string(path)?; 291 | let list = data 292 | .split("\n") 293 | .filter(|s| !s.is_empty()) 294 | .map(String::from) 295 | .collect::>(); 296 | Ok(list) 297 | } 298 | 299 | async fn list_zones( 300 | api_client: &ApiClientType, 301 | ) -> Result> { 302 | let response = api_client 303 | .request(&zone::ListZones { 304 | params: zone::ListZonesParams { 305 | ..Default::default() 306 | }, 307 | }) 308 | .await; 309 | match response { 310 | Ok(res) => Ok(res.result), 311 | Err(err) => bail!(err), 312 | } 313 | } 314 | 315 | async fn list_dns_records( 316 | zone_identifier: &str, 317 | api_client: &ApiClientType, 318 | ) -> Result> { 319 | let response = api_client 320 | .request(&dns::ListDnsRecords { 321 | zone_identifier, 322 | params: dns::ListDnsRecordsParams { 323 | direction: Some(OrderDirection::Ascending), 324 | ..Default::default() 325 | }, 326 | }) 327 | .await; 328 | match response { 329 | Ok(res) => Ok(res.result), 330 | Err(err) => bail!(err), 331 | } 332 | } 333 | 334 | async fn create_dns_record<'a, ApiClientType: ApiClient>( 335 | zone_identifier: &str, 336 | params: dns::CreateDnsRecordParams<'a>, 337 | api_client: &ApiClientType, 338 | ) -> Result { 339 | let response = api_client 340 | .request(&dns::CreateDnsRecord { 341 | zone_identifier, 342 | params, 343 | }) 344 | .await; 345 | match response { 346 | Ok(res) => Ok(res.result), 347 | Err(err) => bail!(err), 348 | } 349 | } 350 | 351 | async fn update_dns_record<'a, ApiClientType: ApiClient>( 352 | zone_identifier: &str, 353 | identifier: &str, 354 | params: dns::UpdateDnsRecordParams<'a>, 355 | api_client: &ApiClientType, 356 | ) -> Result { 357 | let response = api_client 358 | .request(&dns::UpdateDnsRecord { 359 | zone_identifier, 360 | identifier, 361 | params, 362 | }) 363 | .await; 364 | match response { 365 | Ok(res) => Ok(res.result), 366 | Err(err) => bail!(err), 367 | } 368 | } 369 | 370 | fn send_mail(from: &str, to: &str, subject: &str, body: &str) -> Result { 371 | let email = Message::builder() 372 | .from(from.parse()?) 373 | .to(to.parse()?) 374 | .subject(subject) 375 | .body(body.to_owned())?; 376 | 377 | // Open a local connection on port 25 and send the email 378 | let mailer = SmtpTransport::unencrypted_localhost(); 379 | 380 | mailer 381 | .send(&email) 382 | .map_err(|e| anyhow!("Failed to send email: {}", e)) 383 | } 384 | --------------------------------------------------------------------------------