<?php
/********************************************************************
KeaKeeper

Copyright (C) 2017 DesigNET, INC.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
********************************************************************/


/*****************************************************************************
* Class          : v6type
* Description    : Investigate whether subnet is in keaconf(DHCPv4)
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class v6typeValidate extends AbstractValidate {
    public function run($val, $option = array())
    {
        if ($val == 'ip' || $val == 'prefix') {
            return true;
        }
        return false;
    }
}

/*****************************************************************************
* Class          : subnetinconf4
* Description    : Investigate whether subnet is in keaconf(DHCPv4)
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class subnetinconf4Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        $conf = new KeaConf();
        $ret = $conf->check_subnet4($val);
        if ($ret === false) {
            return false;
        }
        return true;
    }
}

/*****************************************************************************
* Class          : subnetinconf6
* Description    : Investigate whether subnet is in keaconf(DHCPv6)
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class subnetinconf6Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        $conf = new KeaConf();
        $ret = $conf->check_subnet6($val);
        if ($ret === false) {
            return false;
        }
        return true;
    }
}

/*****************************************************************************
* Class          : checkexistipv4Validate
* Description    : Investigate whether the IP address exists in the database
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class checkexistipv4Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        /* make query for check duplicate */
        $cond = ["ipv4_address" => $val];

        $dbutil = new dbutils($this->allval['store']->db);
        $dbutil->select('COUNT(ipv4_address)');
        $dbutil->from('hosts');
        $dbutil->where("ipv4_address = INET_ATON('" . $val . "')");

        /* fetch COUNT query's result */
        $ret = $dbutil->get();

        /* greater than 0, already exists */
        if (max($ret[0]) > 0) {
            return true;
        }
        return false;
    }
}

/*****************************************************************************
* Class          : checkexistipv6Validate
* Description    : Validation class that check duplication
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class checkexistipv6Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        /* make query for check duplicate */
        $dbutil = new dbutils($this->allval['store']->db);
        $dbutil->select('address,prefix_len,type');
        $dbutil->from('ipv6_reservations');

        /* fetch COUNT query's result */
        $ret = $dbutil->get();

        foreach ($ret as $key => $value) {
            if ($value['type'] == 0) {
                /* compare ipv6 address from db */
                $db_addr = inet_pton($value['address']);
                $post_addr = inet_pton($val);

                /* Compare addresses */
                if ($db_addr == $post_addr) {
                    return true;
                }

            } else if ($value['type'] == 2) {
                /* compare range of ipv6 address from db */
                $db_addr = $value['address'];
                $post_prefix = $value['prefix_len'];

                $binPrefix = $this->masktobyte($post_prefix);
                $db_addr_min = inet_pton($db_addr);
                $db_addr_max = inet_pton($db_addr) | ~$binPrefix;
                $post_addr = inet_pton($val);

                /* Compare addresses */
                if ($post_addr >= $db_addr_min && $post_addr <= $db_addr_max) {
                    return true;
                }
            }
        }
        return false;
    }
}

/*****************************************************************************
* Class          : duplicate_delegate6Validate
* Description    : Validation class that check duplication
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class checkexistdelegateValidate extends AbstractValidate {
    public function run($val, $option = array())
    {
        /* make query for check duplicate */
        $dbutil = new dbutils($this->allval['store']->db);
        $dbutil->select('address, prefix_len, type');
        $dbutil->from('ipv6_reservations');

        /* fetch COUNT query's result */
        $ret = $dbutil->get();
        foreach ($ret as $key => $value) {
            if ($value['type'] == 0) {
                /* compare ipv6 address from db */
                $db_addr = inet_pton($value['address']);

                /* range of ipv6 address from post */
                $binPrefix = $this->masktobyte($option[0]);
                $post_addr_max = inet_pton($val) | ~$binPrefix;
                $post_addr_min = inet_pton($val);

                /* Compare addresses */
                if ($db_addr >= $post_addr_min && $db_addr <= $post_addr_max) {
                    return true;
                }

            } else if ($value['type'] == 2) {
                /* compare range of ipv6 address from db */
                $db_addr = $value['address'];
                $post_prefix = $value['prefix_len'];

                $binPrefix = $this->masktobyte($post_prefix);
                $db_addr_min = inet_pton($db_addr);
                $db_addr_max = inet_pton($db_addr) | ~$binPrefix;

                /* range of ipv6 address from post */
                $binPrefix = $this->masktobyte($option[0]);
                $post_addr_max = inet_pton($val) | ~$binPrefix;
                $post_addr_min = inet_pton($val);

                /* Compare addresses */
                if ($post_addr_min <= $db_addr_max && $db_addr_min <= $post_addr_max) {
                    return true;
                }
            }
        }
        return false;
    }
}

