Files
wevads-platform/scripts/api_freedns.php
2026-02-26 04:53:11 +01:00

526 lines
22 KiB
PHP
Executable File

<?php
/**
* FreeDNS Factory — Full automation with captcha solving
* Endpoint: /api/freedns.php
* Called by N8N workflow "FreeDNS Auto Creator (5 domains/account)"
*
* Actions: check_accounts_needed, create_account, create_domains,
* configure_dns_records, verify_propagation, stats, list_domains
*/
require_once("/opt/wevads/config/credentials.php");
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$action = $_GET['action'] ?? $_POST['action'] ?? json_decode(file_get_contents('php://input'),true)['action'] ?? 'stats';
$db = get_pg("adx_system");
if(!$db){echo json_encode(['status'=>'error','message'=>'DB error']);exit;}
pg_query($db,"SET search_path TO admin");
// FreeDNS base URL
define('FREEDNS_BASE','https://freedns.afraid.org');
// Config from DB
$cfg = [];
$r = pg_query($db,"SELECT config_key,config_value FROM admin.freedns_config");
while($row=pg_fetch_assoc($r)) $cfg[$row['config_key']] = $row['config_value'];
$DOMAINS_PER_ACCOUNT = (int)($cfg['domains_per_account'] ?? 5);
$TRACKING_IP = $cfg['tracking_ip'] ?? '151.80.235.110';
$BAD_TLDS = explode(',', $cfg['bad_tlds'] ?? '.ru,.tk,.xyz');
$PREFERRED_TLDS = explode(',', $cfg['preferred_tlds'] ?? '.com,.net,.org');
$MAX_HOSTS = (int)($cfg['max_hosts_threshold'] ?? 50000);
// ======= CAPTCHA SOLVER =======
function solveCaptcha($siteKey, $pageUrl, $type='recaptcha_v2') {
global $db;
// Get active captcha service with API key
$r = pg_query($db,"SELECT name,api_key,api_url FROM admin.captcha_services WHERE status='active' AND api_key IS NOT NULL AND api_key!='' ORDER BY priority LIMIT 1");
$svc = pg_fetch_assoc($r);
if(!$svc) return ['error'=>'No captcha service configured with API key'];
$name = $svc['name'];
$key = $svc['api_key'];
if($name === '2captcha') {
return solve2Captcha($key, $siteKey, $pageUrl, $type);
} elseif($name === 'anticaptcha') {
return solveAntiCaptcha($key, $siteKey, $pageUrl, $type);
} elseif($name === 'capsolver') {
return solveCapsolver($key, $siteKey, $pageUrl, $type);
}
return ['error'=>"Unknown captcha service: $name"];
}
function solve2Captcha($apiKey, $siteKey, $pageUrl, $type) {
// Step 1: Submit task
$params = [
'key' => $apiKey,
'method' => 'userrecaptcha',
'googlekey' => $siteKey,
'pageurl' => $pageUrl,
'json' => 1
];
$ch = curl_init('https://2captcha.com/in.php?' . http_build_query($params));
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_TIMEOUT=>30]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
if(($resp['status'] ?? 0) != 1) return ['error'=>'2captcha submit failed: '.($resp['request']??'unknown')];
$taskId = $resp['request'];
// Step 2: Poll for result (max 120s)
for($i=0; $i<24; $i++) {
sleep(5);
$ch = curl_init("https://2captcha.com/res.php?key={$apiKey}&action=get&id={$taskId}&json=1");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_TIMEOUT=>15]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
if(($resp['status'] ?? 0) == 1) return ['token'=>$resp['request']];
if(($resp['request'] ?? '') !== 'CAPCHA_NOT_READY') return ['error'=>'2captcha: '.($resp['request']??'failed')];
}
return ['error'=>'2captcha timeout'];
}
function solveAntiCaptcha($apiKey, $siteKey, $pageUrl, $type) {
$payload = json_encode([
'clientKey' => $apiKey,
'task' => [
'type' => 'RecaptchaV2TaskProxyless',
'websiteURL' => $pageUrl,
'websiteKey' => $siteKey
]
]);
$ch = curl_init('https://api.anti-captcha.com/createTask');
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_POST=>1, CURLOPT_POSTFIELDS=>$payload, CURLOPT_HTTPHEADER=>['Content-Type: application/json'], CURLOPT_TIMEOUT=>30]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
if(($resp['errorId'] ?? 1) != 0) return ['error'=>'anticaptcha: '.($resp['errorDescription']??'submit failed')];
$taskId = $resp['taskId'];
for($i=0; $i<24; $i++) {
sleep(5);
$ch = curl_init('https://api.anti-captcha.com/getTaskResult');
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_POST=>1, CURLOPT_POSTFIELDS=>json_encode(['clientKey'=>$apiKey,'taskId'=>$taskId]), CURLOPT_HTTPHEADER=>['Content-Type: application/json'], CURLOPT_TIMEOUT=>15]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
if(($resp['status'] ?? '') === 'ready') return ['token'=>$resp['solution']['gRecaptchaResponse']];
if(($resp['status'] ?? '') !== 'processing') return ['error'=>'anticaptcha: '.($resp['errorDescription']??'failed')];
}
return ['error'=>'anticaptcha timeout'];
}
function solveCapsolver($apiKey, $siteKey, $pageUrl, $type) {
$payload = json_encode([
'clientKey' => $apiKey,
'task' => [
'type' => 'ReCaptchaV2TaskProxyLess',
'websiteURL' => $pageUrl,
'websiteKey' => $siteKey
]
]);
$ch = curl_init('https://api.capsolver.com/createTask');
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_POST=>1, CURLOPT_POSTFIELDS=>$payload, CURLOPT_HTTPHEADER=>['Content-Type: application/json'], CURLOPT_TIMEOUT=>30]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
if(($resp['errorId'] ?? 1) != 0) return ['error'=>'capsolver: '.($resp['errorDescription']??'submit failed')];
$taskId = $resp['taskId'];
for($i=0; $i<24; $i++) {
sleep(5);
$ch = curl_init('https://api.capsolver.com/getTaskResult');
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_POST=>1, CURLOPT_POSTFIELDS=>json_encode(['clientKey'=>$apiKey,'taskId'=>$taskId]), CURLOPT_HTTPHEADER=>['Content-Type: application/json'], CURLOPT_TIMEOUT=>15]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
if(($resp['status'] ?? '') === 'ready') return ['token'=>$resp['solution']['gRecaptchaResponse']];
if(($resp['status'] ?? '') !== 'processing') return ['error'=>'capsolver: '.($resp['errorDescription']??'failed')];
}
return ['error'=>'capsolver timeout'];
}
// ======= FreeDNS HTTP Client =======
function freednsRequest($url, $cookie='', $post=null) {
$ch = curl_init($url);
$opts = [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FOLLOWLOCATION => 1,
CURLOPT_TIMEOUT => 30,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
CURLOPT_HEADER => 1,
CURLOPT_SSL_VERIFYPEER => false,
];
if($cookie) $opts[CURLOPT_COOKIE] = $cookie;
if($post !== null) {
$opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = is_array($post) ? http_build_query($post) : $post;
}
curl_setopt_array($ch, $opts);
$resp = curl_exec($ch);
$hsize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($resp, 0, $hsize);
$body = substr($resp, $hsize);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Extract cookies
preg_match_all('/Set-Cookie:\s*([^;]+)/i', $headers, $m);
$cookies = implode('; ', $m[1] ?? []);
return ['code'=>$code, 'body'=>$body, 'cookies'=>$cookies, 'headers'=>$headers];
}
// ======= ACTIONS =======
switch($action) {
case 'check_accounts_needed':
$r = pg_query($db,"SELECT COUNT(*) as c FROM admin.freedns_accounts WHERE status='active'");
$cnt = (int)pg_fetch_assoc($r)['c'];
$r2 = pg_query($db,"SELECT COUNT(*) as c FROM admin.freedns_domains WHERE status='active'");
$doms = (int)pg_fetch_assoc($r2)['c'];
// Need more accounts if we have less than 3 active or < 15 active domains
$need = ($cnt < 3 || $doms < 15);
echo json_encode([
'status'=>'success',
'accounts'=>$cnt,
'domains'=>$doms,
'need_more'=>$need,
'target_accounts'=>max(3, $cnt),
'target_domains'=>max(15, $doms)
]);
break;
case 'create_account':
// Step 1: Get signup page to find captcha sitekey
$signup = freednsRequest(FREEDNS_BASE . '/signup/');
$body = $signup['body'];
$cookie = $signup['cookies'];
// Extract reCAPTCHA sitekey
$siteKey = '';
if(preg_match('/data-sitekey="([^"]+)"/', $body, $m)) {
$siteKey = $m[1];
} elseif(preg_match('/recaptcha.*?sitekey.*?["\']([0-9A-Za-z_-]+)["\']/', $body, $m)) {
$siteKey = $m[1];
}
// Generate random credentials
$user = 'wvd_' . substr(md5(uniqid(mt_rand(),true)),0,8);
$pass = bin2hex(random_bytes(8));
$email = $user . '@' . ['yopmail.com','guerrillamail.com','tempail.com'][mt_rand(0,2)];
// Step 2: Solve captcha if present
$captchaToken = '';
if($siteKey) {
$result = solveCaptcha($siteKey, FREEDNS_BASE . '/signup/');
if(isset($result['error'])) {
echo json_encode(['status'=>'error','message'=>'Captcha failed: '.$result['error'],'sitekey'=>$siteKey]);
break;
}
$captchaToken = $result['token'];
}
// Step 3: Submit registration
$postData = [
'username' => $user,
'password' => $pass,
'password2' => $pass,
'email' => $email,
'action' => 'signup',
];
if($captchaToken) {
$postData['g-recaptcha-response'] = $captchaToken;
}
$resp = freednsRequest(FREEDNS_BASE . '/signup/submit.php', $cookie, $postData);
// Check success
$success = (strpos($resp['body'],'activation') !== false || strpos($resp['body'],'success') !== false || $resp['code'] == 302);
if($success || strpos($resp['body'],'already') === false) {
// Store account
$u = pg_escape_string($db, $user);
$p = pg_escape_string($db, $pass);
$e = pg_escape_string($db, $email);
$ck = pg_escape_string($db, $resp['cookies'] ?: $cookie);
pg_query($db,"INSERT INTO admin.freedns_accounts(username,password,cookie,domains_count,max_domains,status) VALUES('$u','$p','$ck',0,$DOMAINS_PER_ACCOUNT,'pending_activation')");
echo json_encode([
'status'=>'success',
'message'=>'Account created, pending activation',
'username'=>$user,
'email'=>$email,
'captcha_solved'=>!empty($captchaToken),
'http_code'=>$resp['code']
]);
} else {
echo json_encode([
'status'=>'error',
'message'=>'Registration failed',
'http_code'=>$resp['code'],
'snippet'=>substr(strip_tags($resp['body']),0,300)
]);
}
break;
case 'activate_account':
$uid = (int)($_GET['account_id'] ?? $_POST['account_id'] ?? 0);
if(!$uid) { echo json_encode(['error'=>'account_id required']); break; }
$r = pg_query($db,"SELECT * FROM admin.freedns_accounts WHERE id=$uid");
$acc = pg_fetch_assoc($r);
if(!$acc) { echo json_encode(['error'=>'Account not found']); break; }
// Login to get fresh cookie
$loginResp = freednsRequest(FREEDNS_BASE . '/zc.php?from=L2luZGV4Lmh0bWw=', '', [
'username' => $acc['username'],
'password' => $acc['password'],
'submit' => 'Login',
'action' => 'auth'
]);
if($loginResp['cookies']) {
$ck = pg_escape_string($db, $loginResp['cookies']);
pg_query($db,"UPDATE admin.freedns_accounts SET cookie='$ck', status='active', last_login=NOW() WHERE id=$uid");
echo json_encode(['status'=>'success','message'=>'Account activated','username'=>$acc['username']]);
} else {
echo json_encode(['status'=>'error','message'=>'Login failed','http_code'=>$loginResp['code']]);
}
break;
case 'create_domains':
$count = (int)($_GET['count'] ?? $_POST['count'] ?? $DOMAINS_PER_ACCOUNT);
$accountId = (int)($_GET['account_id'] ?? $_POST['account_id'] ?? 0);
// Get active account with capacity
$where = $accountId ? "AND id=$accountId" : "";
$r = pg_query($db,"SELECT * FROM admin.freedns_accounts WHERE status='active' AND domains_count < max_domains $where ORDER BY domains_count ASC LIMIT 1");
$acc = pg_fetch_assoc($r);
if(!$acc) { echo json_encode(['status'=>'error','message'=>'No active FreeDNS account with capacity']); break; }
// Step 1: Get list of available shared domains
$page = freednsRequest(FREEDNS_BASE . '/domain/registry/', $acc['cookie']);
$body = $page['body'];
// Parse available domains - look for high-reputation ones
$available = [];
// FreeDNS registry page lists domains with host counts
if(preg_match_all('/<tr[^>]*>.*?<td[^>]*>(.*?)<\/td>.*?<td[^>]*>(\d+)<\/td>/si', $body, $matches, PREG_SET_ORDER)) {
foreach($matches as $m) {
$dom = trim(strip_tags($m[1]));
$hosts = (int)$m[2];
if(!$dom || !$hosts) continue;
// Filter: skip bad TLDs
$skip = false;
foreach($BAD_TLDS as $bad) {
if(substr($dom, -strlen(trim($bad))) === trim($bad)) { $skip = true; break; }
}
if($skip) continue;
// Prefer domains with moderate hosts (not too new, not too crowded)
$score = 0;
foreach($PREFERRED_TLDS as $pref) {
if(substr($dom, -strlen(trim($pref))) === trim($pref)) $score += 50;
}
if($hosts > 100 && $hosts < $MAX_HOSTS) $score += 30;
if($hosts > 1000) $score += 20;
$available[] = ['domain'=>$dom, 'hosts'=>$hosts, 'score'=>$score];
}
}
// Sort by score desc
usort($available, fn($a,$b) => $b['score'] - $a['score']);
// Also try parsing links format
if(empty($available)) {
if(preg_match_all('/href="[^"]*domain_id=(\d+)[^"]*"[^>]*>([^<]+)</', $body, $matches, PREG_SET_ORDER)) {
foreach($matches as $m) {
$available[] = ['domain'=>trim($m[2]), 'domain_id'=>$m[1], 'hosts'=>0, 'score'=>50];
}
}
}
$created = [];
$limit = min($count, count($available), $DOMAINS_PER_ACCOUNT - $acc['domains_count']);
for($i=0; $i<$limit; $i++) {
$base = $available[$i]['domain'];
$sub = 'mx' . substr(md5(uniqid(mt_rand(),true)),0,6);
$subdomain = $sub . '.' . $base;
// Create subdomain via FreeDNS API
$createResp = freednsRequest(FREEDNS_BASE . '/subdomain/save.php', $acc['cookie'], [
'type' => 'A',
'subdomain' => $sub,
'domain_id' => $available[$i]['domain_id'] ?? '',
'address' => $TRACKING_IP,
'ttl' => '',
'wildcard' => '',
'action' => 'save'
]);
// Extract freedns_id from response
$fid = '';
if(preg_match('/edit\.php\?data_id=(\d+)/', $createResp['body'], $m)) {
$fid = $m[1];
}
// Store in DB
$aid = $acc['id'];
$d = pg_escape_string($db, $subdomain);
$bd = pg_escape_string($db, $base);
$fi = pg_escape_string($db, $fid);
pg_query($db,"INSERT INTO admin.freedns_domains(account_id,domain,freedns_id,domain_type,base_domain,status) VALUES($aid,'$d','$fi','subdomain','$bd','active') ON CONFLICT DO NOTHING");
$created[] = ['subdomain'=>$subdomain, 'base'=>$base, 'freedns_id'=>$fid];
}
// Update account domain count
pg_query($db,"UPDATE admin.freedns_accounts SET domains_count = (SELECT COUNT(*) FROM admin.freedns_domains WHERE account_id={$acc['id']}) WHERE id={$acc['id']}");
echo json_encode([
'status'=>'success',
'created'=>count($created),
'domains'=>$created,
'available_bases'=>count($available),
'account'=>$acc['username'],
'top_bases'=>array_slice(array_map(fn($a)=>$a['domain'].' ('.$a['hosts'].' hosts)', $available),0,10)
]);
break;
case 'configure_dns_records':
// Configure MX, SPF, DKIM, DMARC for all domains without records
$r = pg_query($db,"SELECT fd.id, fd.domain, fd.freedns_id, fd.account_id, fa.cookie
FROM admin.freedns_domains fd
JOIN admin.freedns_accounts fa ON fd.account_id=fa.id
WHERE fd.status='active' AND NOT EXISTS(SELECT 1 FROM admin.freedns_records fr WHERE fr.domain_id=fd.id AND fr.record_type='MX')
LIMIT 10");
$configured = [];
while($dom = pg_fetch_assoc($r)) {
$did = $dom['id'];
$domain = $dom['domain'];
$cookie = $dom['cookie'];
$records = [];
// MX record → tracking server
$mxResp = freednsRequest(FREEDNS_BASE . '/subdomain/save.php', $cookie, [
'type'=>'MX', 'subdomain'=>'', 'domain_id'=>$dom['freedns_id'],
'address'=>$TRACKING_IP, 'ttl'=>'', 'mx'=>10, 'action'=>'save'
]);
$records[] = 'MX';
// TXT SPF
$spfResp = freednsRequest(FREEDNS_BASE . '/subdomain/save.php', $cookie, [
'type'=>'TXT', 'subdomain'=>'', 'domain_id'=>$dom['freedns_id'],
'address'=>"v=spf1 ip4:$TRACKING_IP ip4:89.167.1.139 ~all",
'ttl'=>'', 'action'=>'save'
]);
$records[] = 'SPF';
// TXT DMARC
$dmarcResp = freednsRequest(FREEDNS_BASE . '/subdomain/save.php', $cookie, [
'type'=>'TXT', 'subdomain'=>'_dmarc', 'domain_id'=>$dom['freedns_id'],
'address'=>'v=DMARC1; p=none; rua=mailto:dmarc@'.$domain,
'ttl'=>'', 'action'=>'save'
]);
$records[] = 'DMARC';
// Store records
foreach($records as $rec) {
pg_query($db,"INSERT INTO admin.freedns_records(domain_id,record_type,name,value) VALUES($did,'$rec','$domain','configured') ON CONFLICT DO NOTHING");
}
$configured[] = ['domain'=>$domain, 'records'=>$records];
}
echo json_encode(['status'=>'success','configured'=>count($configured),'domains'=>$configured]);
break;
case 'verify_propagation':
$r = pg_query($db,"SELECT fd.id, fd.domain FROM admin.freedns_domains fd WHERE fd.status='active' LIMIT 20");
$results = [];
while($dom = pg_fetch_assoc($r)) {
$dns = dns_get_record($dom['domain'], DNS_A);
$mx = dns_get_record($dom['domain'], DNS_MX);
$txt = dns_get_record($dom['domain'], DNS_TXT);
$propagated = !empty($dns);
$results[] = [
'domain'=>$dom['domain'],
'a_records'=>count($dns),
'mx_records'=>count($mx),
'txt_records'=>count($txt),
'propagated'=>$propagated
];
if($propagated) {
pg_query($db,"UPDATE admin.freedns_domains SET status='propagated' WHERE id={$dom['id']}");
}
}
echo json_encode(['status'=>'success','checked'=>count($results),'results'=>$results]);
break;
case 'sync_to_pool':
// Sync propagated FreeDNS domains to main domains_pool
$r = pg_query($db,"SELECT fd.domain, fd.base_domain FROM admin.freedns_domains fd WHERE fd.status IN ('active','propagated') AND NOT EXISTS(SELECT 1 FROM admin.domains_pool dp WHERE dp.domain=fd.domain)");
$synced = 0;
while($dom = pg_fetch_assoc($r)) {
$d = pg_escape_string($db, $dom['domain']);
$bd = pg_escape_string($db, $dom['base_domain']);
pg_query($db,"INSERT INTO admin.domains_pool(domain,status,provider,dns_provider,has_spf,has_dkim,has_dmarc) VALUES('$d','FREE','freedns','freedns',true,false,true) ON CONFLICT DO NOTHING");
$synced++;
}
echo json_encode(['status'=>'success','synced_to_pool'=>$synced]);
break;
case 'stats':
$acc = pg_fetch_assoc(pg_query($db,"SELECT COUNT(*) as total, COUNT(*) FILTER(WHERE status='active') as active FROM admin.freedns_accounts"));
$dom = pg_fetch_assoc(pg_query($db,"SELECT COUNT(*) as total, COUNT(*) FILTER(WHERE status='active') as active, COUNT(*) FILTER(WHERE status='propagated') as propagated FROM admin.freedns_domains"));
$rec = pg_fetch_assoc(pg_query($db,"SELECT COUNT(*) as total FROM admin.freedns_records"));
$cap = pg_fetch_assoc(pg_query($db,"SELECT COUNT(*) as total, COUNT(*) FILTER(WHERE status='active' AND api_key IS NOT NULL AND api_key!='') as ready FROM admin.captcha_services"));
$pool = pg_fetch_assoc(pg_query($db,"SELECT COUNT(*) as c FROM admin.domains_pool WHERE provider='freedns'"));
echo json_encode([
'status'=>'success',
'accounts'=>['total'=>(int)$acc['total'],'active'=>(int)$acc['active']],
'domains'=>['total'=>(int)$dom['total'],'active'=>(int)$dom['active'],'propagated'=>(int)$dom['propagated']],
'records'=>(int)$rec['total'],
'captcha_services'=>['total'=>(int)$cap['total'],'ready'=>(int)$cap['ready']],
'in_domains_pool'=>(int)$pool['c'],
'config'=>$cfg,
'health'=>((int)$cap['ready'] > 0) ? 'READY' : 'NEEDS_CAPTCHA_KEY'
]);
break;
case 'list_domains':
$limit = min((int)($_GET['limit'] ?? 50), 100);
$r = pg_query($db,"SELECT fd.*, fa.username as account FROM admin.freedns_domains fd JOIN admin.freedns_accounts fa ON fd.account_id=fa.id ORDER BY fd.created_at DESC LIMIT $limit");
$rows = [];
while($row=pg_fetch_assoc($r)) $rows[] = $row;
echo json_encode(['status'=>'success','data'=>$rows,'count'=>count($rows)]);
break;
case 'set_captcha_key':
$service = pg_escape_string($db, $_POST['service'] ?? $_GET['service'] ?? '');
$apiKey = pg_escape_string($db, $_POST['api_key'] ?? $_GET['api_key'] ?? '');
if(!$service || !$apiKey) { echo json_encode(['error'=>'service and api_key required']); break; }
pg_query($db,"UPDATE admin.captcha_services SET api_key='$apiKey', status='active' WHERE name='$service'");
echo json_encode(['status'=>'success','message'=>"Captcha key set for $service"]);
break;
default:
echo json_encode([
'status'=>'success',
'service'=>'freedns-factory',
'actions'=>[
'stats'=>'Dashboard stats',
'check_accounts_needed'=>'Check if more accounts needed (called by N8N)',
'create_account'=>'Create new FreeDNS account with captcha solving',
'activate_account'=>'Login and activate account (account_id required)',
'create_domains'=>'Create subdomains on high-rep bases (count, account_id optional)',
'configure_dns_records'=>'Set MX/SPF/DMARC on domains without records',
'verify_propagation'=>'Check DNS propagation',
'sync_to_pool'=>'Sync FreeDNS domains to main domains_pool',
'list_domains'=>'List all FreeDNS domains',
'set_captcha_key'=>'Configure captcha service (service=2captcha&api_key=xxx)'
]
]);
}