modules/dns/include/dnsinclude.php
e45c3ec9
 <?php
c208bd90
 /*
 This file belongs to the Webinterface of schokokeks.org Hosting
 
cf54502a
 Written 2008-2018 by schokokeks.org Hosting, namely
c208bd90
   Bernd Wurst <bernd@schokokeks.org>
   Hanno Böck <hanno@schokokeks.org>
 
 To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
 
2626dd47
 You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see
c208bd90
 http://creativecommons.org/publicdomain/zero/1.0/
 
 Nevertheless, in case you use a significant part of this code, we ask (but not require, see the license) that you keep the authors' names in place and return your changes to the public. We would be especially happy if you tell us what you're going to do with this code.
 */
e45c3ec9
 
 require_once('inc/debug.php');
 require_once('inc/base.php');
 require_once('inc/security.php');
39adb185
 require_once('inc/error.php');
e45c3ec9
 
 require_once('class/domain.php');
 
0263dab5
 $caa_properties= array( 0 => "issue", 1 => "issuewild", 2 => "iodef" );
e45c3ec9
 
2626dd47
 function get_dyndns_accounts()
e45c3ec9
 {
2626dd47
     $uid = (int) $_SESSION['userinfo']['uid'];
     $result = db_query("SELECT * FROM dns.dyndns WHERE uid=?", array($uid));
     $list = array();
     while ($item = $result->fetch()) {
         array_push($list, $item);
     }
     DEBUG($list);
     return $list;
e45c3ec9
 }
 
 
2626dd47
 function get_dyndns_account($id, $ignore=true)
