diff --git a/package/yast2-dns-server.changes b/package/yast2-dns-server.changes index 98ec9cf..532134b 100644 --- a/package/yast2-dns-server.changes +++ b/package/yast2-dns-server.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Wed May 25 11:11:30 UTC 2016 - mvidner@suse.com + +- Keep non-ACL items in allow-transfer AKA Enable Zone Transport + (bsc#976643#c23) +- Fix parsing 'keyword{value;};' (no spaces) in named.conf + (bsc#976643#c16). +- 3.1.21 + ------------------------------------------------------------------- Wed May 4 08:45:09 UTC 2016 - cwh@suse.com diff --git a/package/yast2-dns-server.spec b/package/yast2-dns-server.spec index 6e1a2fb..670ec8c 100644 --- a/package/yast2-dns-server.spec +++ b/package/yast2-dns-server.spec @@ -17,7 +17,7 @@ Name: yast2-dns-server -Version: 3.1.20 +Version: 3.1.21 Release: 0 Url: https://github.com/yast/yast-dns-server diff --git a/src/include/dns-server/dialog-masterzone.rb b/src/include/dns-server/dialog-masterzone.rb index 6f83c34..73ae15f 100644 --- a/src/include/dns-server/dialog-masterzone.rb +++ b/src/include/dns-server/dialog-masterzone.rb @@ -58,6 +58,22 @@ def initialize_dns_server_dialog_masterzone(include_target) @current_zone_forwarders = [] end + BUILTIN_ACLS = ["any", "none", "localhost", "localnets"] + + # ACL names to present in a multiselection box + def acl_names + acls = DnsServer.GetAcl + names = acls.map do |a| + a.strip.split(/[ \t]/).fetch(0, "") + end + # bsc#976643#c23 + names = (names + current_zone_allow_transfer + BUILTIN_ACLS).sort.uniq + # bug #203910 + # hide "none" from listed ACLs + # "none" means, not allowed and thus multiselectbox of ACLs is disabled + names.find_all {|a| a != "none"} + end + # Dialog Tab - Zone Editor - Basics # @return [Yast::Term] for Get_ZoneEditorTab() def GetMasterZoneEditorTabBasics @@ -65,24 +81,6 @@ def GetMasterZoneEditorTabBasics updater_keys = Builtins.maplist(updater_keys_m) do |m| Ops.get_string(m, "key", "") end - acl = DnsServer.GetAcl - acl = Builtins.maplist(acl) do |a| - while Builtins.substring(a, 0, 1) == " " || - Builtins.substring(a, 0, 1) == "\t" - a = Builtins.substring(a, 1) - end - s = Builtins.splitstring(a, " \t") - type = Ops.get(s, 0, "") - type - end - acl = Builtins.filter(acl) { |a| a != "" } - acl = Convert.convert( - Builtins.sort( - Builtins.merge(acl, ["any", "none", "localhost", "localnets"]) - ), - :from => "list", - :to => "list " - ) expert_settings = Empty() if DnsServer.ExpertUI @@ -114,11 +112,6 @@ def GetMasterZoneEditorTabBasics ) end - # bug #203910 - # hide "none" from listed ACLs - # "none" means, not allowed and thus multiselectbox of ACLs is disabled - acl = Builtins.filter(acl) { |one_acl| one_acl != "none" } - @available_zones_to_connect = [] zone_name = "" zones_to_connect = Builtins.maplist(@zones) do |z| @@ -160,7 +153,7 @@ def GetMasterZoneEditorTabBasics # multi selection box VSquash( HSquash( - MinWidth(30, MultiSelectionBox(Id("acls_list"), _("ACLs"), acl)) + MinWidth(30, MultiSelectionBox(Id("acls_list"), _("ACLs"), acl_names)) ) ) ) @@ -207,37 +200,35 @@ def GetMasterZoneEditorTabBasics deep_copy(contents) end - def ZoneAclInit - allowed = false - keys = [] - Builtins.foreach(Ops.get_list(@current_zone, "options", [])) do |m| - if Ops.get_string(m, "key", "") == "allow-transfer" && !allowed - key = Builtins.regexpsub( - Ops.get_string(m, "value", ""), - "^.*\\{[ \t]*(.*)[ \t]*\\}.*$", - "\\1" - ) - if key != nil - keys = Builtins.splitstring(key, " ;") - keys = Builtins.filter(keys) { |k| k != "" } - allowed = true - end - end + # @return [Array] + def current_zone_allow_transfer + target_pair = @current_zone.fetch("options", []).find do |m| + m["key"] == "allow-transfer" end + return [] unless target_pair + + value = target_pair["value"] || "" + value = value[/\A.*\{[ \t]*(.*)[ \t]*\}.*\z/, 1] + return [] unless value + + value.split(/[ \t;]/).reject(&:empty?) + end + + def ZoneAclInit + keys = current_zone_allow_transfer # bug #203910 # no keys in allow-transfer means that transfer is allowed for all # explicitly say that - if Builtins.size(keys) == 0 - allowed = true + if keys.empty? keys = ["any"] # the only way how to disable the transfer is to set "allow-transfer { none; };" # "none" must be alone, remove it from the list, it is not present in the multi-sel box - elsif Builtins.size(keys) == 1 && keys == ["none"] - allowed = false + elsif keys == ["none"] keys = [] end + allowed = !keys.empty? UI.ChangeWidget(Id("enable_zone_transport"), :Value, allowed) UI.ChangeWidget(Id("acls_list"), :Enabled, allowed) UI.ChangeWidget(Id("acls_list"), :SelectedItems, keys) if allowed @@ -2670,23 +2661,6 @@ def runMasterZoneTabDialog # Dialog Zone Editor - Slave # @return [Object] dialog result for wizard def runSlaveZoneTabDialog - acl = Builtins.maplist(DnsServer.GetAcl) do |acl_record| - acl_splitted = Builtins.splitstring(acl_record, " \t") - Ops.get(acl_splitted, 0, "") - end - acl = Convert.convert( - Builtins.sort( - Builtins.merge(acl, ["any", "none", "localhost", "localnets"]) - ), - :from => "list", - :to => "list " - ) - - # bug #203910 - # hide "none" from listed ACLs - # "none" means, not allowed and thus multiselectbox of ACLs is disabled - acl = Builtins.filter(acl) { |one_acl| one_acl != "none" } - zone_name = Ops.get_string(@current_zone, "zone", "") contents = VBox( HBox( @@ -2717,7 +2691,7 @@ def runSlaveZoneTabDialog ) ), # multi selection box - VSquash(MultiSelectionBox(Id("acls_list"), _("ACLs"), acl)), + VSquash(MultiSelectionBox(Id("acls_list"), _("ACLs"), acl_names)), VStretch() ) diff --git a/src/modules/DnsData.pm b/src/modules/DnsData.pm index 6b6ef9c..59be6da 100644 --- a/src/modules/DnsData.pm +++ b/src/modules/DnsData.pm @@ -32,6 +32,23 @@ our $chroot = 0; my @allowed_interfaces = (); +=head3 @zones + +See also L + +One zone is a hash with this content: + +=over + +=item - "modified" -> boolean + +=item - "options" -> list of hashes {"key" => ..., "value" => ...} + +=item - ... + +=back + +=cut my @zones = (); my @options = (); @@ -50,8 +67,24 @@ my $save_all = 0; my @files_to_delete = (); +=head3 %current_zone + +A copy of an item from @zones + +Get it with SelectZone(integer); see also FindZone(string) -> integer + +Put it back with StoreZone() -> true + +=cut my %current_zone = (); +=head3 $current_zone_index + +SelectZone sets this + +StoreZone will append a zone if this is -1 + +=cut my $current_zone_index = -1; my $adapt_firewall = 0; diff --git a/src/modules/DnsServer.pm b/src/modules/DnsServer.pm index f4197dc..a2650e5 100644 --- a/src/modules/DnsServer.pm +++ b/src/modules/DnsServer.pm @@ -571,6 +571,8 @@ sub StoreZone { } BEGIN { $TYPEINFO{FindZone} = ["function", "integer", "string"]; } +# Find zone by name ("zone" key) +# Return -1 if not found sub FindZone { my $self = shift; my $zone_name = shift; @@ -611,6 +613,10 @@ sub RemoveZone { } BEGIN { $TYPEINFO{SelectZone} = ["function", "boolean", "integer"]; } +# SelectZone(index) copies that zone to %current_zone +# SelectZone(-1) is valid, initializes a default %current_zone, +# later StoreZone will append it to the @zones list +# The index is stored to $current_zone_index sub SelectZone { my $self = shift; my $zone_index = shift; @@ -773,7 +779,9 @@ sub GetAllowedInterfaces { return \@allowed_interfaces; } + BEGIN {$TYPEINFO{FetchCurrentZone} = [ "function", ["map", "string", "any"] ]; } +# read %current_zone sub FetchCurrentZone { my $self = shift; @@ -781,6 +789,7 @@ sub FetchCurrentZone { } BEGIN {$TYPEINFO{StoreCurrentZone} = [ "function", "boolean", ["map", "string", "any"] ]; } +# write %current_zone sub StoreCurrentZone { my $self = shift; %current_zone = %{+shift}; diff --git a/src/scrconf/dns_named.scr b/src/scrconf/dns_named.scr index b91ea5c..f6ca7d9 100644 --- a/src/scrconf/dns_named.scr +++ b/src/scrconf/dns_named.scr @@ -22,7 +22,7 @@ "comments" : [ "^[ \t]*#.*$", "^[ \t]*$" ], "params" : [ $[ - "match" : [ "([^ \t]+)[ \t]([^ \t]+(.*[^ \t]+)+)[ \t]*;", "%s %s;" ], + "match" : [ "([^ \t{]+)[ \t]*([^ \t]+(.*[^ \t]+)+)[ \t]*;[ \t]*$", "%s %s;" ], // This enables multiline parameters "multiline" : [ "([^ \t]+)[ \t]+([^}]*)", "([^}]*\\}[ \t]*;)[ \t]*" ], ], diff --git a/test/Makefile.am b/test/Makefile.am index da2b9fe..1fa26ed 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,8 @@ TESTS = \ dns_server_test.rb \ - dns_server_ui_test.rb + dns_server_ui_test.rb \ + etc_named_parsing_test.rb \ + masterzone_test.rb TEST_EXTENSIONS = .rb RB_LOG_COMPILER = rspec diff --git a/test/data/braces-without-space/etc/named.conf b/test/data/braces-without-space/etc/named.conf new file mode 100644 index 0000000..a8d7f26 --- /dev/null +++ b/test/data/braces-without-space/etc/named.conf @@ -0,0 +1,12 @@ +zone "localhost" in { + type master; + file "localhost.zone"; + allow-update {none;}; +}; + +zone "0.0.127.in-addr.arpa" in { + type master; + file "127.0.0.zone"; + # here a space is missing which caused trouble in bsc#976643 (c16) + allow-update{none;}; +}; diff --git a/test/data/braces-without-space/etc/named.conf.json b/test/data/braces-without-space/etc/named.conf.json new file mode 100644 index 0000000..4830b1c --- /dev/null +++ b/test/data/braces-without-space/etc/named.conf.json @@ -0,0 +1,69 @@ +{ + "comment": "", + "file": -1, + "kind": "section", + "name": "", + "type": -1, + "value": [ + { + "comment": "", + "file": -1, + "kind": "section", + "name": "zone \"localhost\" in", + "type": 0, + "value": [ + { + "comment": "", + "kind": "value", + "name": "type", + "type": 0, + "value": "master" + }, + { + "comment": "", + "kind": "value", + "name": "file", + "type": 0, + "value": "\"localhost.zone\"" + }, + { + "comment": "", + "kind": "value", + "name": "allow-update", + "type": 0, + "value": "{none;}" + } + ] + }, + { + "comment": "\n", + "file": -1, + "kind": "section", + "name": "zone \"0.0.127.in-addr.arpa\" in", + "type": 0, + "value": [ + { + "comment": "", + "kind": "value", + "name": "type", + "type": 0, + "value": "master" + }, + { + "comment": "", + "kind": "value", + "name": "file", + "type": 0, + "value": "\"127.0.0.zone\"" + }, + { + "comment": " # here a space is missing which caused trouble in bsc#976643 (c16)\n", + "kind": "value", + "name": "allow-update", + "type": 0, + "value": "{none;}" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/data/stock/etc/named.conf b/test/data/stock/etc/named.conf new file mode 100644 index 0000000..4abc44a --- /dev/null +++ b/test/data/stock/etc/named.conf @@ -0,0 +1,162 @@ +# Copyright (c) 2001-2004 SuSE Linux AG, Nuernberg, Germany. +# All rights reserved. +# +# Author: Frank Bodammer, Lars Mueller +# +# /etc/named.conf +# +# This is a sample configuration file for the name server BIND 9. It works as +# a caching only name server without modification. +# +# A sample configuration for setting up your own domain can be found in +# /usr/share/doc/packages/bind/sample-config. +# +# A description of all available options can be found in +# /usr/share/doc/packages/bind/misc/options. + +options { + + # The directory statement defines the name server's working directory + + directory "/var/lib/named"; + + # enable DNSSEC validation + # + # If BIND logs error messages about the root key being expired, you + # will need to update your keys. See https://www.isc.org/bind-keys + # + # dnssec-enable yes (default), indicates that a secure DNS service + # is being used which may be one, or more, of TSIG + # (for securing zone transfers or DDNS updates), SIG(0) + # (for securing DDNS updates) or DNSSEC. + + #dnssec-enable yes; + + # dnssec-validation yes (default), indicates that a resolver + # (a caching or caching-only name server) will attempt to validate + # replies from DNSSEC enabled (signed) zones. To perform this task + # the server also needs either a valid trusted-keys clause + # (containing one or more trusted-anchors or a managed-keys clause. + + #dnssec-validation auto; + managed-keys-directory "/var/lib/named/dyn/"; + + # Write dump and statistics file to the log subdirectory. The + # pathenames are relative to the chroot jail. + + dump-file "/var/log/named_dump.db"; + statistics-file "/var/log/named.stats"; + + # The forwarders record contains a list of servers to which queries + # should be forwarded. Enable this line and modify the IP address to + # your provider's name server. Up to three servers may be listed. + + #forwarders { 192.0.2.1; 192.0.2.2; }; + + # Enable the next entry to prefer usage of the name server declared in + # the forwarders section. + + #forward first; + + # The listen-on record contains a list of local network interfaces to + # listen on. Optionally the port can be specified. Default is to + # listen on all interfaces found on your system. The default port is + # 53. + + #listen-on port 53 { 127.0.0.1; }; + + # The listen-on-v6 record enables or disables listening on IPv6 + # interfaces. Allowed values are 'any' and 'none' or a list of + # addresses. + + listen-on-v6 { any; }; + + # The next three statements may be needed if a firewall stands between + # the local server and the internet. + + #query-source address * port 53; + #transfer-source * port 53; + #notify-source * port 53; + + # The allow-query record contains a list of networks or IP addresses + # to accept and deny queries from. The default is to allow queries + # from all hosts. + + #allow-query { 127.0.0.1; }; + + # If notify is set to yes (default), notify messages are sent to other + # name servers when the the zone data is changed. Instead of setting + # a global 'notify' statement in the 'options' section, a separate + # 'notify' can be added to each zone definition. + + notify no; + + disable-empty-zone "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA"; +}; + +# To configure named's logging remove the leading '#' characters of the +# following examples. +#logging { +# # Log queries to a file limited to a size of 100 MB. +# channel query_logging { +# file "/var/log/named_querylog" +# versions 3 size 100M; +# print-time yes; // timestamp log entries +# }; +# category queries { +# query_logging; +# }; +# +# # Or log this kind alternatively to syslog. +# channel syslog_queries { +# syslog user; +# severity info; +# }; +# category queries { syslog_queries; }; +# +# # Log general name server errors to syslog. +# channel syslog_errors { +# syslog user; +# severity error; +# }; +# category default { syslog_errors; }; +# +# # Don't log lame server messages. +# category lame-servers { null; }; +#}; + +# The following zone definitions don't need any modification. The first one +# is the definition of the root name servers. The second one defines +# localhost while the third defines the reverse lookup for localhost. + +zone "." in { + type hint; + file "root.hint"; +}; + +zone "localhost" in { + type master; + file "localhost.zone"; +}; + +zone "0.0.127.in-addr.arpa" in { + type master; + file "127.0.0.zone"; +}; + +zone "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" IN { + type master; + file "127.0.0.zone"; +}; + + +# Include the meta include file generated by createNamedConfInclude. This +# includes all files as configured in NAMED_CONF_INCLUDE_FILES from +# /etc/sysconfig/named + +include "/etc/named.conf.include"; + +# You can insert further zone records for your own domains below or create +# single files in /etc/named.d/ and add the file names to +# NAMED_CONF_INCLUDE_FILES. +# See /usr/share/doc/packages/bind/README.SUSE for more details. diff --git a/test/data/stock/etc/named.conf.json b/test/data/stock/etc/named.conf.json new file mode 100644 index 0000000..95ee99c --- /dev/null +++ b/test/data/stock/etc/named.conf.json @@ -0,0 +1,166 @@ +{ + "comment": "", + "file": -1, + "kind": "section", + "name": "", + "type": -1, + "value": [ + { + "comment": "# Copyright (c) 2001-2004 SuSE Linux AG, Nuernberg, Germany.\n# All rights reserved.\n#\n# Author: Frank Bodammer, Lars Mueller \n#\n# /etc/named.conf\n#\n# This is a sample configuration file for the name server BIND 9. It works as\n# a caching only name server without modification.\n#\n# A sample configuration for setting up your own domain can be found in\n# /usr/share/doc/packages/bind/sample-config.\n#\n# A description of all available options can be found in\n# /usr/share/doc/packages/bind/misc/options.\n\n", + "file": -1, + "kind": "section", + "name": "options", + "type": 0, + "value": [ + { + "comment": "\n\t# The directory statement defines the name server's working directory\n\n", + "kind": "value", + "name": "directory", + "type": 0, + "value": "\"/var/lib/named\"" + }, + { + "comment": "\n\t# enable DNSSEC validation\n\t#\n\t# If BIND logs error messages about the root key being expired, you\n\t# will need to update your keys. See https://www.isc.org/bind-keys\n\t#\n\t# dnssec-enable yes (default), indicates that a secure DNS service\n\t# is being used which may be one, or more, of TSIG\n\t# (for securing zone transfers or DDNS updates), SIG(0)\n\t# (for securing DDNS updates) or DNSSEC.\n\n\t#dnssec-enable yes;\n\n\t# dnssec-validation yes (default), indicates that a resolver\n\t# (a caching or caching-only name server) will attempt to validate\n\t# replies from DNSSEC enabled (signed) zones. To perform this task\n\t# the server also needs either a valid trusted-keys clause\n\t# (containing one or more trusted-anchors or a managed-keys clause.\n\n\t#dnssec-validation auto;\n", + "kind": "value", + "name": "managed-keys-directory", + "type": 0, + "value": "\"/var/lib/named/dyn/\"" + }, + { + "comment": "\n\t# Write dump and statistics file to the log subdirectory. The\n\t# pathenames are relative to the chroot jail.\n\n", + "kind": "value", + "name": "dump-file", + "type": 0, + "value": "\"/var/log/named_dump.db\"" + }, + { + "comment": "", + "kind": "value", + "name": "statistics-file", + "type": 0, + "value": "\"/var/log/named.stats\"" + }, + { + "comment": "\n\t# The forwarders record contains a list of servers to which queries\n\t# should be forwarded. Enable this line and modify the IP address to\n\t# your provider's name server. Up to three servers may be listed.\n\n\t#forwarders { 192.0.2.1; 192.0.2.2; };\n\n\t# Enable the next entry to prefer usage of the name server declared in\n\t# the forwarders section.\n\n\t#forward first;\n\n\t# The listen-on record contains a list of local network interfaces to\n\t# listen on. Optionally the port can be specified. Default is to\n\t# listen on all interfaces found on your system. The default port is\n\t# 53.\n\n\t#listen-on port 53 { 127.0.0.1; };\n\n\t# The listen-on-v6 record enables or disables listening on IPv6\n\t# interfaces. Allowed values are 'any' and 'none' or a list of\n\t# addresses.\n\n", + "kind": "value", + "name": "listen-on-v6", + "type": 0, + "value": "{ any; }" + }, + { + "comment": "\n\t# The next three statements may be needed if a firewall stands between\n\t# the local server and the internet.\n\n\t#query-source address * port 53;\n\t#transfer-source * port 53;\n\t#notify-source * port 53;\n\n\t# The allow-query record contains a list of networks or IP addresses\n\t# to accept and deny queries from. The default is to allow queries\n\t# from all hosts.\n\n\t#allow-query { 127.0.0.1; };\n\n\t# If notify is set to yes (default), notify messages are sent to other\n\t# name servers when the the zone data is changed. Instead of setting\n\t# a global 'notify' statement in the 'options' section, a separate\n\t# 'notify' can be added to each zone definition.\n\n", + "kind": "value", + "name": "notify", + "type": 0, + "value": "no" + }, + { + "comment": "\n", + "kind": "value", + "name": "disable-empty-zone", + "type": 0, + "value": "\"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA\"" + } + ] + }, + { + "comment": "\n# To configure named's logging remove the leading '#' characters of the\n# following examples.\n#logging {\n#\t# Log queries to a file limited to a size of 100 MB.\n#\tchannel query_logging {\n#\t\tfile \"/var/log/named_querylog\"\n#\t\t\tversions 3 size 100M;\n#\t\tprint-time yes;\t\t\t// timestamp log entries\n#\t};\n#\tcategory queries {\n#\t\tquery_logging;\n#\t};\n#\n#\t# Or log this kind alternatively to syslog.\n#\tchannel syslog_queries {\n#\t\tsyslog user;\n#\t\tseverity info;\n#\t};\n#\tcategory queries { syslog_queries; };\n#\n#\t# Log general name server errors to syslog.\n#\tchannel syslog_errors {\n#\t\tsyslog user;\n#\t\tseverity error;\n#\t};\n#\tcategory default { syslog_errors; };\n#\n#\t# Don't log lame server messages.\n#\tcategory lame-servers { null; };\n#};\n\n# The following zone definitions don't need any modification. The first one\n# is the definition of the root name servers. The second one defines\n# localhost while the third defines the reverse lookup for localhost.\n\n", + "file": -1, + "kind": "section", + "name": "zone \".\" in", + "type": 0, + "value": [ + { + "comment": "", + "kind": "value", + "name": "type", + "type": 0, + "value": "hint" + }, + { + "comment": "", + "kind": "value", + "name": "file", + "type": 0, + "value": "\"root.hint\"" + } + ] + }, + { + "comment": "\n", + "file": -1, + "kind": "section", + "name": "zone \"localhost\" in", + "type": 0, + "value": [ + { + "comment": "", + "kind": "value", + "name": "type", + "type": 0, + "value": "master" + }, + { + "comment": "", + "kind": "value", + "name": "file", + "type": 0, + "value": "\"localhost.zone\"" + } + ] + }, + { + "comment": "\n", + "file": -1, + "kind": "section", + "name": "zone \"0.0.127.in-addr.arpa\" in", + "type": 0, + "value": [ + { + "comment": "", + "kind": "value", + "name": "type", + "type": 0, + "value": "master" + }, + { + "comment": "", + "kind": "value", + "name": "file", + "type": 0, + "value": "\"127.0.0.zone\"" + } + ] + }, + { + "comment": "\n", + "file": -1, + "kind": "section", + "name": "zone \"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa\" in", + "type": 0, + "value": [ + { + "comment": "", + "kind": "value", + "name": "type", + "type": 0, + "value": "master" + }, + { + "comment": "", + "kind": "value", + "name": "file", + "type": 0, + "value": "\"127.0.0.zone\"" + } + ] + }, + { + "comment": "\n\n# Include the meta include file generated by createNamedConfInclude. This\n# includes all files as configured in NAMED_CONF_INCLUDE_FILES from\n# /etc/sysconfig/named\n\n", + "kind": "value", + "name": "include", + "type": 0, + "value": "\"/etc/named.conf.include\"" + } + ] +} \ No newline at end of file diff --git a/test/data/whitespace/etc/named.conf b/test/data/whitespace/etc/named.conf new file mode 100644 index 0000000..1887356 --- /dev/null +++ b/test/data/whitespace/etc/named.conf @@ -0,0 +1,92 @@ +# https://bugzilla.suse.com/show_bug.cgi?id=976643 +# Yast2 dns-server is not parsing the named.conf file correctly +# if there is white space after an option + +options { + + # The directory statement defines the name server's working directory + + directory "/var/lib/named"; + + # NOTE the following line ends with whitespace + version "unavailable"; + allow-transfer { none; }; + allow-recursion { none; }; + recursion no; + + also-notify { 10.1.2.3; }; + + # enable DNSSEC validation + # + # If BIND logs error messages about the root key being expired, you + # will need to update your keys. See https://www.isc.org/bind-keys + # + # dnssec-enable yes (default), indicates that a secure DNS service + # is being used which may be one, or more, of TSIG + # (for securing zone transfers or DDNS updates), SIG(0) + # (for securing DDNS updates) or DNSSEC. + + #dnssec-enable yes; + + # dnssec-validation yes (default), indicates that a resolver + # (a caching or caching-only name server) will attempt to validate + # replies from DNSSEC enabled (signed) zones. To perform this task + # the server also needs either a valid trusted-keys clause + # (containing one or more trusted-anchors or a managed-keys clause. + + #dnssec-validation auto; + managed-keys-directory "/var/lib/named/dyn/"; + + # Write dump and statistics file to the log subdirectory. The + # pathenames are relative to the chroot jail. + + dump-file "/var/log/named_dump.db"; + statistics-file "/var/log/named.stats"; + + # The forwarders record contains a list of servers to which queries + # should be forwarded. Enable this line and modify the IP address to + # your provider's name server. Up to three servers may be listed. + + #forwarders { 192.0.2.1; 192.0.2.2; }; + + # Enable the next entry to prefer usage of the name server declared in + # the forwarders section. + + #forward first; + + # The listen-on record contains a list of local network interfaces to + # listen on. Optionally the port can be specified. Default is to + # listen on all interfaces found on your system. The default port is + # 53. + + #listen-on port 53 { 127.0.0.1; }; + + # The listen-on-v6 record enables or disables listening on IPv6 + # interfaces. Allowed values are 'any' and 'none' or a list of + # addresses. + + listen-on-v6 { none; }; + + # The next three statements may be needed if a firewall stands between + # the local server and the internet. + + #query-source address * port 53; + #transfer-source * port 53; + #notify-source * port 53; + + # The allow-query record contains a list of networks or IP addresses + # to accept and deny queries from. The default is to allow queries + # from all hosts. + + #allow-query { 127.0.0.1; }; + + # If notify is set to yes (default), notify messages are sent to other + # name servers when the the zone data is changed. Instead of setting + # a global 'notify' statement in the 'options' section, a separate + # 'notify' can be added to each zone definition. + + notify explicit; + + disable-empty-zone "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA"; + include "/etc/named.d/forwarders.conf"; +}; diff --git a/test/data/whitespace/etc/named.conf.json b/test/data/whitespace/etc/named.conf.json new file mode 100644 index 0000000..21ea51f --- /dev/null +++ b/test/data/whitespace/etc/named.conf.json @@ -0,0 +1,109 @@ +{ + "comment": "", + "file": -1, + "kind": "section", + "name": "", + "type": -1, + "value": [ + { + "comment": "# https://bugzilla.suse.com/show_bug.cgi?id=976643\n# Yast2 dns-server is not parsing the named.conf file correctly\n# if there is white space after an option\n\n", + "file": -1, + "kind": "section", + "name": "options", + "type": 0, + "value": [ + { + "comment": "\n\t# The directory statement defines the name server's working directory\n\n", + "kind": "value", + "name": "directory", + "type": 0, + "value": "\"/var/lib/named\"" + }, + { + "comment": " \n # NOTE the following line ends with whitespace\n", + "kind": "value", + "name": "version", + "type": 0, + "value": "\"unavailable\"" + }, + { + "comment": "", + "kind": "value", + "name": "allow-transfer", + "type": 0, + "value": "{ none; }" + }, + { + "comment": "", + "kind": "value", + "name": "allow-recursion", + "type": 0, + "value": "{ none; }" + }, + { + "comment": "", + "kind": "value", + "name": "recursion", + "type": 0, + "value": "no" + }, + { + "comment": "\n", + "kind": "value", + "name": "also-notify", + "type": 0, + "value": "{ 10.1.2.3; }" + }, + { + "comment": "\n\t# enable DNSSEC validation\n\t#\n\t# If BIND logs error messages about the root key being expired, you\n\t# will need to update your keys. See https://www.isc.org/bind-keys\n\t#\n\t# dnssec-enable yes (default), indicates that a secure DNS service\n\t# is being used which may be one, or more, of TSIG\n\t# (for securing zone transfers or DDNS updates), SIG(0)\n\t# (for securing DDNS updates) or DNSSEC.\n\n\t#dnssec-enable yes;\n\n\t# dnssec-validation yes (default), indicates that a resolver\n\t# (a caching or caching-only name server) will attempt to validate\n\t# replies from DNSSEC enabled (signed) zones. To perform this task\n\t# the server also needs either a valid trusted-keys clause\n\t# (containing one or more trusted-anchors or a managed-keys clause.\n\n\t#dnssec-validation auto;\n", + "kind": "value", + "name": "managed-keys-directory", + "type": 0, + "value": "\"/var/lib/named/dyn/\"" + }, + { + "comment": "\n\t# Write dump and statistics file to the log subdirectory. The\n\t# pathenames are relative to the chroot jail.\n\n", + "kind": "value", + "name": "dump-file", + "type": 0, + "value": "\"/var/log/named_dump.db\"" + }, + { + "comment": "", + "kind": "value", + "name": "statistics-file", + "type": 0, + "value": "\"/var/log/named.stats\"" + }, + { + "comment": "\n\t# The forwarders record contains a list of servers to which queries\n\t# should be forwarded. Enable this line and modify the IP address to\n\t# your provider's name server. Up to three servers may be listed.\n\n\t#forwarders { 192.0.2.1; 192.0.2.2; };\n\n\t# Enable the next entry to prefer usage of the name server declared in\n\t# the forwarders section.\n\n\t#forward first;\n\n\t# The listen-on record contains a list of local network interfaces to\n\t# listen on. Optionally the port can be specified. Default is to\n\t# listen on all interfaces found on your system. The default port is\n\t# 53.\n\n\t#listen-on port 53 { 127.0.0.1; };\n\n\t# The listen-on-v6 record enables or disables listening on IPv6\n\t# interfaces. Allowed values are 'any' and 'none' or a list of\n\t# addresses.\n\n", + "kind": "value", + "name": "listen-on-v6", + "type": 0, + "value": "{ none; }" + }, + { + "comment": "\n\t# The next three statements may be needed if a firewall stands between\n\t# the local server and the internet.\n\n\t#query-source address * port 53;\n\t#transfer-source * port 53;\n\t#notify-source * port 53;\n\n\t# The allow-query record contains a list of networks or IP addresses\n\t# to accept and deny queries from. The default is to allow queries\n\t# from all hosts.\n\n\t#allow-query { 127.0.0.1; };\n\n\t# If notify is set to yes (default), notify messages are sent to other\n\t# name servers when the the zone data is changed. Instead of setting\n\t# a global 'notify' statement in the 'options' section, a separate\n\t# 'notify' can be added to each zone definition.\n\n", + "kind": "value", + "name": "notify", + "type": 0, + "value": "explicit" + }, + { + "comment": "\n", + "kind": "value", + "name": "disable-empty-zone", + "type": 0, + "value": "\"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA\"" + }, + { + "comment": "", + "kind": "value", + "name": "include", + "type": 0, + "value": "\"/etc/named.d/forwarders.conf\"" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/etc_named_parsing_test.rb b/test/etc_named_parsing_test.rb new file mode 100755 index 0000000..b1fa89b --- /dev/null +++ b/test/etc_named_parsing_test.rb @@ -0,0 +1,37 @@ +#! /usr/bin/env rspec + +require_relative "test_helper" +require "yast" +require "yast/rspec" +require "json" + +describe ".dns.named agent" do + dirs = Dir.glob(File.join(File.dirname(__FILE__), "data", "*")) + dirs.each do |dir| + basename = File.basename(dir) + it "parses '#{basename}'" do + # 1) Use the YaST agent to parse a text file to a nested hash structure + actual = nil + change_scr_root(dir) do + actual = Yast::SCR.Read(Yast::Path.new(".dns.named.all")) + end + + # 2) Read a JSON file representing the expected structure. + # (Why json? It is Structured and Simple (which YAML is not)) + expected_json = File.read("#{dir}/etc/named.conf.json") + + # 3a) We could compare the hashes, but in case of a mismatch + # RSpec makes it hard to see where the difference is... + + # expected = JSON.parse(expected_json) + # expect(actual).to eq(expected) + + # 3b)... So we compare the textual JSONs instead. The diff is much nicer. + actual_json = JSON.pretty_generate(actual) + if ENV["WRITE_ACTUAL_JSON"] # enable when developing a new test + File.write("#{dir}/etc/named.conf.actual.json", actual_json) + end + expect(actual_json).to eq(expected_json) + end + end +end diff --git a/test/masterzone_test.rb b/test/masterzone_test.rb new file mode 100755 index 0000000..e4d0227 --- /dev/null +++ b/test/masterzone_test.rb @@ -0,0 +1,159 @@ +#! /usr/bin/env rspec + +require_relative "test_helper" +require "yast" +require "yast/rspec" + +describe "DnsServerDialogMasterzoneInclude" do + class CurrentZoneMock + include Yast::I18n + include Yast::UIShortcuts + + attr_accessor :current_zone + def initialize + Yast.include self, "dns-server/dialog-masterzone.rb" + @current_zone = {} + end + end + + let(:empty_zone_options) do + {} + end + + let(:simple_zone_options) do + { + "options" => [ + { "key" => "allow-transfer", "value" => "{ 10.0.0.0; }" } + ] + } + end + + let(:any_zone_options) do + { + "options" => [ + { "key" => "allow-transfer", "value" => "{ any; }" } + ] + } + end + + let(:none_zone_options) do + { + "options" => [ + { "key" => "allow-transfer", "value" => "{ none; }" } + ] + } + end + + describe "#current_zone_allow_transfer" do + it "parses a simple case" do + z = CurrentZoneMock.new + z.current_zone = simple_zone_options + expect(z.current_zone_allow_transfer).to eq ["10.0.0.0"] + end + + it "parses a 'none' case" do + z = CurrentZoneMock.new + z.current_zone = none_zone_options + expect(z.current_zone_allow_transfer).to eq ["none"] + end + + it "parses an 'any' case" do + z = CurrentZoneMock.new + z.current_zone = any_zone_options + expect(z.current_zone_allow_transfer).to eq ["any"] + end + + it "parses an empty case" do + z = CurrentZoneMock.new + z.current_zone = empty_zone_options + expect(z.current_zone_allow_transfer).to eq [] + end + end + + describe "#acl_names" do + it "handles a simple case" do + z = CurrentZoneMock.new + z.current_zone = simple_zone_options + expect(z.acl_names).to eq ["10.0.0.0", "any", "localhost", "localnets"] + end + + it "handles an empty case" do + z = CurrentZoneMock.new + z.current_zone = empty_zone_options + expect(z.acl_names).to eq ["any", "localhost", "localnets"] + end + + it "omits 'none'" do + z = CurrentZoneMock.new + z.current_zone = none_zone_options + expect(z.acl_names).to eq ["any", "localhost", "localnets"] + end + + it "deduplicates 'any'" do + z = CurrentZoneMock.new + z.current_zone = any_zone_options + expect(z.acl_names).to eq ["any", "localhost", "localnets"] + end + + it "uses predeclared ACLs" do + z = CurrentZoneMock.new + expect(Yast::DnsServer).to receive(:GetAcl).and_return [ + "acl1 {10.1.0.1; 10.1.0.2;}", + "acl2 {10.2.0.1; 10.2.0.2;}", + ] + expect(z.acl_names).to eq ["acl1", "acl2", "any", "localhost", "localnets"] + end + end + + describe "#ZoneAclInit" do + it "sets up a simple case" do + z = CurrentZoneMock.new + z.current_zone = simple_zone_options + + expect(Yast::UI).to receive(:ChangeWidget) + .with(Id("enable_zone_transport"), :Value, true) + expect(Yast::UI).to receive(:ChangeWidget) + .with(Id("acls_list"), :Enabled, true) + expect(Yast::UI).to receive(:ChangeWidget) + .with(Id("acls_list"), :SelectedItems, ["10.0.0.0"]) + expect { z.ZoneAclInit }.to_not raise_error + end + + it "sets up a 'none' case" do + z = CurrentZoneMock.new + z.current_zone = none_zone_options + + expect(Yast::UI).to receive(:ChangeWidget) + .with(Id("enable_zone_transport"), :Value, false) + expect(Yast::UI).to receive(:ChangeWidget) + .with(Id("acls_list"), :Enabled, false) + expect { z.ZoneAclInit }.to_not raise_error + end + + it "sets up an 'any' case" do + z = CurrentZoneMock.new + z.current_zone = any_zone_options + + expect(Yast::UI).to receive(:ChangeWidget) + .with(Id("enable_zone_transport"), :Value, true) + expect(Yast::UI).to receive(:ChangeWidget) + .with(Id("acls_list"), :Enabled, true) + expect(Yast::UI).to receive(:ChangeWidget) + .with(Id("acls_list"), :SelectedItems, ["any"]) + expect { z.ZoneAclInit }.to_not raise_error + end + + it "sets up an empty case" do + z = CurrentZoneMock.new + z.current_zone = empty_zone_options + + expect(Yast::UI).to receive(:ChangeWidget) + .with(Id("enable_zone_transport"), :Value, true) + expect(Yast::UI).to receive(:ChangeWidget) + .with(Id("acls_list"), :Enabled, true) + expect(Yast::UI).to receive(:ChangeWidget) + .with(Id("acls_list"), :SelectedItems, ["any"]) + expect { z.ZoneAclInit }.to_not raise_error + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 884ec84..05c2211 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -24,7 +24,9 @@ if ENV["COVERAGE"] require "simplecov" - SimpleCov.start + SimpleCov.start do + add_filter "/test/" + end # for coverage we need to load all ruby files Dir["#{SRC_PATH}/{lib,modules}/**/*.rb"].each { |f| require_relative f }