/*****************************************************************************
* Class          : equaltosubnetValidate
* Description    : Validation class that ip address in subnet pool
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class equaltosubnetValidate extends AbstractValidate {
    public function run($val, $option = array())
    {

        $subnet = implode(":", $option);
        list($addr, $mask) = explode('/', $subnet);

        if ($val == $mask) {
            return true;
        }
        return false;
    }
}

/*****************************************************************************
* Class          : outpoolValidate
* Description    : Validation class that ip address in subnet pool
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class outpoolValidate extends AbstractValidate {
    public function run($val, $option = array())
    {
        $subnet = $option[0];

        $conf = new KeaConf();
        $pools = $conf->get_pools($subnet);

        /* Returns true if there is no pool */
        if (is_array($pools) === false) {
            return true;
        }

        foreach ($pools as $pool) {
            list($min, $max) = explode(' - ', $pool['pool']);

            $ip_long  = ip2long($val);
            $min_long = ip2long($min);
            $max_long = ip2long($max);

            if ($ip_long >= $min_long && $ip_long <= $max_long) {
                return false;
            }
        }
        return true;
    }
}

/*****************************************************************************
* Class          : outpool_delegate6Validate
* Description    : Validation class that ip address in subnet pool
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class outpool_delegate6Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        /* make str for subnet and prefix */
        $prefix = array_pop($option);
        $subnet = implode(":", $option);

        $conf = new KeaConf();
        $pools = $conf->get_pools6($subnet);

        /* Returns true if there is no pool */
        if (is_array($pools) === false) {
            return true;
        }

        foreach ($pools as $pool) {
            list($min, $max) = explode(' - ', $pool['pool']);

            /* Convert hexadecimal number to binary number */
            $val_addr = inet_pton($val);

            $ret = filter_var($min, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
            if ($ret === false) {
                return false;
            }
            $ret = filter_var($max, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
            if ($ret === false) {
                return false;
            }

            $min      = inet_pton($min);
            $max      = inet_pton($max);

            /* Convert mask value to bytes */
            $binPrefix = $this->masktobyte($prefix);

            /* Convert IPv6 address to byte and mask it */
            $val_max = inet_pton($val) | ~$binPrefix;

            /* Compare addresses */
            if ($val_addr <= $max && $min <= $val_max) {
                return false;
            }
        }
        return true;
    }
}

/*****************************************************************************
* Class          : outpool6Validate
* Description    : Validation class that ip address in subnet pool
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class outpool6Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        $conf = new KeaConf();
        $subnet = implode(":", $option);
        $pools = $conf->get_pools6($subnet);

        /* Convert hexadecimal number to binary number */
        $val = inet_pton($val);

        /* Returns true if there is no pool */
        if (is_array($pools) === false) {
            return true;
        }

        foreach ($pools as $pool) {

            list($min, $max) = explode(' - ', $pool['pool']);

            /* Check the format of the pool */
            $ret = filter_var($min, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
            if ($ret === false) {
                return false;
            }
            $ret = filter_var($max, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
            if ($ret === false) {
                return false;
            }

            $min = inet_pton($min);
            $max = inet_pton($max);

            /* Compare addresses */
            if ($val >= $min && $val <= $max) {
                return false;
            }
        }
        return true;
    }
}