e45c3ec9
 {
2626dd47
     $args = array(":id" => (int) $id,
8132c40e
                 ":uid" => (int) $_SESSION['userinfo']['uid']);
2626dd47
     $result = db_query("SELECT * FROM dns.dyndns WHERE id=:id AND uid=:uid", $args);
     if ($result->rowCount() != 1) {
         if ($ignore) {
             return null;
         }
         logger(LOG_WARNING, "modules/dns/include/dnsinclude", "dyndns", "account »{$id}« invalid for uid »{$_SESSION['userinfo']['uid']}«.");
         system_failure("Account ungültig");
     }
     $item = $result->fetch();
     DEBUG($item);
     return $item;
e45c3ec9
 }
 
 
 function create_dyndns_account($handle, $password_http, $sshkey)
 {
2626dd47
     $uid = (int) $_SESSION['userinfo']['uid'];
53f24981
 
2626dd47
     if ($password_http == '' && $sshkey == '') {
         system_failure('Sie müssen entweder einen SSH-Key oder ein Passwort zum Web-Update eingeben.');
     }
53f24981
 
2626dd47
     $handle = filter_input_username($handle);
690f4b9d
 
2626dd47
     if (strlen(trim($sshkey)) == 0) {
         $sshkey = null;
     } else {
         $sshkey = filter_ssh_key($sshkey);
     }
e45c3ec9
 
2626dd47
     $pwhash = null;
     if ($password_http) {
         $pwhash = "{SHA}".base64_encode(sha1($password_http, true));
     }
e45c3ec9
 
2626dd47
     db_query(
       "INSERT INTO dns.dyndns (uid, handle, password, sshkey) VALUES ".
8132c40e
            "(:uid, :handle, :pwhash, :sshkey)",
2626dd47
            array(":uid" => $uid, ":handle" => $handle, ":pwhash" => $pwhash, ":sshkey" => $sshkey)
   );
     $dyndns_id = db_insert_id();
     //$masterdomain = new Domain(config('masterdomain'));
     //db_query("INSERT INTO dns.custom_records (type, domain, hostname, dyndns, ttl) VALUES ".
     //         "('a', :dom, :hostname, :dyndns, 120)",
     //         array(":dom" => $masterdomain->id, ":hostname" => filter_input_hostname($handle).'.'.$_SESSION['userinfo']['username'], ":dyndns" => $dyndns_id));
     logger(LOG_INFO, "modules/dns/include/dnsinclude", "dyndns", "inserted account {$dyndns_id}");
     return $dyndns_id;
e45c3ec9
 }
 
 
 function edit_dyndns_account($id, $handle, $password_http, $sshkey)
 {
2626dd47
     $id = (int) $id;
     $oldaccount = get_dyndns_account($id);
     $handle = filter_input_username($handle);
     $sshkey = filter_input_general($sshkey);
     if (chop($sshkey) == '') {
         $sshkey = null;
     }
e45c3ec9
 
2626dd47
     if ($oldaccount['handle'] != $handle) {
         $masterdomain = new Domain(config('masterdomain'));
         db_query(
         "UPDATE dns.custom_records SET hostname=:newhostname WHERE ".
6b12fc7d
              "hostname=:oldhostname AND domain=:dom AND dyndns=:dyndns AND ip IS NULL",
              array(":dom" => $masterdomain->id, ":newhostname" => filter_input_hostname($handle).'.'.$_SESSION['userinfo']['username'],
2626dd47
                    ":oldhostname" => $oldaccount['handle'].'.'.$_SESSION['userinfo']['username'],  ":dyndns" => $id)
     );
     }
6b12fc7d
 
2626dd47
     $args = array(":handle" => $handle, ":sshkey" => $sshkey, ":id" => $id);
     $pwhash = null;
     if ($password_http && $password_http != '************') {
         $args[":pwhash"] = "{SHA}".base64_encode(sha1($password_http, true));
         db_query("UPDATE dns.dyndns SET handle=:handle, password=:pwhash, sshkey=:sshkey WHERE id=:id", $args);
     } else {
         db_query("UPDATE dns.dyndns SET handle=:handle, sshkey=:sshkey WHERE id=:id", $args);
     }
     logger(LOG_INFO, "modules/dns/include/dnsinclude", "dyndns", "edited account »{$id}«");
e45c3ec9
 }
 
 
 function delete_dyndns_account($id)
 {
2626dd47
     $id = (int) $id;
e45c3ec9
 
2626dd47
     db_query("DELETE FROM dns.dyndns WHERE id=?", array($id));
     logger(LOG_INFO, "modules/dns/include/dnsinclude", "dyndns", "deleted account »{$id}«");
e45c3ec9
 }
 
 
 function get_dyndns_records($id)
 {
2626dd47
     $id = (int) $id;
     $result = db_query("SELECT hostname, domain, type, ttl, lastchange, id FROM dns.custom_records WHERE dyndns=?", array($id));
     $data = array();
     while ($entry = $result->fetch()) {
         $dom = new Domain((int) $entry['domain']);
         if ($dom->fqdn != config('masterdomain') && $dom->fqdn != config('user_vhosts_domain')) {
             $dom->ensure_userdomain();
         }
         $entry['fqdn'] = $entry['hostname'].'.'.$dom->fqdn;
         if (! $entry['hostname']) {
             $entry['fqdn'] = $dom->fqdn;
         }
         array_push($data, $entry);
4e5f5cb1
     }
2626dd47
     DEBUG($data);
     return $data;
e45c3ec9
 }
 
0263dab5
 $valid_record_types = array('a', 'aaaa', 'mx', 'ns', 'spf', 'txt', 'cname', 'ptr', 'srv', 'raw', 'sshfp', 'caa');
f7ff31f2
 
 
 function blank_dns_record($type)
2626dd47
 {
     global $valid_record_types;
     if (!in_array(strtolower($type), $valid_record_types)) {
         system_failure('invalid type: '.$type);
     }
     $rec = array('hostname' => null,
f7ff31f2
                'domain' => 0,
                'type' => strtolower($type),
                'ttl' => 3600,
2626dd47
                'ip' => null,
                'dyndns' => null,
                'data' => null,
                'spec' => null);
     if (strtolower($type) == 'mx') {
         $rec['data'] = config('default_mx');
         $rec['spec'] = '5';
     }
     return $rec;
f7ff31f2
 }
 
 function get_dns_record($id)
 {
2626dd47
     $id = (int) $id;
     $result = db_query("SELECT hostname, domain, type, ip, dyndns, spec, data, ttl FROM dns.custom_records WHERE id=?", array($id));
     if ($result->rowCount() != 1) {
         system_failure('illegal ID');
     }
     $data = $result->fetch();
     $dom = new Domain((int) $data['domain']);
     $dom->ensure_userdomain();
     DEBUG($data);
     return $data;
f7ff31f2
 }
