Unix::Conf::Bind8::Conf::Zone - Class for representing the Bind8 zone


Unix-Conf-Bind8 documentation Contained in the Unix-Conf-Bind8 distribution.

Index


Code Index:

NAME

Top

Unix::Conf::Bind8::Conf::Zone - Class for representing the Bind8 zone directive

SYNOPSIS

Top

    use Unix::Conf::Bind8;

    my ($conf, $zone, $acl, $db, $ret);
    $conf = Unix::Conf::Bind8->new_conf (
        FILE        => '/etc/named.conf',
        SECURE_OPEN => 1,
    ) or $conf->die ("couldn't open `named.conf'");

    #
    # Ways to get a Zone object.
    #

    $zone = $conf->new_zone (
        NAME	=> 'extremix.net',
        TYPE	=> 'master',
        FILE	=> 'db.extremix.net',
    ) or $zone->die ("couldn't create zone");

    # OR

    $zone = $conf->get_zone ('extremix.net')
        or $zone->die ("couldn't get zone");

	#
	# Operations that can be performed on a Zone object.
	#

    $ret = $zone->type ('slave') $ret->die ("couldn't change type");
    $ret = $zone->masters (qw (192.168.1.1 192.168.1.2))
        or $ret->die ("couldn't set masters");

    # create a new acl to be defined before the zone directive
    # 'extremix.net'.
    $acl = $conf->new_acl (
        NAME     => 'extremix.com-slaves', 
        ELEMENTS => [ qw (element1 element2) ],
        WHERE	 => 'BEFORE',
        WARG	 => $zone,
    ) or $acl->die ("couldn't create `extremix.com-slaves'");

    $ret = $zone->allow_transfer ('extremix.com-slaves')
        or $ret->die ("couldn't set `allow-transfer'");

    $ret = $zone->delete_allow_update ()
        or $ret->die ("couldn't delete `allow-update'");

    $db = $zone->get_db () or $db->die ("couldn't get db");
    # Refer to documentation for Unix::Conf::Bind8::DB
    # for manipulating the DB file.

    # delete 
	$ret = $zone->delete () or $ret->die ("couldn't delete");

    # OR

	$ret = $conf->delete_zone ('extremix.net')
		or $ret->die ("couldn't delete zone `extremix.net'");

METHODS

Top

new ()
 Arguments
 NAME             => 'name',
 TYPE             => 'type',            # 'master'|'slave'|'forward'|'stub'|'hint'
 CLASS            => 'class',           # 'in'|'hs'|'hesiod'|'chaos'
 FILE             => 'pathname',
 MASTERS          => { 					# only if TYPE =~  /'slave'|'stub'/
 						PORT => 'port'	# optional
						ADDRESS => [ qw (ip1 ip2) ],
					 },	
 FORWARD          => 'yes_no',
 FORWARDERS       => [ qw (ip1 ip2) ],
 CHECK-NAMES      => 'value',           # 'warn'|'fail'|'ignore'
 ALLOW-UPDATE     => [ qw (host1 host2) ],
 ALLOW-QUERY      => [ qw (host1 host2) ],
 ALLOW-TRANSFER   => [ qw (host1 host2) ],
 DIALUP           => 'yes_no',
 NOTIFY           => 'yes_no',
 ALSO-NOTIFY      => [ qw (ip1 ip2) ],
 WHERE  => 'FIRST'|'LAST'|'BEFORE'|'AFTER'
 WARG   => Unix::Conf::Bind8::Conf::Directive subclass object
                        # WARG is to be provided only in case WHERE eq 'BEFORE 
                        # or WHERE eq 'AFTER'
 PARENT	=> reference,   # to the Conf object datastructure.

Class constructor Creates a new Unix::Conf::Bind8::Conf::Zone object and returns it if successful, an Err object otherwise. Do not use this constructor directly. Use Unix::Conf::Bind8::Conf::new_zone () instead.

name ()
 Arguments
 'zone',    # optional

