Script in PHP to update DNS type A records on porkbun.com using their API
This commit is contained in:
commit
ec0905e5f4
|
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
|
||||
//
|
||||
// Outputs a message to CLI, if this script is run from CLI.
|
||||
//
|
||||
|
||||
function echo_to_cli(string $message)
|
||||
{
|
||||
if (php_sapi_name() === 'cli') echo $message;
|
||||
}
|
||||
|
||||
//
|
||||
// Wrapper class to communicate with Porkbun API.
|
||||
//
|
||||
|
||||
class PorkbunAPI
|
||||
{
|
||||
function __construct(string $config_filename)
|
||||
{
|
||||
$this->config = json_decode(file_get_contents($config_filename));
|
||||
$this->ch = curl_init();
|
||||
}
|
||||
|
||||
function __destruct()
|
||||
{
|
||||
curl_close($this->ch);
|
||||
}
|
||||
|
||||
//
|
||||
// Test communication with Porkbun API.
|
||||
//
|
||||
// https://porkbun.com/api/json/v3/documentation#Authentication
|
||||
//
|
||||
|
||||
function ping()
|
||||
{
|
||||
// Create the correct endpoint based on the URL:
|
||||
$endpoint = $this->config->url . "ping";
|
||||
|
||||
// Set some cURL options:
|
||||
curl_setopt($this->ch, CURLOPT_URL, $endpoint);
|
||||
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($this->ch, CURLOPT_POST, 1);
|
||||
|
||||
// Tell the server that the request body contains JSON data:
|
||||
$headers = array();
|
||||
$headers[] = "Content-Type: application/json";
|
||||
curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// Porkbun API wants to be passed data in JSON format:
|
||||
$json = array
|
||||
(
|
||||
"apikey" => $this->config->apikey,
|
||||
"secretapikey" => $this->config->secretapikey
|
||||
);
|
||||
|
||||
curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($json));
|
||||
|
||||
// Execute cURL and return the result:
|
||||
$result = curl_exec($this->ch);
|
||||
if (curl_errno($this->ch))
|
||||
{
|
||||
echo_to_cli("Error: " . curl_error($this->ch));
|
||||
exit;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
//
|
||||
// Create a DNS record.
|
||||
//
|
||||
// https://porkbun.com/api/json/v3/documentation#DNS%20Create%20Record
|
||||
//
|
||||
|
||||
function create(string $domain, string $name = "", string $type = "A", string $content = "1.1.1.1", int $ttl = 600)
|
||||
{
|
||||
// Create the correct endpoint based on the URL:
|
||||
$endpoint = $this->config->url . "dns/create/" . $domain;
|
||||
|
||||
// Set some cURL options:
|
||||
curl_setopt($this->ch, CURLOPT_URL, $endpoint);
|
||||
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($this->ch, CURLOPT_POST, 1);
|
||||
|
||||
// Tell the server that the request body contains JSON data:
|
||||
$headers = array();
|
||||
$headers[] = "Content-Type: application/json";
|
||||
curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// Porkbun API wants to be passed data in JSON format:
|
||||
$json = array
|
||||
(
|
||||
"apikey" => $this->config->apikey,
|
||||
"secretapikey" => $this->config->secretapikey,
|
||||
"name" => "$name",
|
||||
"type" => "$type",
|
||||
"content" => "$content",
|
||||
"ttl" => "$ttl"
|
||||
);
|
||||
|
||||
curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($json));
|
||||
|
||||
// Execute cURL and return the result:
|
||||
$result = curl_exec($this->ch);
|
||||
if (curl_errno($this->ch))
|
||||
{
|
||||
echo_to_cli("Error: " . curl_error($this->ch));
|
||||
exit;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
//
|
||||
// Edit a DNS record.
|
||||
//
|
||||
// https://porkbun.com/api/json/v3/documentation#DNS%20Edit%20Record%20by%20Domain%20and%20ID
|
||||
//
|
||||
|
||||
function edit(string $domain, string $id, string $content, string $name = "")
|
||||
{
|
||||
// Create the correct endpoint based on the URL:
|
||||
$endpoint = $this->config->url . "dns/edit/" . $domain;
|
||||
$endpoint .= "/" . $id;
|
||||
|
||||
// Set some cURL options:
|
||||
curl_setopt($this->ch, CURLOPT_URL, $endpoint);
|
||||
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($this->ch, CURLOPT_POST, 1);
|
||||
|
||||
// Tell the server that the request body contains JSON data:
|
||||
$headers = array();
|
||||
$headers[] = "Content-Type: application/json";
|
||||
curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// Porkbun API wants the API key and secret in a JSON structure:
|
||||
$json = array
|
||||
(
|
||||
"apikey" => $this->config->apikey,
|
||||
"secretapikey" => $this->config->secretapikey,
|
||||
"name" => "$name",
|
||||
"type" => "A",
|
||||
"content" => "$content"
|
||||
);
|
||||
|
||||
curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($json));
|
||||
|
||||
// Execute cURL and return the result:
|
||||
$result = curl_exec($this->ch);
|
||||
if (curl_errno($this->ch))
|
||||
{
|
||||
echo_to_cli("Error: " . curl_error($this->ch));
|
||||
exit;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve all editable DNS records associated with a domain or a single record for a particular record ID.
|
||||
//
|
||||
// https://porkbun.com/api/json/v3/documentation#DNS%20Retrieve%20Records%20by%20Domain%20or%20ID
|
||||
//
|
||||
|
||||
function retrieve(string $domain, string $id = null)
|
||||
{
|
||||
// Create the correct endpoint based on the URL:
|
||||
$endpoint = $this->config->url . "dns/retrieve/" . $domain;
|
||||
if (!is_null($id)) $endpoint .= "/" . $id;
|
||||
|
||||
// Set some cURL options:
|
||||
curl_setopt($this->ch, CURLOPT_URL, $endpoint);
|
||||
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($this->ch, CURLOPT_POST, 1);
|
||||
|
||||
// Tell the server that the request body contains JSON data:
|
||||
$headers = array();
|
||||
$headers[] = "Content-Type: application/json";
|
||||
curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// Porkbun API wants the API key and secret in a JSON structure:
|
||||
$json = array
|
||||
(
|
||||
"apikey" => $this->config->apikey,
|
||||
"secretapikey" => $this->config->secretapikey
|
||||
);
|
||||
|
||||
curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($json));
|
||||
|
||||
// Execute cURL and return the result:
|
||||
$result = curl_exec($this->ch);
|
||||
if (curl_errno($this->ch))
|
||||
{
|
||||
echo_to_cli("Error: " . curl_error($this->ch));
|
||||
exit;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve all editable DNS records associated with a domain, subdomain and type.v
|
||||
//
|
||||
// https://porkbun.com/api/json/v3/documentation#DNS%20Retrieve%20Records%20by%20Domain,%20Subdomain%20and%20Type
|
||||
//
|
||||
|
||||
function retrieveByNameType(string $domain, string $type, string $subdomain = null)
|
||||
{
|
||||
// Create the correct endpoint based on the URL:
|
||||
$endpoint = $this->config->url . "dns/retrieveByNameType/" . $domain . "/" . $type;
|
||||
if (!is_null($subdomain)) $endpoint .= "/" . $subdomain;
|
||||
|
||||
// Set some cURL options:
|
||||
curl_setopt($this->ch, CURLOPT_URL, $endpoint);
|
||||
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($this->ch, CURLOPT_POST, 1);
|
||||
|
||||
// Tell the server that the request body contains JSON data:
|
||||
$headers = array();
|
||||
$headers[] = "Content-Type: application/json";
|
||||
curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// Porkbun API wants the API key and secret in a JSON structure:
|
||||
$json = array
|
||||
(
|
||||
"apikey" => $this->config->apikey,
|
||||
"secretapikey" => $this->config->secretapikey
|
||||
);
|
||||
|
||||
curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($json));
|
||||
|
||||
// Execute cURL and return the result:
|
||||
$result = curl_exec($this->ch);
|
||||
if (curl_errno($this->ch))
|
||||
{
|
||||
echo_to_cli("Error: " . curl_error($this->ch));
|
||||
exit;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// These properties don't need to be declared as they would be dynamically created when assigned a value:
|
||||
private stdClass $config;
|
||||
private CurlHandle $ch;
|
||||
};
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# pb-dydns
|
||||
|
||||
Script in PHP to update DNS type A records on porkbun.com using their API.
|
||||
|
||||
This can easily be modified to a different programming or scripting language and domain registrar.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- At least one domain in porkbun.com with DNS type A records already pointing to your dynamic IP address.
|
||||
- Access to Porkbun API.
|
||||
|
||||
## How to use it
|
||||
|
||||
### from command line
|
||||
|
||||
```
|
||||
$ php /path/to/pb-dydns.php domain_name
|
||||
```
|
||||
|
||||
### as a cron job
|
||||
|
||||
```
|
||||
$ crontab -e
|
||||
```
|
||||
|
||||
Modify this line to fit your needs, and add it as many times as domains you want to automatically update:
|
||||
|
||||
```
|
||||
*/10 * * * * php /path/to/pb-dydns.php domain_name > /dev/null
|
||||
```
|
||||
|
||||
That will run the script every 10 minutes.
|
||||
|
||||
Then restart cron (I'm not sure if this is necessary):
|
||||
|
||||
```
|
||||
$ sudo systemctl restart cron
|
||||
```
|
||||
|
||||
Entries to a log file called "pb-dydns.log" will be added, to view it you can:
|
||||
|
||||
```
|
||||
$ cat /path/to/pb-dydns.log
|
||||
```
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
//
|
||||
// This script updates the DNS type A on porkbun.com so it is always pointing to your public IP address.
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// - PorkbunAPI.php
|
||||
// - pb-dydns.json (it will be created automatically the 1st time)
|
||||
//
|
||||
// How to use it:
|
||||
//
|
||||
// - from command line:
|
||||
//
|
||||
// $ php /path/to/pb-dydns.php domain_name
|
||||
//
|
||||
// It will retrieve all DNS records for the specificied domain_name and update the A type ones
|
||||
// with your public IP address.
|
||||
//
|
||||
//- as a cron job, to add it to:
|
||||
//
|
||||
// $ crontab -e
|
||||
//
|
||||
// Append this line as many times as domains you want to automatically update:
|
||||
//
|
||||
// */10 * * * * php /path/to/pb-dydns.php domain_name > /dev/null
|
||||
//
|
||||
// And restart cron (I'm not sure if this is necessary):
|
||||
//
|
||||
// $ sudo systemctl restart cron
|
||||
//
|
||||
// Entries to a log file called "pb-dydns.log" will be added, to view it you can:
|
||||
//
|
||||
// $ cat /path/to/pb-dydns.log
|
||||
//
|
||||
|
||||
require "PorkbunAPI.php";
|
||||
|
||||
// Make sure that pb-dydns.json exists:
|
||||
$config_filename = __DIR__ . "/pb-dydns.json";
|
||||
|
||||
if (!file_exists($config_filename))
|
||||
{
|
||||
echo "The config file $config_filename does not exist." . PHP_EOL;
|
||||
|
||||
$config = json_encode(array
|
||||
(
|
||||
"url" => "https://porkbun.com/api/json/v3/",
|
||||
"apikey" => "",
|
||||
"secretapikey" => ""
|
||||
), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
file_put_contents($config_filename, $config);
|
||||
|
||||
echo "It has been created but you must edit it with your API key and secret." . PHP_EOL;
|
||||
echo "After that you can try again." . PHP_EOL;
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Domain name must be provided as argument.
|
||||
// There should be 2 arguments as $argc includes the script name itself:
|
||||
if ($argc != 2)
|
||||
{
|
||||
echo_to_cli("This script requires a domain name as argument:" . PHP_EOL);
|
||||
echo_to_cli("php $argv[0] domain_name" . PHP_EOL);
|
||||
|
||||
exit(2);
|
||||
}
|
||||
|
||||
// Take the arguments in individual variables:
|
||||
$myDomain = $argv[1];
|
||||
|
||||
// Create an instance of PorkbunAPI:
|
||||
$pbapi = new PorkbunAPI($config_filename);
|
||||
|
||||
// Test connection to Porkbun API in order to get the public IP:
|
||||
$result = json_decode($pbapi->ping());
|
||||
if ($result->status == "SUCCESS") $myIp = $result->yourIp;
|
||||
else
|
||||
{
|
||||
echo_to_cli("There was an error trying to ping Porkbun." . PHP_EOL);
|
||||
var_dump($result);
|
||||
exit(3);
|
||||
}
|
||||
|
||||
echo_to_cli("Your public IP address is $myIp" . PHP_EOL);
|
||||
|
||||
// Retrieve all DNS records associated with user's domain:
|
||||
$records = $pbapi->retrieve($myDomain);
|
||||
|
||||
// Discard those records that are not type "A":
|
||||
$data = json_decode($records);
|
||||
$filteredRecords = array_filter($data->records, function ($record)
|
||||
{
|
||||
return $record->type == "A";
|
||||
});
|
||||
|
||||
$log_filename = __DIR__ . "/pb-dydns.log";
|
||||
|
||||
// Update the records that passed the filter with the public IP:
|
||||
foreach ($filteredRecords as $record)
|
||||
{
|
||||
echo_to_cli("Porkbun's DNS for $record->name is pointing to $record->content... ");
|
||||
|
||||
if ($record->content != $myIp)
|
||||
{
|
||||
echo_to_cli("Let's change that... ");
|
||||
$name = rtrim(strstr($record->name, $myDomain, true), ".");
|
||||
$result = json_decode($pbapi->edit($myDomain, $record->id, $myIp, $name));
|
||||
if ($result->status == "SUCCESS") echo_to_cli("Done!" . PHP_EOL);
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$message = "$now: Updated DNS on $record->name from $record->content to $myIp" . PHP_EOL;
|
||||
file_put_contents($log_filename, $message, FILE_APPEND);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
echo_to_cli("Nothing needs to be changed!" . PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
Loading…
Reference in New Issue