e45c3ec9
 
 
 function get_domain_records($dom)
 {
2626dd47
     $dom = (int) $dom;
     $result = db_query("SELECT hostname, domain, type, ip, dyndns, spec, data, ttl, id FROM dns.custom_records WHERE domain=?", array($dom));
     $data = array();
     while ($entry = $result->fetch()) {
         $dom = new Domain((int) $entry['domain']);
         $dom->ensure_userdomain();
         $entry['fqdn'] = $entry['hostname'].'.'.$dom->fqdn;
         if (! $entry['hostname']) {
             $entry['fqdn'] = $dom->fqdn;
         }
         array_push($data, $entry);
     }
     DEBUG($data);
     return $data;
e45c3ec9
 }
 
f7ff31f2
 function get_domain_auto_records($domainname)
 {
2626dd47
     $result = db_query("SELECT hostname, domain, CONCAT_WS('.', hostname, domain) AS fqdn, type, ip, spec, data, ttl FROM dns.tmp_autorecords WHERE domain=?", array($domainname));
     $data = array();
     while ($entry = $result->fetch()) {
         array_push($data, $entry);
     }
     DEBUG($data);
     return $data;
f7ff31f2
 }
 
e45c3ec9
 
0263dab5
 $implemented_record_types = array('a', 'aaaa', 'mx', 'spf', 'txt', 'cname', 'ptr', 'srv', 'ns', 'sshfp', 'caa');