Object method. Get/Set object's name attribute. If argument is passed, the method tries to set the name attribute to 'zone', and returns true if successful, an Err object otherwise. If no argument is passed, returns the name of the zone, if defined, an Err object otherwise.

class ()
 Arguments
 'class',     # optional

Object method. Get/Set object's class attribute. If argument is passed, the method tries to set the class attribute to 'class', and returns true if successful, an Err object otherwise. If no argument is passed, returns the class of the zone, if defined, an Err object otherwise.

file ()
 Arguments
 'file',    # optional

Object method. Get/Set the object's file attribute. If argument is passed, the method tries to set the file attribute to 'file', and returns true if successful, and Err object otherwise. If no argument is passed, returns the file of the zone, if defined, an Err object otherwise.

type ()
 Arguments
 'type',    # optional

Object method. Get/Set the object's type attribute. If argument is passed, the method tries to set the type attribute to 'type', and returns true if successful, an Err object otherwise. If no argument is passed, returns the type of the zone, if defined, an Err object otherwise.

forward ()
notify ()
dialup ()
 Arguments
 SCALAR,		# 'yes'|'no'|0|1

Object method Get/set corresponding attribute in the invocant. If argument is passed, the method tries to set it as the value and returns true if successful, an Err object otherwise. If no argument is passed the value of that attribute is returned if defined, an Err object otherwise.

check_names ()
 Arguments
 string,		# 'warn'|'fail'|'ignore'

Object method Get/set corresponding attribute in the invocant. If argument is passed, the method tries to set it as the value and returns true if successful, an Err object otherwise. If no argument is passed the value of that attribute is returned if defined, an Err object otherwise.

transfer_source ()
 Arguments
 string,		# IPv4 address in dotted quad notation

Object method Get/set corresponding attribute in the invocant. If argument is passed, the method tries to set it as the value and returns true if successful, an Err object otherwise. If no argument is passed the value of that attribute is returned if defined, an Err object otherwise.

max_transfer_time_in ()
 Arguments
 number,		

Object method Get/set corresponding attribute in the invocant. If argument is passed, the method tries to set it as the value and returns true if successful, an Err object otherwise. If no argument is passed the value of that attribute is returned if defined, an Err object otherwise.

also_notify ()
forwarders ()
 Arguments
 LIST			# List of IPv4 addresses in 
 or 			# dotted quad notation
 [ LIST ]

Object method. Get/set the corresponding attribute in the invoking object. If argument(s) is/are passed, the method tries to set the attribute and returns true on success, an Err object otherwise. If no arguments are passed then the method tries to return an array reference if the attribute is defined, an Err object otherwise.

add_to_also_notify ()
add_to_forwarders ()
add_to_masters ()
 Arguments
 LIST			# List of IPv4 addresses in
 or				# dotted quad notation.
 [ LIST ]

Object method. Add the elements of the list to the corresponding attribute. Return true on success, an Err object otherwise.

delete_from_also_notify ()
delete_from_forwarders ()
delete_from_masters ()
 Arguments
 LIST			# List of IPv4 addresses in
 or				# dotted quad notation.
 [ LIST ]

Object method. Delete elements of the list from the corresponding attribute. Return true on success, an Err object otherwise.

allow_transfer ()
allow_query ()
allow_update ()
 Arguments
 Acl object,
 or
 LIST
 or 
 [ LIST ]

Object method. If argument(s) is/are passed, tries to set the elements of the corresponding attribute and returns true on success, an Err object otherwise. If no arguments are passed, tries to return the elements defined for that attribute as an anonymous array, if defined, an Err object otherwise.

add_to_allow_transfer ()
add_to_allow_query ()
add_to_allow_update ()
delete_from_allow_transfer ()
delete_from_allow_query ()
delete_from_allow_update ()
 Arguments
 LIST
 [ LIST ]

Object method. Add to/delete from elements defined for the corresponding attributes. Returns true on success, an Err object otherwise.