/*************************************************************************
* Class          : ipv6_delegateValidate
* Description    : Validation class that ipv6 address
* args           : $val     - validate values
*                : $options - method options
* return         : true or false
*************************************************************************/
class ipv6_delegateValidate extends AbstractValidate {
    public function run($val, $option = array())
    {
        $ret = filter_var($val, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
        if ($ret === false) {
            return false;
        }

        $prefix = $option[0];
        if ($prefix === NULL) {
            return false;
        }

        /* Convert mask value to bytes */
        $binPrefix = $this->masktobyte($prefix);

        /* Mask by applying logical AND */
        $val_orig = inet_pton($val);
        $val_mask = inet_pton($val) & $binPrefix;

        /* For example, when the prefix is 112,
          an error occurs if the end does not end with:0000 */
        if ($val_orig !== $val_mask) {
            return false;
        }

        return true;
    }
}

/*****************************************************************************
* Class          : insubnet_delegate6Validate
* Description    : Validation class that ip address in subnet
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class insubnet_delegate6Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        /* make str for subnet and prefix */
        $prefix = array_pop($option);
        $subnet = implode(":", $option);

        /* Separate into addresses and netmasks */
        list($net, $mask) = explode("/", $subnet);

        /* When the value of the subnet mask is larger than the prefix */
        if ($mask > $prefix) {
            return false;
        }

        /* Convert mask value to bytes */
        $binMask = $this->masktobyte($mask);

        /* Mask by applying logical AND */
        $maskNet = inet_pton($net) & $binMask; // Mask by applying logical AND
        /* Convert IPv6 address to byte and mask it */
        $maskipv6 = inet_pton($val) & $binMask;

        /* Compare the masked IP part with the masked IPv6 entry */
        /* Since it is out of range unless it is the same, an error */
        if ($maskNet != $maskipv6) {
            return FALSE;
        }
        return TRUE;
    }
}

/*****************************************************************************
* Class          : ipaddrs4Validate
* Description    : Validation class that servers
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class ipaddrs4Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        if (strlen($val) > 256) {
            return false;
        }

        $separated = [];
        if (strpos($val, ',')) {
            $separated = explode(',', $val);
        } else {
            $separated[] = $val;
        }

        foreach ($separated as $host) {
            $ipaddr = ipv4Validate::run($host);

            if ($ipaddr === false) {
                return false;
            }
        }
        return true;
    }
}

/*****************************************************************************
* Class          : serversValidate
* Description    : Validation class that servers
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class serversValidate extends AbstractValidate {
    public function run($val, $option = array())
    {
        if (strlen($val) > 256) {
            return false;
        }

        $separated = [];
        if (strpos($val, ',')) {
            $separated = explode(',', $val);
        } else {
            $separated[] = $val;
        }

        foreach ($separated as $host) {
            $ipaddr = ipv4Validate::run($host);
            $host   = domainValidate::run($host);

            if ($ipaddr === false && $host === false) {
                return false;
            }
        }
        return true;
    }
}

/*****************************************************************************
* Class          : ipaddrs6Validate
* Description    : Validation class that servers
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class ipaddrs6Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        if (strlen($val) > 256) {
            return false;
        }

        $separated = [];
        if (strpos($val, ',')) {
            $separated = explode(',', $val);
        } else {
            $separated[] = $val;
        }

        foreach ($separated as $host) {
            $ipaddr = ipv6Validate::run($host);

            if ($ipaddr === false) {
                return false;
            }
        }
        return true;
    }
}

/*****************************************************************************
* Class          : servers6Validate
* Description    : Validation class that servers
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class servers6Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        if (strlen($val) > 256) {
            return false;
        }

        $separated = [];
        if (strpos($val, ',')) {
            $separated = explode(',', $val);
        } else {
            $separated[] = $val;
        }

        foreach ($separated as $host) {
            $ipaddr = ipv6Validate::run($host);
            $host   = domainValidate::run($host);

            if ($ipaddr === false && $host === false) {
                return false;
            }
        }
        return true;
    }
}

/*****************************************************************************
* Class          : duplicateValidate
* Description    : Validation class that check duplication
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class duplicateValidate extends AbstractValidate {
    public function run($val, $option = array())
    {
        if (count($option) > 1) {
            $val = $option[1]($val);
        }

        /* make query for check duplicate */
        if (count($option) > 2) {
            $cond = [$option[0] => $val, 'dhcp_identifier_type' => $option[2]];
        } else {
            $cond = [$option[0] => $val];
        }

        $dbutil = new dbutils($this->allval['store']->db);
        $dbutil->select('COUNT(' . $option[0] . ')');
        $dbutil->from('hosts');
        $dbutil->where($cond);

        /* fetch COUNT query's result */
        $ret = $dbutil->get();

        /* greater than 0, already exists */
        if (max($ret[0]) > 0) {
            return false;
        }
        return true;
    }
}