361b0a4b
 
 function save_dns_record($id, $record)
 {
2626dd47
     global $valid_record_types;
     global $implemented_record_types;
     $record['type'] = strtolower($record['type']);
     if (!in_array($record['type'], $valid_record_types)) {
         system_failure('invalid type: '.$record['type']);
     }
     if (!in_array($record['type'], $implemented_record_types)) {
         system_failure('record type '.$record['type'].' not implemented at the moment.');
     }
     $dom = new Domain((int) $record['domain']);
     $dom->ensure_userdomain();
     if (! $dom->id) {
         system_failure('invalid domain');
     }
     if ($record['hostname'] == '') {
         $record['hostname'] = null;
     }
     verify_input_hostname($record['hostname'], true);
     verify_input_recorddata($record['data']);
     if ($record['ttl'] &&  (int) $record['ttl'] < 1) {
         system_failure('Fehler bei TTL');
     }
     switch ($record['type']) {
361b0a4b
     case 'a':
2626dd47
       if ($record['dyndns']) {
           get_dyndns_account($record['dyndns']);
           $record['ip'] = null;
       } else {
           verify_input_ipv4($record['ip']);
           $record['data'] = null;
           $record['spec'] = null;
361b0a4b
       }
       break;
     case 'aaaa':
ce4c0c44
       if ($record['dyndns']) {
2626dd47
           get_dyndns_account($record['dyndns']);
           $record['ip'] = null;
ce4c0c44
       } else {
2626dd47
           $record['dyndns'] = null;
ce4c0c44
           verify_input_ipv6($record['ip']);
2626dd47
           $record['data'] = null;
           $record['spec'] = null;
ce4c0c44
       }
361b0a4b
       break;
     case 'mx':
2626dd47
       $record['dyndns'] = null;
361b0a4b
       $record['spec'] = (int) $record['spec'];
2626dd47
       if ($record['spec'] < 1) {
           systen_failure("invalid priority");
       }
361b0a4b
       verify_input_hostname($record['data']);
2626dd47
       if (! $record['data']) {
           system_failure('MX hostname missing');
       }
       $record['ip'] = null;
361b0a4b
       break;
93c5af8f
     case 'ptr':
0408a9ba
     case 'ns':
d9cce25d
       if (!$record['hostname']) {
           system_failure("Die angestrebte Konfiguration wird nicht funktionieren, Speichern wurde daher verweigert.");
       }
2626dd47
       // no break
d9cce25d
     case 'cname':
2626dd47
       $record['dyndns'] = null;
       $record['spec'] = null;
       $record['ip'] = null;
361b0a4b
       verify_input_hostname($record['data']);
2626dd47
       if (! $record['data']) {
           system_failure('destination host missing');
       }
361b0a4b
       break;
 
93c5af8f
     case 'spf':
     case 'txt':
2626dd47
       $record['dyndns'] = null;
       $record['spec'] = null;
       $record['ip'] = null;
       if (! $record['data']) {
           system_failure('text entry missing');
       }
bd044f18
       break;
 
3490c290
     case 'sshfp':
2626dd47
       $record['dyndns'] = null;
       $record['spec'] = max((int) $record['spec'], 1);
       $record['ip'] = null;
       if (! $record['data']) {
           system_failure('text entry missing');
       }
3490c290
       break;
 
0263dab5
     case 'caa':
2626dd47
       $record['dyndns'] = null;
       $record['ip'] = null;
       if (! $record['data']) {
           system_failure('text entry missing');
       }
0263dab5
       break;
3490c290
 
361b0a4b
     case 'srv':
       system_failure('not implemented yet');
2626dd47
       // no break
361b0a4b
     default:
       system_failure('Not implemented');
   }
2626dd47
     $id = (int) $id;
     $args = array(":domain" => $dom->id,
8132c40e
                 ":hostname" => $record['hostname'],
                 ":type" => $record['type'],
2626dd47
                 ":ttl" => ($record['ttl'] == 0 ? null : (int) $record['ttl']),
8132c40e
                 ":ip" => $record['ip'],
                 ":dyndns" => $record['dyndns'],
                 ":data" => $record['data'],
                 ":spec" => $record['spec']);
2626dd47
     if ($id) {
         $args[":id"] = $id;
         db_query("UPDATE dns.custom_records SET hostname=:hostname, domain=:domain, type=:type, ttl=:ttl, ip=:ip, dyndns=:dyndns, data=:data, spec=:spec WHERE id=:id", $args);
     } else {
         db_query("INSERT INTO dns.custom_records (hostname, domain, type, ttl, ip, dyndns, data, spec) VALUES (:hostname, :domain, :type, :ttl, :ip, :dyndns, :data, :spec)", $args);
     }
361b0a4b
 }
 
 
 function delete_dns_record($id)
 {
2626dd47
     $id = (int) $id;
     // Diese Funktion prüft, ob der Eintrag einer eigenen Domain gehört
     $record = get_dns_record($id);
     db_query("DELETE FROM dns.custom_records WHERE id=?", array($id));
361b0a4b
 }
e45c3ec9
 
0408a9ba
 
 function convert_from_autorecords($domainid)
 {
2626dd47
     $dom = new Domain((int) $domainid);
     $dom->ensure_userdomain();
     $dom = $dom->id;
 
     db_query("INSERT IGNORE INTO dns.custom_records SELECT r.id, r.lastchange, type, d.id, hostname, ip, NULL AS dyndns, data, spec, ttl FROM dns.v_tmptable_allrecords AS r INNER JOIN dns.v_domains AS d ON (d.name=r.domain) WHERE d.id=?", array($dom));
     disable_autorecords($dom);
     db_query("UPDATE dns.dnsstatus SET status='outdated'");
     warning("Die automatischen Einträge werden in Kürze abgeschaltet, bitte haben Sie einen Moment Geduld.");
0408a9ba
 }
 
 
 function enable_autorecords($domainid)
 {
2626dd47
     $dom = new Domain((int) $domainid);
     $dom->ensure_userdomain();
     $dom = $dom->id;
0408a9ba
 
2626dd47
     db_query("UPDATE kundendaten.domains SET autodns=1 WHERE id=?", array($dom));
     db_query("DELETE FROM dns.custom_records WHERE type='ns' AND domain=? AND hostname IS NULL", array($dom));
     warning("Die automatischen Einträge werden in Kürze aktiviert, bitte haben Sie einen Moment Geduld.");
0408a9ba
 }
 
 function disable_autorecords($domainid)
 {
2626dd47
     $dom = new Domain((int) $domainid);
     $dom->ensure_userdomain();
     $dom = $dom->id;
0408a9ba
 
2626dd47
     db_query("UPDATE kundendaten.domains SET autodns=0 WHERE id=?", array($dom));
0408a9ba
 }
 
 