delete_forward ()
delete_notify ()
delete_dialup ()
delete_check_names ()
delete_transfer_source ()
delete_max_transfer_time_in ()
delete_also_notify ()
delete_forwarders ()
delete_allow_transfer ()
delete_allow_query ()
delete_allow_update ()
delete_file ()
delete_class ()
delete_type ()
delete_masters ()
delete_also_notify ()
delete_forwarders ()
delete_pubkey ()

Object method. Deletes the corresponding attribute, if defined and returns true, an Err object otherwise.

masters ()
 Arguments
 PORT		=> port,	# optional
 ADDRESS	=> [ LIST ],

Object method. Get/sets the 'masters' attribute. If argument is passed, the attribute is set to the argument and returns true on success, an Err object otherwise. If not the attribute value is returned in the form of an anonymous array ([ PORT, [ LIST OF ADDRESSES ] ]), if defined, an Err object otherwise.

masters_port ()
 Arguments
 'port',     # optional

Object method. Get/Set the object's masters port attribute. If argument is passed, the method tries to set the masters port attribute to 'port', and returns true if successful, an Err object otherwise. If no argument is passed, returns the masters port, if defined, an Err object otherwise.

pubkey ()
 Arguments
 LIST			# flags, protocol, algorithm, string
 or 
 [ LIST ]		# same structure

delete_directive ()
 Arguments
 'directive',

Object method. Deletes the directive passed as argument, if defined, and returns true, an Err object otherwise.

get_db ()
 Arguments,
 number,    # 0/1 secure open

Constructor This method is a wrapper method of the class constructor of the Unix::Conf::Bind8::DB class. Creates and returns a new Unix::Conf::Bind8::DB object representing the records file for the zone, if successful, an error object otherwise.


Unix-Conf-Bind8 documentation Contained in the Unix-Conf-Bind8 distribution.
# Bind8 Zone handling
#
# Copyright Karthik Krishnamurthy <karthik.k@extremix.net>

package Unix::Conf::Bind8::Conf::Zone;

use strict;
use warnings;
use Unix::Conf;

use Unix::Conf::Bind8::Conf::Directive;
our @ISA = qw (Unix::Conf::Bind8::Conf::Directive);

use Unix::Conf::Bind8::Conf;
use Unix::Conf::Bind8::Conf::Lib;
use Unix::Conf::Bind8::Conf::Acl;

# dont become too restrictive. i am putting in validations offhand.
# recheck with Bind behaviour.
# Arguments: zone class
# INCOMPLETE
sub validate ($)
{
	my ($zone) = @_;
	my $errmsg = "";

	($zone->type () eq 'master') && do {
		$errmsg .= sprintf ("no records file defined for master zone `%s'\n", $zone->name ())
			if (! $zone->file ());
		$errmsg .= sprintf ("masters defined for master zone `%s'\n", $zone->name ()) 
			if ($zone->masters ()); 
	};
	($zone->type () eq 'slave') && do {
		$errmsg .= sprintf ("masters not defined for slave zone `%s'\n", $zone->name ())
			if (! $zone->masters ());
	};
	($zone->type () eq 'forward') && do {
		$errmsg .= sprintf ("masters defined for forward zone `%s'\n", $zone->name ()) 
			if ($zone->masters ()); 
		$errmsg .= sprintf ("forward not defined for forward zone `%s'\n", $zone->name ()) 
			if (! $zone->forward ());
		$errmsg .= sprintf ("forwarders not defined for forward zone `%s'\n", $zone->name ()) 
			if (! $zone->forwarders ());
	};

	return ($errmsg) if ($errmsg);
	return ();
}

