mirror of
https://codeberg.org/privacy1st/netcup-dns
synced 2024-12-22 23:36:04 +01:00
init
This commit is contained in:
commit
bc17a5d458
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/.idea/
|
||||||
|
/cfg/
|
||||||
|
/venv/
|
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# netcup DNS
|
||||||
|
|
||||||
|
Update DNS records with your current external IP address using the netcup DNS API.
|
||||||
|
|
||||||
|
## TODOs
|
||||||
|
|
||||||
|
Alternative external IP detection:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def external_ip_upnp():
|
||||||
|
"""
|
||||||
|
https://stackoverflow.com/a/41385033
|
||||||
|
|
||||||
|
Didn't work for me. Even after double checking fritz.box settings:
|
||||||
|
|
||||||
|
fritz.box > Heimnetz > Netzwerk > Statusinformationen über UPnP übertragen
|
||||||
|
"""
|
||||||
|
import miniupnpc
|
||||||
|
u = miniupnpc.UPnP()
|
||||||
|
u.discoverdelay = 1000
|
||||||
|
u.discover()
|
||||||
|
u.selectigd()
|
||||||
|
print('external ip address: {}'.format(u.externalipaddress()))
|
||||||
|
```
|
88
main.py
Normal file
88
main.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import configparser
|
||||||
|
import ipaddress
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from nc_dnsapi import Client, DNSRecord
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
https://github.com/nbuchwitz/nc_dnsapi
|
||||||
|
"""
|
||||||
|
destination = external_ipv4()
|
||||||
|
|
||||||
|
files = [file for file in Path('cfg').iterdir() if file.name.endswith('.cfg') and file.is_file()]
|
||||||
|
for file in files:
|
||||||
|
cfg = configparser.ConfigParser()
|
||||||
|
cfg.read(file)
|
||||||
|
customer = cfg['credentials']['customer']
|
||||||
|
api_key = cfg['credentials']['api_key']
|
||||||
|
api_password = cfg['credentials']['api_password']
|
||||||
|
|
||||||
|
domains = [section for section in cfg.sections() if section != 'credentials']
|
||||||
|
|
||||||
|
with Client(customer, api_key, api_password) as api:
|
||||||
|
for domain in domains:
|
||||||
|
hostname = cfg[domain]['hostname']
|
||||||
|
type_ = cfg[domain]['type']
|
||||||
|
update_record_destination(api, domain, hostname, type_, destination)
|
||||||
|
|
||||||
|
|
||||||
|
def update_record_destination(api: Client, domain: str, hostname: str, type_: str, destination: str) -> None:
|
||||||
|
record = get_record(api, domain, hostname, type_)
|
||||||
|
record.destination = destination
|
||||||
|
api.update_dns_record(domain, record)
|
||||||
|
|
||||||
|
|
||||||
|
def get_record(api: Client, domain: str, hostname: str, type_: str) -> DNSRecord:
|
||||||
|
records: list[DNSRecord] = api.dns_records(domain)
|
||||||
|
record: DNSRecord
|
||||||
|
|
||||||
|
matches = [record for record in records if record.hostname == hostname and record.type == type_]
|
||||||
|
if len(matches) != 1:
|
||||||
|
raise Exception(f'Expected one DNSRecord for {hostname}.{domain}, but got {len(matches)}')
|
||||||
|
return matches[0]
|
||||||
|
|
||||||
|
|
||||||
|
def external_ipv4() -> str:
|
||||||
|
"""
|
||||||
|
:return: Public IPv4 address
|
||||||
|
"""
|
||||||
|
|
||||||
|
# IPv4 only.
|
||||||
|
endpoints = ['https://checkipv4.dedyn.io/', 'https://api.ipify.org', 'https://v4.ident.me/']
|
||||||
|
# Not sure if they return IPv4 addresses only,
|
||||||
|
# so we try these endpoints last.
|
||||||
|
endpoints += ['https://ipinfo.io/ip']
|
||||||
|
|
||||||
|
for endpoint in endpoints:
|
||||||
|
backup = None
|
||||||
|
try:
|
||||||
|
# Force the usage of IPv4
|
||||||
|
# https://stackoverflow.com/a/72440253/6334421
|
||||||
|
#
|
||||||
|
# Alternatively, use urllib3: https://stackoverflow.com/a/46972341/6334421
|
||||||
|
backup = requests.packages.urllib3.util.connection.HAS_IPV6
|
||||||
|
requests.packages.urllib3.util.connection.HAS_IPV6 = False
|
||||||
|
|
||||||
|
# Timeout after 5 seconds.
|
||||||
|
ip = requests.get(endpoint, timeout=5).text.strip()
|
||||||
|
# Check if it is actually an IPv4 address.
|
||||||
|
# Some services, such as e.g. v4.ident.me, sometimes return IPv6.
|
||||||
|
ipv4 = ipaddress.ip_address(ip)
|
||||||
|
if not isinstance(ipv4, ipaddress.IPv4Address):
|
||||||
|
continue
|
||||||
|
# Return ip address as string.
|
||||||
|
return ipv4.exploded
|
||||||
|
except Exception as _e:
|
||||||
|
continue
|
||||||
|
finally:
|
||||||
|
# Allow usage of IPv6 again.
|
||||||
|
if backup is not None:
|
||||||
|
requests.packages.urllib3.util.connection.HAS_IPV6 = backup
|
||||||
|
raise Exception('Could not determine public IPv4 address.')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
nc-dnsapi
|
Loading…
Reference in New Issue
Block a user