17511dca
 function domain_is_maildomain($domain)
 {
2626dd47
     $domain = (int) $domain;
     $result = db_query("SELECT mail FROM kundendaten.domains WHERE id=?", array($domain));
     $dom = $result->fetch();
     return ($dom['mail'] != 'none');
17511dca
 }
 
0408a9ba
 
8eb67e00
 $own_ns = array();
 
2626dd47
 function own_ns()
 {
     global $own_ns;
8eb67e00
 
2626dd47
     if (count($own_ns) < 1) {
         $auth = dns_get_record(config('masterdomain'), DNS_NS);
         foreach ($auth as $ns) {
             $own_ns[] = $ns['target'];
         }
8eb67e00
     }
 
2626dd47
     return $own_ns;
8eb67e00
 }
 
 
 $tld_ns = array();
 
2626dd47
 function check_dns($domainname, $tld)
 {
     global $tld_ns;
     $domain=idn_to_ascii($domainname.".".$tld, 0, INTL_IDNA_VARIANT_UTS46);
 
     if (! isset($tld_ns[$tld])) {
         $resp = shell_exec('dig @a.root-servers.net. +noall +authority -t ns '.$tld.'.');
         $line = explode("\n", $resp, 2)[0];
         $NS = preg_replace("/^.*\\sIN\\s+NS\\s+(\\S+)$/", '\1', $line);
         $tld_ns[$tld] = $NS;
     }
9086c9ad
 
2626dd47
     $resp = shell_exec('dig @'.$tld_ns[$tld].' +noall +authority -t ns '.$domain.'.');
     $line = explode("\n", $resp, 2)[0];
     if (preg_match('/^.*\\sIN\\s+NS\\s+/', $line) === 0) {
         return "NXDOMAIN";
     }
     $NS = preg_replace("/^.*\\sIN\\s+NS\\s+(\\S+).$/", '\1', $line);
9086c9ad
 
2626dd47
     $own_ns = own_ns();
8eb67e00
 
2626dd47
     if (in_array($NS, $own_ns)) {
         return true;
     }
     return $NS;
8eb67e00
 }
 
2626dd47
 function remove_from_dns($dom)
 {
     $domains = get_domain_list($_SESSION['customerinfo']['customerno'], $_SESSION['userinfo']['uid']);
     $current = null;
     foreach ($domains as $d) {
         if ($d->id == $dom && $d->dns == 1) {
             $current = $d;
             break;
         }
8eb67e00
     }
2626dd47
     if (! $current) {
         system_failure("Domain nicht gefunden!");
     }
     db_query("UPDATE kundendaten.domains SET dns=0 WHERE id=?", array($current->id));
8eb67e00
 }
 
2626dd47
 function add_to_dns($dom)
 {
     $domains = get_domain_list($_SESSION['customerinfo']['customerno'], $_SESSION['userinfo']['uid']);
     $current = null;
     foreach ($domains as $d) {
         if ($d->id == $dom && $d->dns == 0) {
             $current = $d;
             break;
         }
8eb67e00
     }
2626dd47
     if (! $current) {
         system_failure("Domain nicht gefunden!");
     }
     db_query("UPDATE kundendaten.domains SET dns=1, autodns=1 WHERE id=?", array($current->id));
8eb67e00
 }