# change to access the hash members directly instead of thro the methods.
# that should speed up things a bit
sub __render
{
	my $self = $_[0];
	my ($rendered, $tmp);

	# name class { type
	if ($self->__defined_class ()) {
		$rendered = sprintf (qq (zone "%s" %s {\n\ttype %s;\n), $self->name (), $self->class (), $self->type ());
	}	
	else {
		$rendered = sprintf (qq (zone "%s" {\n\ttype %s;\n), $self->name (), $self->type ());
	}

	$rendered .= qq (\tfile "$tmp";\n)
		if (($tmp = $self->file ()));
	if (($tmp = $self->masters ())) {
		local $" = "; ";
		$rendered .= sprintf (qq (\tmasters %s{\n\t\t@{$tmp->[1]};\n\t};\n), 
			defined ($tmp->[0]) ? "port $tmp->[0] " : "");
	}

	$rendered .= qq (\tforward $tmp;\n)
		if (($tmp = $self->forward ()));
	# list can be empty.
	if (($tmp = $self->forwarders ())) {
		local $" = "; ";
		$rendered .= qq (\tforwarders {);
		# the array might be empty. print `{};' in such cases
		$rendered .= qq (\n\t\t@{$tmp};\n\t) if (@$tmp);
		$rendered .= qq (};\n);
	}

	$rendered .= qq (\tcheck-names $tmp;\n)
		if (($tmp = $self->check_names ()));

	$rendered .= qq (\tnotify $tmp;\n)
		if (($tmp = $self->notify ()));
	# list can be empty
	if (($tmp = $self->also_notify ())) {
		local $" = "; ";
		$rendered .= qq (\talso-notify {);
		$rendered .= qq (\n\t\t@{$tmp};\n\t) if (@$tmp);
		$rendered .= qq (};\n);
	}

	# The values are represented by an ACL. Get the elements, stringify it
	# and set the ACL to clean, so that the destructors do not write it to file
	{
		$rendered .= "\tallow-update " . ${$tmp->_rstring (undef, 1)} . "\n"
			if (($tmp = $self->allow_update ())); 
		$rendered .= "\tallow-query " . ${$tmp->_rstring (undef, 1)} . "\n"
			if (($tmp = $self->allow_query ()));
		$rendered .= "\tallow-transfer " . ${$tmp->_rstring (undef, 1)} . "\n"
			if (($tmp = $self->allow_transfer ()));
	}
	#local $" = " ";
	$rendered .= qq/\tpubkey @{$tmp}[0..2] "$tmp->[3]";\n/
		if ($tmp = $self->pubkey ());

	$rendered .= "};";
	return ($self->_rstring (\$rendered));
}


my %ZoneDirectives = (
	'forward'			=> \&__valid_yesno,
	'notify'			=> \&__valid_yesno,
	'dialup'			=> \&__valid_yesno,
	'check-names'		=> \&__valid_checknames,
	'transfer-source'	=> \&__valid_ipaddress,
	'max-transfer-time-in'
						=> \&__valid_number,

	'also-notify'		=> 'IPLIST',
	'forwarders'		=> 'IPLIST',


	'allow-transfer'	=> 'acl',
	'allow-query'		=> 'acl',
	'allow-update'		=> 'acl',

	# can't delete the 'name' attribute
	'name'				=> 0,
	'file'				=> 1,
	'class'				=> 1,
	'type'				=> 1,
	'masters'			=> 1,
	'pubkey'			=> 1,
);


sub new
{
	shift ();
	my $new = bless ({});
	my %args = @_;
	my ($ret, $acl);
	
	$args{NAME} || return (Unix::Conf->_err ('new', "zone name not defined"));
	$args{PARENT} || return (Unix::Conf->_err ('new', "PARENT not defined"));
	my $where = $args{WHERE} ? $args{WHERE} : 'LAST';
	my $warg = $args{WARG};
	$ret = $new->_parent ($args{PARENT}) or return ($ret);
	$ret = $new->name ($args{NAME}) or return ($ret);
	delete (@args{'NAME','PARENT','WHERE','WARG'});

	# now what is left in %args are zone attributes.
	for (keys (%args)) {
		my $meth = $_;
		$meth =~ tr/A-Z/a-z/;
		return (Unix::Conf->_err ("new", "attribute `$meth' not supported"))
			unless (defined ($ZoneDirectives{$meth}));
		$meth =~ tr/-/_/;
		($_ eq 'MASTERS')	&& do {
			$ret = $new->$meth (%{$args{$_}}) or return ($ret);
			next;
		};
		$ret = $new->$meth ($args{$_}) or return ($ret);
	}
	$ret = Unix::Conf::Bind8::Conf::_insert_in_list ($new, $where, $warg)
		or return ($ret);
	return ($new);
}

sub name
{
	my ($self, $name) = @_;

	if (defined ($name)) {
		my $ret;
		# strip the double quotes if any
		$name =~ s/^"(.+)"$/$1/;
		# already defined. changing name
		if ($self->{name}) {
			$ret = Unix::Conf::Bind8::Conf::_del_zone ($self) or return ($ret);
		}
		$self->{name} = $name;
		$ret = Unix::Conf::Bind8::Conf::_add_zone ($self) or return ($ret);
		$self->dirty (1);
		return (1);
	}
	return ($self->{name});
}

sub class
{
	my ($self, $class) = @_;

	if (defined ($class)) {
		return (Unix::Conf->_err ('class', "illegal class `$class'"))
			if ($class !~ /^(in|hs|hesoid|chaos)$/i);
		$self->{class} = lc ($class);
		return (1);
	}
	return ( defined ($self->{class}) ? $self->{class} : "IN" );
}

sub __defined_class { return ( defined ($_[0]->{class}) ); }

sub file
{
	my ($self, $file) = @_;

	if (defined ($file)) {
		# strip the double quotes if any
		$file =~ s/^"(.+)"$/$1/;
		$self->{file} = $file;
		$self->dirty (1);
		return (1);
	}
	return (
		defined ($self->{file}) ? $self->{file} : Unix::Conf->_err ('file', "file not defined")
	);
}

sub type
{
	my ($self, $type) = @_;

	if (defined ($type)) {
		return (Unix::Conf->_err ('type', "illegal type `$type'"))
			if ($type !~ /^(hint|master|slave|stub|forward)$/); 
		$self->{type} = $type;
		$self->dirty (1);
		return (1);
	}
	return ($self->{type});
}

for my $dir (keys (%ZoneDirectives)) {
	no strict 'refs';

	my $meth;
	($meth = $dir) =~ tr/-/_/;

	($ZoneDirectives{$dir} eq 'IPLIST')	&& do {
		*$meth = sub {
			my $self = shift ();
			my $addresses;

			if (@_) {
				if (ref ($_[0])) {
					return (
						Unix::Conf->_err (
							"$meth", 
							"expected arguments are a list or an array reference"
						)
					) unless (UNIVERSAL::isa ($_[0], 'ARRAY'));
					$addresses = $_[0];
				}
				else {
					$addresses = \@_;
				}
				for (@$addresses) {
					return (Unix::Conf->_err ("$meth", "illegal IP address `$_'"))
						if (! __valid_ipaddress ($_));
				}
				# reinit
				$self->{$dir} = undef;
				@{$self->{$dir}}{@$addresses} = (1) x @$addresses;
				$self->dirty (1);
				return (1);
			}

			return (
				defined ($self->{$dir}) ? [ keys (%{$self->{$dir}}) ] :
					Unix::Conf->_err ("$meth", "zone directive `$dir' not defined")
			)
		};

		*{"add_to_$meth"} = sub {
			my $self = shift ();
			my $addresses;

			if (@_) {
				if (ref ($_[0])) {
					return (
						Unix::Conf->_err (
							"add_to_$meth", 
							"expected arguments are a list or an array reference"
						)
					) unless (UNIVERSAL::isa ($_[0], 'ARRAY'));
					$addresses = $_[0];
				}
				else {
					$addresses = \@_;
				}
				for (@$addresses) {
					return (Unix::Conf->_err ("add_to_$meth", "illegal IP address `$_'"))
						if (! __valid_ipaddress ($_));
					return (
						Unix::Conf->_err ( "add_to_$meth", "address `$_' already defined" )
					) if ($self->{$dir}{$_});
				}
				@{$self->{$dir}}{@$addresses} = (1) x @$addresses;
				$self->dirty (1);
				return (1);
			}
			return (Unix::Conf->_err ("add_to_$meth", "addresses to be added not passed"));
		};

		*{"delete_from_$meth"} = sub {
			my $self = shift ();
			my $addresses;

			if (@_) {
				if (ref ($_[0])) {
					return (
						Unix::Conf->_err (
							"delete_from_$meth", 
							"expected arguments are a list or an array reference"
						)
					) unless (UNIVERSAL::isa ($_[0], 'ARRAY'));
					$addresses = $_[0];
				}
				else {
					$addresses = \@_;
				}
				for (@$addresses) {
					return (Unix::Conf->_err ("delete_from_$meth", "illegal IP address `$_'"))
						if (! __valid_ipaddress ($_));
					return (
						Unix::Conf->_err ( "delete_from_$meth", "address `$_' not defined" )
					) unless ($self->{$dir}{$_});
				}
				delete (@{$self->{$dir}}{@$addresses});
				# if no keys left delete the zone directive itself
				delete ($self->{$dir})
					unless (keys (%{$self->{$dir}}));
				$self->dirty (1);
				return (1);
			}
			return (Unix::Conf->_err ("delete_from_$meth", "addresses to be deleted not passed"));
		};
		goto CREATE_DELETE;
	};

	# zone directives taking Acl as arguments.
	($ZoneDirectives{$dir} eq 'acl')	&& do {
		*$meth = sub {
			my $self = shift ();
			my $elements;

			if (@_) {
				if (ref ($_[0])) {
					if (UNIVERSAL::isa ($_[0], 'Unix::Conf::Bind8::Conf::Acl')) {
						# Acl object passed
						$self->{$dir} = $_[0];
						$self->dirty (1);
						return (1);
					}
					elsif (UNIVERSAL::isa ($_[0], 'ARRAY')) {
						# array ref was passed
						return (Unix::Conf->_err ("$meth", "array passed by reference empty"))
							unless (@{$_[0]});
						$elements = $_[0];
					}
					else {
						return (	
							Unix::Conf->_err (
								"$meth", 
								"expected arguments are a list, an Unix::Conf::Bind8::Conf::Acl object or an array ref"
							)
						);
					}
				}
				else {
					# assume a list of elements to be set was passed.
					$elements = \@_;
				}

				my $acl;
				$acl = Unix::Conf::Bind8::Conf::Acl->new (
					PARENT => $self->_parent (), ELEMENTS => $elements,
				) or return ($acl);
				$self->{$dir} = $acl;
				$self->dirty (1);
				return (1);
			}
			return (
				defined ($self->{$dir}) ? 
					$self->{$dir} : 
					Unix::Conf->_err ("$meth", "zone directive `$dir' not defined")
			);
		};

		# add_to_* counterpart for options taking ACL elements as arguments
		*{"add_to_$meth"} = sub {
			my $self = shift ();
			my ($elements, $ret);

			return (Unix::Conf->_err ("add_to_$meth", "elements to be added not passed"))
				unless (@_);

			if (ref ($_[0])) {
				return (
					Unix::Conf->_err (
						"add_to_$meth", 
						"expected arguments are either a list of elements or an array ref")
				) unless (UNIVERSAL::isa ($_[0], 'ARRAY'));
				return (Unix::Conf->_err ("add_to_$meth", "array passed by reference empty"))
					unless (@{$_[0]});
				$elements = $_[0];
			}
			else {
				$elements = [ @_ ];
			}
			$self->{$dir} = Unix::Conf::Bind8::Conf::Acl->new (
				PARENT => $self->_parent ()
			) unless (defined ($self->{$dir}));
			$ret = $self->{$dir}->add_elements ($elements) or return ($ret);
			$self->dirty (1);
			return (1);
		};

		# delete_from_* counterpart for options taking ACL elements as arguments
		*{"delete_from_$meth"} = sub {
			my $self = shift ();
			my ($elements, $ret);

			return (Unix::Conf->_err ("delete_from_$meth", "elements to be added not passed"))
				unless (@_);

			if (ref ($_[0])) {
				return (
					Unix::Conf->_err (
						"delete_from_$meth", 
						"expected arguments are either a list of elements or an array ref")
				) unless (UNIVERSAL::isa ($_[0], 'ARRAY'));
				return (Unix::Conf->_err ("delete_from_$meth", "array passed by reference empty"))
					unless (@{$_[0]});
				$elements = $_[0];
			}
			else {
				$elements = [ @_ ];
			}

			return (Unix::Conf::->_err ("delete_from_$meth", "zone directive `$dir' not defined"))
				unless (defined ($self->{$dir}));
			$ret = $self->{$dir}->delete_elements ($elements) or return ($ret);
			# if all elements have been deleted, delete the option itself.
			delete ($self->{$dir})
				unless (@{$self->{$dir}->elements ()});
			$self->dirty (1);
			return (1);
		};
		# *_elements
		*{"${meth}_elements"} = sub {
			return (
				defined ($_[0]->{$dir}) ? $_[0]->{$dir}->elements () : 
					Unix::Conf->_err ("{$meth}_elements", "zone directive $dir not defined")
			);
		};
		goto CREATE_DELETE;
	};

	("$ZoneDirectives{$dir}" =~ /^CODE/)	&& do {
		*$meth = sub {
				my ($self, $arg) = @_;
				
				if (defined ($arg)) {
					return (Unix::Conf->_err ("$meth", "invalid argument $arg"))
						unless (&{$ZoneDirectives{$dir}}($arg));
					$self->{$dir} = $arg;
					$self->dirty (1);
					return (1);
				}
				return (
					defined ($self->{$dir}) ? 
					$self->{$dir} : 
					Unix::Conf->_err ("$meth", "zone directive `$dir' not defined")
				);
		};
	};
CREATE_DELETE:
	# delete_* to be created only for directives which have true value.
	# will not be created for name.
	($ZoneDirectives{$dir})					&& do {
		*{"delete_$meth"} = sub {
			return (Unix::Conf->_err ("delete_$meth", "zone directive `$dir' not defined"))
				unless (defined ($_[0]->{$dir}));
			delete ($_[0]->{$dir});
			$_[0]->dirty (1);
			return (1);
		};
	};
}

sub masters
{
	my $self = shift ();
	
	if (@_) {
		my %args = ( @_ );
		
		$self->{masters} = undef;
		if (defined ($args{PORT})) {
			return (Unix::Conf->_err ("masters", "illegal PORT `$args{PORT}'"))
				unless (__valid_port ($args{PORT}));
			$self->{masters}[0] = $args{PORT};
		}
		for (@{$args{ADDRESS}}) {
			return (Unix::Conf->_err ("masters", "illegal IP address `$_'"))
				if (! __valid_ipaddress ($_));
		}
		# reinit
		@{$self->{masters}[1]}{@{$args{ADDRESS}}} = (1) x @{$args{ADDRESS}};
		$self->dirty (1);
	}

	return (Unix::Conf->_err ("masters", "zone directive `masters' not defined"))
		unless ($self->{masters});
	return ([ $self->{masters}[0], [ keys (%{$self->{masters}[1]}) ] ]);
}

sub add_to_masters
{
	my $self = shift ();
	my $addresses;

	if (@_) {
		if (ref ($_[0])) {
			return (
				Unix::Conf->_err (
					"add_to_masters", 
					"expected arguments are a list or an array reference"
				)
			) unless (UNIVERSAL::isa ($_[0], 'ARRAY'));
			$addresses = $_[0];
		}
		else {
			$addresses = \@_;
		}
		for (@$addresses) {
			return (Unix::Conf->_err ("add_to_masters", "illegal IP address `$_'"))
				if (! __valid_ipaddress ($_));
			return (
				Unix::Conf->_err ( "add_to_masters", "address `$_' already defined")
			) if ($self->{masters}[1]{$_});
		}
		@{$self->{masters}[1]}{@$addresses} = (1) x @$addresses;
		$self->dirty (1);
		return (1);
	}
	return (Unix::Conf->_err ("add_to_masters", "addresses to be added not passed"));
}

sub delete_from_masters
{
	my $self = shift ();
	my $addresses;

	if (@_) {
		if (ref ($_[0])) {
			return (
				Unix::Conf->_err (
					"delete_from_masters", 
					"expected arguments are a list or an array reference"
				)
			) unless (UNIVERSAL::isa ($_[0], 'ARRAY'));
			$addresses = $_[0];
		}
		else {
			$addresses = \@_;
		}
		for (@$addresses) {
			return (Unix::Conf->_err ("delete_from_masters", "illegal IP address `$_'"))
				if (! __valid_ipaddress ($_));
			return (
				Unix::Conf->_err ( "delete_from_masters", "address `$_' not defined" )
			) unless ($self->{masters}[1]{$_});
		}
		delete (@{$self->{masters}[1]}{@$addresses});
		# if no keys left delete the zone directive itself
		delete ($self->{masters})
			unless (keys (%{$self->{masters}[1]}));
		$self->dirty (1);
		return (1);
	}
	return (Unix::Conf->_err ("delete_from_masters", "addresses to be deleted not passed"));
}

sub masters_port
{
	my ($self, $port) = @_;

	if (defined ($port)) {
		return (Unix::Conf->_err ("masters", "illegal PORT `$port'"))
			unless (__valid_port ($port));
		$self->{masters}[0] = $port;
		$self->dirty (1);
		return (1);
	}
	return (
		defined ($self->{masters}[0]) ? $self->{masters}[0] : 
			Unix::Conf->_err ('masters_port', "masters port not defined")
	);
}

sub pubkey 
{
	my $self = shift ();
	my $args;

	return (
		defined ($self->{pubkey}) ? [ @{$self->{pubkey}} ] :
			Unix::Conf->_err ('pubkey', "zone directive `pubkey' not defined")
	) unless (@_);

	if (ref ($_[0])) {
		return (Unix::Conf->_err ('pubkey', ""))
			unless (UNIVERSAL::isa ($_[0], 'ARRAY'));
		 $args = [ @{$_[0]} ];
	}
	elsif (@_ == 4) {
		$args = [ @_ ];
	}
	else {
		return (
			Unix::Conf->_err (
				'pubkey', "expected arguments are LIST (flags, protocol, algorithm, key) or [ LIST ]"
			)
		);
	}
	# strip quotes if any.
	$args->[3] =~ s/^"(.+)"$/$1/;
	$self->{pubkey} = $args;
	$self->dirty (1);
	return (1);
}

sub delete_directive
{
	my ($self, $dir) = @_;

	return (Unix::Conf->_err ('delete_zonedir', "directive to be deleted not passed"))
		unless ($dir);
	# validate $dir 
	return (Unix::Conf->_err ('delete_zonedir', "illegal zone directive `$dir'"))
		if ($dir !~ /^(type|file|masters|check-names|allow-update|allow-query|allow-transfer|forward|forwarders|transfer-source|max-transfer-time-in|notify|also-notify)$/);
	return (Unix::Conf->_err ('delete_zonedir', "cannot delete `$dir'"))
		if ($dir =~ /^(name|type)$/);
	undef ($self->{$dir});
	$self->dirty (1);
	return (1);
}

sub get_db
{
	require Unix::Conf::Bind8::DB;
	my ($self, $secure_open) = @_;
	$secure_open = 1 unless (defined ($secure_open));

	return (
		Unix::Conf::Bind8::DB::->new (
			FILE		=> $self->file (),
			ORIGIN		=> $self->name (),
			CLASS		=> uc ($self->class ()),
			SECURE_OPEN	=> $secure_open
		)
	);
}

1;