ddns.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. <?php
  2. namespace DDNS;
  3. if (!is_callable('curl_init'))
  4. die('ERROR: This PHP script requires cURL to be installed.');
  5. /*
  6. *
  7. * CUSTOM DYNAMIC DNS SOLUTION FOR USE WITH CLOUDFLARE
  8. * ===============================================================================================
  9. * ===============================================================================================
  10. *
  11. * Christopher Pei
  12. * ------------------------------------------
  13. * September 05, 2018 R1
  14. * November 27, 2018 R2
  15. * November 28, 2018 R3
  16. * ===============================================================================================
  17. *
  18. */
  19. // -------------------------------------------------------------------------------------------
  20. // SETTINGS
  21. // -------------------------------------------------------------------------------------------
  22. // API Credential Setup Instructions
  23. // ===========================================================================================
  24. //
  25. // Settings or organized in a tabular format with SETTING NAME on the left and VLUE on the right.
  26. // Please only edit the VALUE data/columns. When editing, please keep values inside the quotation-
  27. // marks ("") and do not remove any parentheses (()) or semi-colons (;).
  28. //
  29. // ------------------ Set Basic Options
  30. //
  31. // Since this script may be publicly accessible on your web server, you can use the "DDNS_PASSWORD"
  32. // setting so that this string must be provided in order to access this script.
  33. //
  34. // To access the script with the password, you use a web browser and access it as follows:
  35. //
  36. // /ddns.php?psswd=56789
  37. //
  38. // replacing "56789" with your DDNS Password. For example, if this script is located
  39. // on your webserver at "https://www.google.com/ddns.php" and your DDNS Password is
  40. // "4392", you would use your browser to access it as follows:
  41. //
  42. // https://www.google.com/ddns.php?passwd=4392
  43. //
  44. // Alternatively, if you wish to disable this security feature, leave the VALUE empty
  45. // (nothing between the quotation marks). Then you can access the script just via the
  46. // name itself. In the example above but without a DDNS Password, you would access it
  47. // as follows:
  48. //
  49. // https://www.google.com/ddns.php
  50. //
  51. //
  52. // -------------------------------------------------------------------------------------------
  53. // ------------------ SETTING NAME -------------------- VALUE --------------------------------
  54. // -------------------------------------------------------------------------------------------
  55. define(__NAMESPACE__ . '\DDNS_PASSWORD', '56789' );
  56. //
  57. //
  58. //
  59. // ------------------ Set Your API Credentials
  60. //
  61. // For many of these settings, you will have access to your CloudFlare account to acquire the
  62. // appropriate information.
  63. //
  64. // 1). Login to your CloudFlare account at: https://dash.cloudflare.com/login
  65. //
  66. // 2). If applicable, select your account.
  67. //
  68. // 3). If applicable, select the domain you wish to use.
  69. //
  70. // 4). Under the "Domain Summary" section, locate the "Zone ID". Copy this value and paste
  71. // this into the VALUE for the "CF_ZONE_ID" setting.
  72. //
  73. // 5). In the same section, click "Get your API Key". Scroll down the page and under the
  74. // "API Keys" locate the "Global API Key" and click "View". Copy this value and paste
  75. // this into the VALUE for the "CF_API_KEY" setting.
  76. //
  77. // 6). The e-mail address you use to login to CloudFlare is your API username. Provide
  78. // this into the VALUE for the "CF_API_USERNAME" setting.
  79. //
  80. // -------------------------------------------------------------------------------------------
  81. // ------------------ SETTING NAME -------------------- VALUE --------------------------------
  82. // -------------------------------------------------------------------------------------------
  83. define(__NAMESPACE__ . "\CF_ZONE_ID", "s40v9w8fbn0w49r84r8gw04954852784" );
  84. define(__NAMESPACE__ . "\CF_API_USERNAME", "your_email@domain.com" );
  85. define(__NAMESPACE__ . "\CF_API_KEY", "xk59gs4jg90d893i8560fg8wejdnci4869vvy" );
  86. //
  87. // ------------------ Create The DNS Record
  88. //
  89. // Now, if you have not already done so, it is time to create a subdomain on the domain
  90. // that you wish to use for the custom dynamic DNS:
  91. //
  92. // 1). Navigate to https://dash.cloudflare.com/websites and select the domain you wish to
  93. // use for the custom dynamic DNS service.
  94. //
  95. // 2). Go to the "DNS" section, and under the "Search" box, select "A" from the drop-down
  96. // menu, enter your desired subdomain in the "Name" field.
  97. //
  98. // 3). Copy and past the value of the "Name" field into the VALUE for the "FQDN" setting.
  99. // Do not include ANY trailing slashes and do NOT include either "http://" or "https://"
  100. // in the setting value.
  101. //
  102. // 4). Enter any IP address into the "IPv4 address" field (even "1.2.3.4" is okay; this will
  103. // be updated by the script automatically later, so its current value is not relevant).
  104. //
  105. // 5). If this dynamic DNS is going to be used for HTTP traffic (such as a website), then you
  106. // can leave the "Automatic TTL" and orange cloud enabled to benefit from CloudFlare's
  107. // reverse proxy security. However, if this will be used for something else (such as FTP,
  108. // SSH, Remote Desktop, etc) then modify the TTL to "2 minutes" and DISABLE the orange
  109. // cloud. Disabling the orange cloud will disable CloudFlare's reverse proxy, which is
  110. // required for non-HTTP protocols.
  111. //
  112. // 6). When finished, click the "Add Record" button.
  113. //
  114. // -------------------------------------------------------------------------------------------
  115. // ------------------ SETTING NAME -------------------- VALUE --------------------------------
  116. // -------------------------------------------------------------------------------------------
  117. define(__NAMESPACE__ . "\FQDN", "something.example.com" );
  118. //
  119. // ------------------ Get The DNS Record ID
  120. //
  121. // This is where it might seem funky. CloudFlare is somewhat "stupid" in the sense that it
  122. // provides no NORMAL way to get the ID of a DNS record, which is a unique ID that identifies
  123. // a particular DNS record for your website. The only way to do this is programmatically, via
  124. // their API.
  125. //
  126. // For simplicity, I have programmed this ability directly into this script:
  127. //
  128. // 1). Please be sure that all other settings are already provided--- otherwise this script
  129. // cannot authenticate with the CloudFlare API to find the ID of your DNS record. The
  130. // VALUE of CF_ZONE_ID, CF_API_KEY, and CF_API_USERNAME must be provided. (Please see
  131. // the [Set Your API Credentials] section above for help).
  132. //
  133. // 2). Using a web browser of your choice, access this PHP script in the URL as follows:
  134. //
  135. // /ddns.php?psswd=12345&dump=1
  136. //
  137. // replacing "12345" with your DDNS Password. For example, if this script is located
  138. // on your webserver at "https://www.google.com/ddns.php" and your DDNS Password is
  139. // "4392", you would use your browser to access it as follows:
  140. //
  141. // https://www.google.com/ddns.php?passwd=4392&dump=1
  142. //
  143. // If your DDNS Password is disabled, access this PHP script in the URL as follows:
  144. //
  145. // /ddns.php?dump=1
  146. //
  147. // For example:
  148. //
  149. // https://www.google.com/ddns.php?dump=1
  150. //
  151. //
  152. // 3). If successful, the script will output a long string. Please carefully copy and paste
  153. // the string into the VALUE for the "CF_DNS_RECORD_ID" setting.
  154. // -------------------------------------------------------------------------------------------
  155. // ------------------ SETTING NAME -------------------- VALUE --------------------------------
  156. // -------------------------------------------------------------------------------------------
  157. define(__NAMESPACE__ . "\CF_DNS_RECORD_ID", "e67898768a9859c859845898b7897f09" );
  158. //
  159. //
  160. // ------------------ Miscellaneous Settings
  161. //
  162. // If you wish to use CloudFlare's reverse proxy security, which will mask your your IP, set this
  163. // value to "true" (without quotations). Otherwise, set it to "false" (without quotations). Note
  164. // that the CloudFlare reverse proxy feature is currently for HTTP services only, and will not
  165. // work for other services, such as FTP, SSH, Remote Desktop, etc. A value of "true" is equivalent
  166. // to the "orange-cloud" DNS setting on your CloudFlare control panel, while a value of "false"
  167. // is equivalent of the "grey-cloud" DNS setting.
  168. // -------------------------------------------------------------------------------------------
  169. // ------------------ SETTING NAME -------------------- VALUE --------------------------------
  170. // -------------------------------------------------------------------------------------------
  171. define(__NAMESPACE__ . "\USE_CF_REV_PROXY", false );
  172. // --------------------------------------------------------------------------------------------
  173. // --------------------------------------------------------------------------------------------
  174. // --------------------------------------------------------------------------------------------
  175. // --------------------------------------------------------------------------------------------
  176. // ------------- END OF SETTINGS - BE CAREFUL CHANGING ANYTHING BEYOND THIS POINT -------------
  177. // --------------------------------------------------------------------------------------------
  178. // --------------------------------------------------------------------------------------------
  179. // --------------------------------------------------------------------------------------------
  180. // --------------------------------------------------------------------------------------------
  181. // VARS
  182. $visitor_ip = $_SERVER['HTTP_CF_CONNECTING_IP']??$_SERVER['REMOTE_ADDR'];
  183. $j = new \stdClass();
  184. // FUNCTIONS
  185. // @BRIEF Creates and outputs the JSON response.
  186. // @RETURNS Does not return a value.
  187. function CREATE_JSON_RESPONSE(&$r){
  188. header('Content-Type: application/json');
  189. echo json_encode($r, JSON_FORCE_OBJECT);
  190. }
  191. // @BRIEF Checks DDNS Password with provided password.
  192. // @RETURNS Returns true if correct or disabled, and false if incorrect.
  193. function CHECK_PASSWORD(){
  194. if (defined('DDNS\\DDNS_PASSWORD')){
  195. if (empty(DDNS_PASSWORD))
  196. return true;
  197. if (!strcmp($_REQUEST['passwd'], DDNS_PASSWORD))
  198. return true;
  199. return false;
  200. }
  201. return true;
  202. }
  203. // @BRIEF Checks if script is being called with a DUMP request.
  204. // @RETURNS Returns true if it is or false otherwise.
  205. function IS_DUMP_REQUEST(){
  206. if (isset($_REQUEST['dump']) &&
  207. !empty($_REQUEST['dump']) &&
  208. ($_REQUEST['dump'] == 1 ||
  209. $_REQUEST['dump'] == "1")
  210. ){
  211. if (!strcmp(FQDN, "something.example.com")){
  212. $j->success = false;
  213. $j->message = 'You must provide the domain you wish to use for the "FQDN" setting.';
  214. CREATE_JSON_RESPONSE($j);
  215. exit(0);
  216. }
  217. else return true;
  218. }
  219. return false;
  220. }
  221. // @BRIEF Creates and DUMP of the DNS data to output the DNS Record ID.
  222. // @RETURNS Does not return a value.
  223. function DUMP(){
  224. try{
  225. // Send the request.
  226. $ch = curl_init();
  227. curl_setopt($ch, CURLOPT_URL, "https://api.cloudflare.com/client/v4/zones/" . CF_ZONE_ID . "/dns_records?type=A&name=" . FQDN . "&match=all");
  228. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  229. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
  230. $headers = array();
  231. $headers[] = "X-Auth-Email: " . CF_API_USERNAME;
  232. $headers[] = "X-Auth-Key: " . CF_API_KEY;
  233. $headers[] = "Content-Type: application/json";
  234. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  235. $response = curl_exec($ch);
  236. if (curl_errno($ch)){
  237. $j->success = false;
  238. $j->message = 'cURL encountered a problem.';
  239. $j->cURL_err = curl_error($ch);
  240. curl_close ($ch);
  241. CREATE_JSON_RESPONSE($j);
  242. exit(0);
  243. }
  244. curl_close ($ch);
  245. // Parse the DNS Record ID.
  246. $r = json_decode($response, true);
  247. if (isset($r['result'][0]['id'])){
  248. $j->success = true;
  249. $j->message = 'Please copy the "id" value below into the VALUE for the CF_DNS_RECORD_ID settings. Be sure to copy only the value below within the quotation marks.';
  250. $j->id = $r['result'][0]['id'];
  251. CREATE_JSON_RESPONSE($j);
  252. exit(0);
  253. }
  254. else{
  255. $j->success = false;
  256. $j->message = 'CloudFlare did not provide a valid response. Please ensure that you have provided correct details for all settings and try again.';
  257. CREATE_JSON_RESPONSE($j);
  258. exit(0);
  259. }
  260. }catch(Exception $e){
  261. $j->success = false;
  262. $j->message = 'A fatal exception occurred.';
  263. $j->exception = $e;
  264. $j->cURL_err = curl_error($ch);
  265. curl_close ($ch);
  266. }
  267. }
  268. // @BRIEF Updates the DNS record with the visitor's IP.
  269. // @RETURNS Does not return a value.
  270. function DNS_UPDATE($new_ip){
  271. try{
  272. // Send the request.
  273. $ch = curl_init();
  274. curl_setopt($ch, CURLOPT_URL, "https://api.cloudflare.com/client/v4/zones/" . CF_ZONE_ID . "/dns_records/" . CF_DNS_RECORD_ID);
  275. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  276. // CloudFlare is fucking retarded and bitches about this as "malformed JSON, error 6007".
  277. //curl_setopt($ch, CURLOPT_POSTFIELDS, "{\"type\":\"A\",\"name\":\"" . FQDN . "\",\"content\":\"" . $new_ip . "\",\"ttl\":120,\"proxied\":" . USE_CF_REV_PROXY . "}");
  278. if (USE_CF_REV_PROXY)
  279. curl_setopt($ch, CURLOPT_POSTFIELDS, "{\"type\":\"A\",\"name\":\"" . FQDN . "\",\"content\":\"" . $new_ip . "\",\"ttl\":120,\"proxied\":true}");
  280. else
  281. curl_setopt($ch, CURLOPT_POSTFIELDS, "{\"type\":\"A\",\"name\":\"" . FQDN . "\",\"content\":\"" . $new_ip . "\",\"ttl\":120,\"proxied\":false}");
  282. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
  283. $headers = array();
  284. $headers[] = "X-Auth-Email: " . CF_API_USERNAME;
  285. $headers[] = "X-Auth-Key: " . CF_API_KEY;
  286. $headers[] = "Content-Type: application/json";
  287. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  288. $response = curl_exec($ch);
  289. if (curl_errno($ch)){
  290. $j->success = false;
  291. $j->message = 'cURL encountered a problem.';
  292. $j->cURL_err = curl_error($ch);
  293. curl_close ($ch);
  294. CREATE_JSON_RESPONSE($j);
  295. exit(0);
  296. }
  297. curl_close ($ch);
  298. // Parse the DNS Record ID.
  299. $r = json_decode($response, true);
  300. if (isset($r['success'])){
  301. // Uncomment these lines for debugging.
  302. // header('Content-Type: application/json');
  303. // echo json_encode($r, JSON_FORCE_OBJECT);
  304. // exit(0);
  305. if ($r['success'] == true){
  306. $j->success = true;
  307. $j->message = 'The [' . FQDN . '] DNS record was successfully updated to point to [' . $new_ip . '].';
  308. CREATE_JSON_RESPONSE($j);
  309. exit(0);
  310. }
  311. $j->success = false;
  312. $j->message = 'CloudFlare reported an error. Please ensure that you have provided correct details for all settings and try again.';
  313. // $j->id = $r['result'][0]['id'];
  314. CREATE_JSON_RESPONSE($j);
  315. exit(0);
  316. }
  317. else{
  318. $j->success = false;
  319. $j->message = 'CloudFlare did not provide a valid response. Please ensure that you have provided correct details for all settings and try again.';
  320. CREATE_JSON_RESPONSE($j);
  321. exit(0);
  322. }
  323. }catch(Exception $e){
  324. $j->success = false;
  325. $j->message = 'A fatal exception occurred.';
  326. $j->exception = $e;
  327. $j->cURL_err = curl_error($ch);
  328. curl_close ($ch);
  329. }
  330. }
  331. // SCRIPT
  332. // ---------------------------------------------------------
  333. if (CHECK_PASSWORD()){
  334. if (IS_DUMP_REQUEST()){
  335. DUMP();
  336. exit(0);
  337. }
  338. DNS_UPDATE($visitor_ip);
  339. exit(0);
  340. }
  341. $j->success = false;
  342. $j->message = 'The DDNS Password is incorrect. Check the "passwd" value and try again.';
  343. CREATE_JSON_RESPONSE($j);
  344. exit(0);
  345. // ---------------------------------------------------------