/*****************************************************************************
* Class          : duplicate6Validate
* Description    : Validation class that check duplication
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class duplicate6Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        /* make query for check duplicate */
        $dbutil = new dbutils($this->allval['store']->db);
        $dbutil->select('address,prefix_len,type');
        $dbutil->from('ipv6_reservations');

        /* fetch COUNT query's result */
        $ret = $dbutil->get();

        foreach ($ret as $key => $value) {
            if ($value['type'] == 0) {
                /* compare ipv6 address from db */
                $db_addr = inet_pton($value['address']);
                $post_addr = inet_pton($val);

                /* Compare addresses */
                if ($db_addr == $post_addr) {
                    return false;
                }

            } else if ($value['type'] == 2) {
                /* compare range of ipv6 address from db */
                $db_addr = $value['address'];
                $post_prefix = $value['prefix_len'];

                $binPrefix = $this->masktobyte($post_prefix);
                $db_addr_min = inet_pton($db_addr);
                $db_addr_max = inet_pton($db_addr) | ~$binPrefix;
                $post_addr = inet_pton($val);

                /* Compare addresses */
                if ($post_addr >= $db_addr_min && $post_addr <= $db_addr_max) {
                    return false;
                }
            }
        }
        return true;
    }
}

/*****************************************************************************
* Class          : duplicate_delegate6Validate
* Description    : Validation class that check duplication
* args           : $val
*                : $options - method options
* return         : true or false
*****************************************************************************/
class duplicate_delegate6Validate extends AbstractValidate {
    public function run($val, $option = array())
    {
        /* make query for check duplicate */
        $dbutil = new dbutils($this->allval['store']->db);
        $dbutil->select('address, prefix_len, type');
        $dbutil->from('ipv6_reservations');

        /* fetch COUNT query's result */
        $ret = $dbutil->get();
        foreach ($ret as $key => $value) {
            if ($value['type'] == 0) {
                /* compare ipv6 address from db */
                $db_addr = inet_pton($value['address']);

                /* range of ipv6 address from post */
                $binPrefix = $this->masktobyte($option[0]);
                $post_addr_max = inet_pton($val) | ~$binPrefix;
                $post_addr_min = inet_pton($val);

                /* Compare addresses */
                if ($db_addr >= $post_addr_min && $db_addr <= $post_addr_max) {
                    return false;
                }

            } else if ($value['type'] == 2) {
                /* compare range of ipv6 address from db */
                $db_addr = $value['address'];
                $post_prefix = $value['prefix_len'];

                $binPrefix = $this->masktobyte($post_prefix);
                $db_addr_min = inet_pton($db_addr);
                $db_addr_max = inet_pton($db_addr) | ~$binPrefix;

                /* range of ipv6 address from post */
                $binPrefix = $this->masktobyte($option[0]);
                $post_addr_max = inet_pton($val) | ~$binPrefix;
                $post_addr_min = inet_pton($val);

                /* Compare addresses */
                if ($post_addr_min <= $db_addr_max && $db_addr_min <= $post_addr_max) {
                    return false;
                }
            }
        }
        return true;
    }
}
