Application to use your DigitalOcean account/service as a dynamic DNS service. https://gitlab.no/rune/DO_DynDNS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
9.1 KiB

  1. #!/usr/bin/python3
  2. import urllib.request
  3. import configparser
  4. import json
  5. import logging
  6. import argparse
  7. import requests
  8. import os
  9. import time
  10. import sys
  11. import PySimpleGUI as sg
  12. # Config stuff
  13. # filepath = os.path.dirname(os.path.abspath(__file__))
  14. linuxfilepath = '~/.config/dyndns/'
  15. filepath = os.path.expanduser(linuxfilepath)
  16. config = configparser.ConfigParser()
  17. def addemptyconfig():
  18. if not os.path.exists(filepath):
  19. os.makedirs(filepath)
  20. if not os.path.exists(filepath+'dyndns.log'):
  21. open(filepath+'dyndns.log', 'a').close()
  22. section = '[DYNDNS]'
  23. section = section.strip("[']")
  24. config[section] = {
  25. 'api_key': '<API KEY> from Digital Ocean at https://cloud.digitalocean.com/api_access',
  26. 'baseuri': 'example.tld',
  27. 'logfile': 'dyndns.log'
  28. }
  29. with open(filepath+'config.ini', 'w') as f:
  30. config.write(f)
  31. try:
  32. config.read(filepath+'config.ini')
  33. api = config['DYNDNS']['api_key']
  34. domain_name = config['DYNDNS']['baseuri']
  35. if not os.path.exists(filepath+config['DYNDNS']['logfile']):
  36. open(filepath+config['DYNDNS']['logfile'], 'a').close()
  37. logging.basicConfig(filename=filepath+config['DYNDNS']['logfile'], level=logging.INFO)
  38. except KeyError:
  39. addemptyconfig()
  40. logging.basicConfig(filename=filepath+'dyndns.log', level=logging.INFO)
  41. logging.error(time.strftime("%Y-%m-%d %H:%M") + ' Missing config.ini. Added to '+filepath)
  42. sg.popup('Woops!\n\nMissing config.ini file!\n\n'
  43. 'An empty file has been created in' + filepath + ' as config.ini\n\n'
  44. 'please edit this file before starting the application again!',
  45. title='About Digital Ocean DynDNS - v0.1', grab_anywhere=True,font=(any, 12))
  46. sys.exit()
  47. local_ip4 = urllib.request.urlopen("http://ip4.iurl.no").read().decode('utf-8')
  48. local_ip4_nice = local_ip4 + ' '
  49. # The program
  50. def refreshlocalip():
  51. return urllib.request.urlopen("http://ip4.iurl.no").read().decode('utf-8')
  52. def refresh_domainnames_from_file():
  53. config = None
  54. config = configparser.ConfigParser()
  55. config.read(filepath + 'config.ini')
  56. api = config['DYNDNS']['api_key']
  57. domain_name = config['DYNDNS']['baseuri']
  58. names = []
  59. for section in config.sections():
  60. if not config.has_option(section, 'subdomainid'):
  61. continue
  62. for name in config.items(section):
  63. names.append(section)
  64. return names
  65. def domainnames():
  66. names = []
  67. for section in config.sections():
  68. if not config.has_option(section, 'subdomainid'):
  69. continue
  70. for name in config.items(section):
  71. names.append(section)
  72. return names
  73. def updateip(ip, domainname):
  74. current = config.items(section=domainname)
  75. domain_id = current[0][1]
  76. data = {'data': ip}
  77. headers = {'Authorization': 'Bearer ' + api, "Content-Type": "application/json"}
  78. response = requests.put('https://api.digitalocean.com/v2/domains/'+domain_name+'/records/' + domain_id, data=json.dumps(data), headers=headers)
  79. if str(response) == '<Response [200]>':
  80. result = [True, response]
  81. logging.info(time.strftime("%Y-%m-%d %H:%M") + ' - Success! Domain %s updated with IP: %s ',
  82. domainname, local_ip4)
  83. else:
  84. result = [False, response]
  85. logging.error('Failure! ' + str(response))
  86. return result
  87. def getremoteip():
  88. count = 0
  89. for section in config.sections():
  90. if not config.has_option(section, 'subdomainid'):
  91. continue
  92. for name, value in config.items(section):
  93. count = count + 1
  94. req = urllib.request.Request('https://api.digitalocean.com/v2/domains/' + domain_name + '/records/' +
  95. value)
  96. req.add_header('Content-Type', 'application/json')
  97. req.add_header('Authorization', 'Bearer ' + api)
  98. current = urllib.request.urlopen(req)
  99. remote = current.read().decode('utf-8')
  100. remoteData = json.loads(remote)
  101. remoteIP4 = remoteData['domain_record']['data']
  102. if count > 0:
  103. break
  104. return remoteIP4
  105. def createnewdomain(name, ip):
  106. data = {'name': name,
  107. 'data': ip,
  108. 'type': "A",
  109. 'ttl': 3600
  110. }
  111. headers = {'Authorization': 'Bearer ' + api, "Content-Type": "application/json"}
  112. response = requests.post('https://api.digitalocean.com/v2/domains/' + domain_name + '/records',
  113. data=json.dumps(data), headers=headers)
  114. if str(response) == '<Response [201]>':
  115. return response.json()
  116. else:
  117. return 'Fail'
  118. def list_all_domains_to_array():
  119. LocalDonains = []
  120. domains = []
  121. existing_in_ini = domainnames()
  122. for section in config.sections():
  123. if not config.has_option(section, 'subdomainid'):
  124. continue
  125. for subdomainid, value in config.items(section, 'subdomainid'):
  126. LocalDonains.append(value)
  127. req = urllib.request.Request('https://api.digitalocean.com/v2/domains/' + domain_name + '/records/')
  128. req.add_header('Content-Type', 'application/json')
  129. req.add_header('Authorization', 'Bearer ' + api)
  130. current = urllib.request.urlopen(req)
  131. remote = current.read().decode('utf-8')
  132. remoteData = json.loads(remote)
  133. domains.append('Domain\tID\t\tDynDNS\t\tIP\n')
  134. for k in remoteData["domain_records"]:
  135. if k['name'] != '@':
  136. if str(k['data']) == local_ip4 or k['name'] in existing_in_ini:
  137. if str(k['data']) != local_ip4:
  138. domains.append(k['name'] + '\t' + str(k['id']) + '\tYes\t\t' + k['data'] + ' Check IP!\n')
  139. else:
  140. domains.append(k['name'] + '\t' + str(k['id']) + '\tYes\t\t' + k['data'] + '\n')
  141. else:
  142. domains.append(k['name'] + '\t' + str(k['id']) + '\tNo\t\t' + k['data'] + '\n')
  143. return domains
  144. def list_domains_to_array():
  145. domain_names = []
  146. domain_ids = []
  147. for section in config.sections():
  148. if not config.has_option(section, 'subdomainid'):
  149. continue
  150. # for subdomainid, value in config.items(section, 'subdomainid'):
  151. # LocalDonains.append(value)
  152. req = urllib.request.Request('https://api.digitalocean.com/v2/domains/' + domain_name + '/records/')
  153. req.add_header('Content-Type', 'application/json')
  154. req.add_header('Authorization', 'Bearer ' + api)
  155. current = urllib.request.urlopen(req)
  156. remote = current.read().decode('utf-8')
  157. remoteData = json.loads(remote)
  158. for k in remoteData["domain_records"]:
  159. if k['name'] != '@':
  160. if str(k['data']) != local_ip4 and str(k['name']) not in config.sections():
  161. domain_names.append(k['name'])
  162. domain_ids.append(str(k['id']))
  163. return domain_names, domain_ids
  164. def add_existing_domain(name, domainid):
  165. section = str(name)
  166. config[section] = {
  167. 'subdomainid': str(domainid)
  168. }
  169. with open(filepath+'config.ini', 'w') as f:
  170. try:
  171. config.write(f)
  172. result = True
  173. except:
  174. result = False
  175. if result:
  176. logging.info(time.strftime("%Y-%m-%d %H:%M")+'Success! Domain %s added with ID: %s', name, domainid)
  177. else:
  178. logging.error(time.strftime("%Y-%m-%d %H:%M")+'Failure! Domain %s NOT added.', name)
  179. return result
  180. def addnewdomin(newdata):
  181. currentlocalip = urllib.request.urlopen("http://ip4.iurl.no").read().decode('utf-8')
  182. response = createnewdomain(newdata, currentlocalip)
  183. if response is not 'Fail':
  184. logging.info(time.strftime("%Y-%m-%d %H:%M")+'Success! Domain %s added to DigitalOcean (%s) with ID: %s', newdata, domain_name,
  185. response['domain_record']['id'])
  186. domainid = str(response['domain_record']['id'])
  187. section = str(newdata)
  188. section = section.strip("[']")
  189. config[section] = {
  190. 'subdomainid': str(domainid)
  191. }
  192. with open(filepath+'config.ini', 'w') as f:
  193. config.write(f)
  194. result = True
  195. else:
  196. result = False
  197. logging.error(time.strftime("%Y-%m-%d %H:%M")+'Failure! Domain %s added to DigitalOcean (%s) with ID: %s', newdata, domain_name)
  198. return result
  199. def getcurrentremoteip(domain):
  200. current = config.items(section=domain)
  201. subdomainid = current[0][1]
  202. req = urllib.request.Request('https://api.digitalocean.com/v2/domains/' + domain_name + '/records/' +
  203. subdomainid)
  204. req.add_header('Content-Type', 'application/json')
  205. req.add_header('Authorization', 'Bearer ' + api)
  206. current = urllib.request.urlopen(req)
  207. remote = current.read().decode('utf-8')
  208. remotedata = json.loads(remote)
  209. remoteip4 = remotedata['domain_record']['data']
  210. return remoteip4
  211. def remove_from_config(name):
  212. try:
  213. config.remove_section(name)
  214. logging.info(time.strftime("%Y-%m-%d %H:%M")+'Success! Domain %s removed from config.ini', name)
  215. with open(filepath+'config.ini', 'w') as f:
  216. config.write(f)
  217. result = True
  218. except:
  219. result = False
  220. logging.error(time.strftime("%Y-%m-%d %H:%M")+'Failure! Domain %s not removed from config.ini', name)
  221. return result