]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Document views.
authorMiod Vallat <miod.vallat@powerdns.com>
Mon, 5 May 2025 11:57:55 +0000 (13:57 +0200)
committerMiod Vallat <miod.vallat@powerdns.com>
Mon, 26 May 2025 11:49:12 +0000 (13:49 +0200)
22 files changed:
.github/actions/spell-check/expect.txt
docs/backends/bind.rst
docs/backends/generic-mysql.rst
docs/backends/generic-odbc.rst
docs/backends/generic-postgresql.rst
docs/backends/generic-sqlite3.rst
docs/backends/geoip.rst
docs/backends/ldap.rst
docs/backends/lmdb.rst
docs/backends/lua2.rst
docs/backends/pipe.rst
docs/backends/random.rst
docs/backends/remote.rst
docs/backends/tinydns.rst
docs/http-api/index.rst
docs/http-api/networks.rst [new file with mode: 0644]
docs/http-api/swagger/authoritative-api-swagger.yaml
docs/http-api/views.rst [new file with mode: 0644]
docs/indexTOC.rst
docs/manpages/pdnsutil.1.rst
docs/settings.rst
docs/views.rst [new file with mode: 0644]

index 763f8527993108a09cd258921357133070101480..e40cc7ba621c7d9d83cd3412b501632ae0ec2f23 100644 (file)
@@ -86,6 +86,7 @@ backported
 backticks
 BADALG
 BADCOOKIE
+badguys
 badips
 BADKEY
 BADMODE
@@ -976,6 +977,7 @@ orsn
 Oservers
 otherdomain
 otherpool
+othervariant
 ourname
 ourserial
 outpacket
@@ -1183,6 +1185,7 @@ Roel
 rolltype
 Rosmalen
 roundrobin
+routable
 rping
 rpms
 rpz
@@ -1498,6 +1501,7 @@ Valkenburg
 Vandalon
 vandergaast
 Vandoren
+variantless
 Vasiliy
 Veldhuyzen
 Verheijen
@@ -1613,4 +1617,5 @@ zskroll
 ztc
 Zumstrull
 Zwane
+zytrax
 zzyzz
index da56f4027578206cc6f5ee2f7c87dd696fd10bd3..6f52f88313525c479af0e2c43b5db4db9901f87d 100644 (file)
@@ -12,6 +12,7 @@ BIND zone file backend
 * Disabled data: No
 * Comments: No
 * Search: Yes
+* Views: No
 * API: Read-only
 * Multiple instances: No
 * Zone caching: Yes
index 1ced43ef65fcfa92b02706cc89719530446eefb0..538f8986a655639d996edb46ffbe816d77a98594 100644 (file)
@@ -12,6 +12,7 @@ Generic MySQL/MariaDB  backend
 * Disabled data: Yes
 * Comments: Yes
 * Search: Yes
+* Views: No
 * API: Read-Write
 * Multiple instances: yes
 * Zone caching: Yes
index 9ce42d993d1e6c03ab5615cb999a503e29bb9a0c..29aabbccd1de451e8dc56c1b684014eb6c4ffdea 100644 (file)
@@ -12,6 +12,7 @@ Generic ODBC Backend
 * Disabled data: Yes
 * Comments: Yes
 * Search: Yes
+* Views: No
 * API: Read-Write
 * Multiple instances: yes
 * Zone caching: Yes
index 1ebbbb7d750cf5a050b8cad9a6539125b469062b..76a6f84a5a30e90ad38c80d9e388d23bb8761a33 100644 (file)
@@ -12,6 +12,7 @@ Generic PostgreSQL backend
 * Disabled data: Yes
 * Comments: Yes
 * Search: Yes
+* Views: No
 * API: Read-Write
 * Multiple instances: yes
 * Zone caching: Yes
index d0b9cd8e92e8a834967d1f78febd46f8aafbd664..635febf92209e9fc6686bbdb1ff5f9547a4573e4 100644 (file)
@@ -12,6 +12,7 @@ Generic SQLite 3 backend
 * Disabled data: Yes
 * Comments: Yes
 * Search: Yes
+* Views: No
 * API: Read-Write
 * Multiple instances: yes
 * Zone caching: Yes
index 3a385e5a3bc382dfc22d472567098a2e5d854458..5e1ea9b17610cbda99edd0967f10fc42e6bf6ae8 100644 (file)
@@ -12,6 +12,7 @@ GeoIP backend
 * Disabled data: No
 * Comments: No
 * Search: No
+* Views: No
 * API: Read-only
 * Multiple instances: Yes
 * Zone caching: Yes
index 2c2cfc0d89e7a6b28560107d954691bc234e3723..1334235ee0ff873107c6c665cff4431e2bea8ec4 100644 (file)
@@ -12,6 +12,7 @@ LDAP backend
 * Disabled data: No
 * Comments: No
 * Search: No
+* Views: No
 * API: Read-only
 * Multiple instances: Yes
 * Zone caching: No
index 5a916412894a6f62464696ac562e9b02e49d96d3..5f9106aedb516f71e7f10419a87e130f389544e2 100644 (file)
@@ -12,6 +12,7 @@ LMDB backend
 * Disabled data: Yes
 * Comments: No
 * Search: No
+* Views: Yes
 * API: Read-Write
 * Multiple instances: No
 * Zone caching: Yes
index 4b303be08830536ca10fcef5d9179046010b1675..a0b7c5e9d11a2ee6fca78b13f92b4485209c6967 100644 (file)
@@ -12,6 +12,7 @@ Lua2 Backend
 * Disabled data: No
 * Comments: No
 * Search: No
+* Views: No
 * API: Read-Write
 * Multiple instances: Yes
 * Zone caching: Yes\*
index 1810721af0699b166c69d820234516fec527f0e2..b08f42c4ef3e73af8afcad9e95581b6297b0f5ed 100644 (file)
@@ -12,6 +12,7 @@ Pipe Backend
 * Disabled data: No
 * Comments: No
 * Search: No
+* Views: No
 * API: Read-only
 * Multiple instances: Yes
 * Zone caching: No
index 54ac8049c783afe99aec4800ad07fd876fea4f46..8a1961db74830a25d42ca47ea0e328c94eeac068 100644 (file)
@@ -15,6 +15,7 @@ Random Backend
 * Disabled data: No
 * Comments: No
 * Search: No
+* Views: No
 * API: No
 * Multiple instances: No
 * Zone caching: No
index 20c54c0f9a68d34951ef7807ebf24fac3d5b84c7..7648004452584baa024a9ce6baf8b841b832e4c7 100644 (file)
@@ -12,6 +12,7 @@ Remote Backend
 * Disabled data: No
 * Comments: No
 * Search: Yes\*
+* Views: No
 * API: Read-Write
 * Multiple instances: Yes
 * Zone caching: Yes\*
index 9e32c0be7296a9f5789221a72e31b2499eceabb1..b000f13538610fb24ece5efbf75631850e5bd815 100644 (file)
@@ -12,6 +12,7 @@ TinyDNS Backend
 * Disabled data: No
 * Comments: No
 * Search: No
+* Views: No
 * API: Read-only
 * Multiple Instances: Yes
 * Zone caching: Yes
index 2051947955cc9645a8dce3781b0a29ee49539159..de479f1abcdba352b891e9d6d6fc94a2b5aacbce 100644 (file)
@@ -363,6 +363,8 @@ The API exposes several endpoints and objects:
 
   server
   zone
+  views
+  networks
   cryptokey
   metadata
   tsigkey
diff --git a/docs/http-api/networks.rst b/docs/http-api/networks.rst
new file mode 100644 (file)
index 0000000..a457d55
--- /dev/null
@@ -0,0 +1,66 @@
+Networks
+========
+
+These endpoints allow configuration of networks, used by :doc:`../views`.
+
+Networks Endpoints
+------------------
+
+.. openapi:: swagger/authoritative-api-swagger.yaml
+  :paths: /servers/{server_id}/networks /servers/{server_id}/networks/{ip}/{prefixlen}
+
+Examples
+--------
+
+Listing all networks
+^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: http
+
+  GET /api/v1/servers/localhost/networks HTTP/1.1
+  X-API-Key: secret
+
+Will yield a response similar to this (several headers omitted):
+
+.. code-block:: http
+  
+  HTTP/1.1 200 OK
+  Content-Type: application/json
+
+  {"networks": [{"network":"192.168.0.0/16","view":"trusted"},{"network":"0.0.0.0/0","view":"untrusted"}]}
+
+Listing the view of a given network
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: http
+
+  GET /api/v1/servers/localhost/networks/192.168.0.0/16 HTTP/1.1
+  X-API-Key: secret
+
+Will yield a response similar to this (several headers omitted):
+
+.. code-block:: http
+  
+  HTTP/1.1 200 OK
+  Content-Type: application/json
+
+  {"networks": [{"network":"192.168.0.0/16","view":"trusted"}]}
+
+Setting up a network
+^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: http
+
+  PUT /api/v1/servers/localhost/networks/192.168.0.0/16 HTTP/1.1
+  X-API-Key: secret
+  Content-Type: application/json
+
+  {"view": "trusted"}
+
+Will yield a response similar to this (several headers omitted):
+
+.. code-block:: http
+  
+  HTTP/1.1 204 No Content
+
+
index b820599f7baeae4e7f6c7bbb1465d95ba90ddb99..0be561ce52606211da249a6b7c5761b015b5bf9d 100644 (file)
@@ -1,6 +1,6 @@
 swagger: '2.0'
 info:
-  version: "0.0.15"
+  version: "0.0.16"
   title: PowerDNS Authoritative HTTP API
   license:
     name: MIT
@@ -939,6 +939,182 @@ paths:
           description: 'OK, key was deleted'
         <<: *commonErrors
 
+  '/servers/{server_id}/views':
+    get:
+      summary: List all views in a server
+      operationId: listViews
+      tags:
+        - views
+      parameters:
+        - name: server_id
+          in: path
+          required: true
+          description: The id of the server to retrieve
+          type: string
+      responses:
+        '200':
+          description: An array of view names
+          schema:
+            $ref: '#/definitions/Views'
+        <<: *commonErrors
+
+  '/servers/{server_id}/views/{view}':
+    get:
+      summary: List the contents of a given view
+      operationId: listView
+      tags:
+        - views
+      parameters:
+        - name: server_id
+          in: path
+          required: true
+          description: The id of the server to retrieve
+          type: string
+        - name: view
+          type: string
+          in: path
+          required: true
+          description: The name of the view to retrieve
+      responses:
+        '200':
+          description: An array of zone names
+          schema:
+            $ref: '#/definitions/View'
+        <<: *commonErrors
+    post:
+      summary: Adds a zone to a given view, creating it if needed
+      operationId: addToView
+      tags:
+        - views
+      parameters:
+        - name: server_id
+          in: path
+          required: true
+          description: The id of the server to retrieve
+          type: string
+        - name: view
+          type: string
+          in: path
+          required: true
+          description: The name of the view to update
+        - name: name
+          description: The zone to add to the view
+          required: true
+          in: body
+          schema:
+            type: string
+      responses:
+        '204':
+          description: 'Returns 204 No Content on success.'
+        <<: *commonErrors
+
+  '/servers/{server_id}/views/{view}/{id}':
+    delete:
+      summary: Removes the given zone from the given view
+      operationId: deleteFromView
+      tags:
+        - views
+      parameters:
+        - name: server_id
+          in: path
+          required: true
+          description: The id of the server to retrieve
+          type: string
+        - name: view
+          type: string
+          in: path
+          required: true
+          description: The name of the view to update
+        - name: id
+          description: The zone to remove from the view
+          required: true
+          in: path
+          type: string
+      responses:
+        '204':
+          description: 'Returns 204 No Content on success.'
+        <<: *commonErrors
+
+  '/servers/{server_id}/networks':
+    get:
+      summary: List all registered networks and views in a server
+      operationId: listNetworks
+      tags:
+        - networks
+      parameters:
+        - name: server_id
+          in: path
+          required: true
+          description: The id of the server to retrieve
+          type: string
+      responses:
+        '200':
+          description: An array of networks
+          schema:
+            $ref: '#/definitions/Networks'
+        <<: *commonErrors
+
+  '/servers/{server_id}/networks/{ip}/{prefixlen}':
+    get:
+      summary: Return the view associated to the given network
+      operationId: getNetwork
+      tags:
+        - networks
+      parameters:
+        - name: server_id
+          in: path
+          required: true
+          description: The id of the server to retrieve
+          type: string
+        - name: ip
+          type: string
+          in: path
+          required: true
+          description: The base address of the network
+        - name: prefixlen
+          type: string
+          in: path
+          required: true
+          description: The length of the network prefix
+      responses:
+        '200':
+          description: An array of networks
+          schema:
+            $ref: '#/definitions/Networks'
+        <<: *commonErrors
+    put:
+      summary: Sets the view associated to the given network
+      operationId: setNetwork
+      tags:
+        - networks
+      parameters:
+        - name: server_id
+          in: path
+          required: true
+          description: The id of the server to retrieve
+          type: string
+        - name: ip
+          type: string
+          in: path
+          required: true
+          description: The base address of the network
+        - name: prefixlen
+          type: string
+          in: path
+          required: true
+          description: The length of the network prefix
+        - name: view
+          type: string
+          required: true
+          description: The name of the view to use for to this network
+          in: body
+          schema:
+            type: string
+      responses:
+        '204':
+          description: 'Returns 204 No Content on success.'
+        <<: *commonErrors
+
 definitions:
   Server:
     title: Server
@@ -1396,3 +1572,40 @@ definitions:
       result:
         type: string
         description: 'A message about the result like "Flushed cache"'
+
+  View:
+    title: View
+    properties:
+      zones:
+        type: array
+        items:
+          type: string
+        description: 'An array of zone names'
+
+  Views:
+    title: Views
+    properties:
+      views:
+        type: array
+        items:
+          type: string
+        description: 'An array of view names'
+
+  Network:
+    title: Network
+    properties:
+      network:
+        type: string
+        description: 'Network specification in human-readable form base address/prefix length'
+      view:
+        type: string
+        description: 'The name of the view'
+
+  Networks:
+    title: Networks
+    properties:
+      networks:
+        type: array
+        items:
+          $ref: '#/definitions/Network'
+          
diff --git a/docs/http-api/views.rst b/docs/http-api/views.rst
new file mode 100644 (file)
index 0000000..2678921
--- /dev/null
@@ -0,0 +1,79 @@
+Views
+=====
+
+These endpoints allow configuration of per-zone :doc:`../views`.
+
+Views Endpoints
+---------------
+
+.. openapi:: swagger/authoritative-api-swagger.yaml
+  :paths: /servers/{server_id}/views /servers/{server_id}/views/{view} /servers/{server_id}/views/{view}/{id}
+
+Examples
+--------
+
+Listing all views
+^^^^^^^^^^^^^^^^^^
+
+.. code-block:: http
+
+  GET /api/v1/servers/localhost/views HTTP/1.1
+  X-API-Key: secret
+
+Will yield a response similar to this (several headers omitted):
+
+.. code-block:: http
+  
+  HTTP/1.1 200 OK
+  Content-Type: application/json
+
+  {"views":["trusted","untrusted"]}
+
+Listing the zones of a view
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: http
+
+  GET /api/v1/servers/localhost/views/trusted HTTP/1.1
+  X-API-Key: secret
+
+Will yield a response similar to this (several headers omitted):
+
+.. code-block:: http
+  
+  HTTP/1.1 200 OK
+  Content-Type: application/json
+
+  {"zones":["example.com..trusted","otherdomain.com..untrusted"]}
+
+Creating or adding to a view
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: http
+
+  POST /api/v1/servers/localhost/views/trusted HTTP/1.1
+  X-API-Key: secret
+  Content-Type: application/json
+
+  {"name":"example.org..trusted"}
+
+Will yield a response similar to this (several headers omitted):
+
+.. code-block:: http
+  
+  HTTP/1.1 204 No Content
+
+Deleting a view
+^^^^^^^^^^^^^^^
+
+.. code-block:: http
+
+  DELETE /api/v1/servers/localhost/views/trusted HTTP/1.1
+  X-API-Key: secret
+
+Will yield a response similar to this (several headers omitted):
+
+.. code-block:: http
+  
+  HTTP/1.1 204 No Content
+
index 6301756ff0e9159ab4e39b2d995f738609321fc9..797ba54b76001521bcc04cf0e9882e26bc5d125e 100644 (file)
@@ -18,6 +18,7 @@ PowerDNS Authoritative Server
     dnsupdate
     catalog
     tsig
+    views
     lua-records/index
     guides/index
     backends/index
index a6ccf7060c3ed411997ad51c05486aa286010c32..83669ca1de384a26c71d5e6070c5747d2e7adeb7 100644 (file)
@@ -31,7 +31,7 @@ Commands
 There are many available commands, this section splits them up into
 their respective uses.
 
-DNSSEC-related Commands
+DNSSEC-RELATED COMMANDS
 -----------------------
 
 Several commands manipulate the DNSSEC keys and options for zones. Some
@@ -433,6 +433,33 @@ zonemd-verify-file *ZONE* *FILE*
 
     Validate ZONEMD for *ZONE* read from *FILE*.
 
+VIEWS COMMANDS
+--------------
+
+list-networks
+
+    List all defined networks with their chosen views.
+
+set-network *NET* [*VIEW*]
+
+    Set the *VIEW* for a the *NET* network, or delete if no *VIEW* argument.
+
+view-add-zone *VIEW* *ZONE..VARIANT*
+
+    Add the given *ZONE* *VARIANT* to a *VIEW*.
+
+view-del-zone *VIEW* *ZONE..VARIANT*
+
+    Remove a *ZONE* *VARIANT* from a *VIEW*.
+
+list-view *VIEW*
+
+    List all within *VIEW*.
+
+list-views
+
+    List all view names.
+
 DEBUGGING TOOLS
 ---------------
 
index ba2af94da1ec765ed0709b32f69a7b0041cf9256..5e998db10a993e5f779d57ffd8720c5193254ca1 100644 (file)
@@ -2018,6 +2018,18 @@ behaviour, to ``powerdns`` to just make it state
 setting will return a ServFail, much like Microsoft nameservers do. You
 can set this response to a custom value as well.
 
+.. _setting-views:
+
+``views``
+---------
+
+-  Boolean
+-  Default: no
+
+.. versionadded:: 5.0.0
+
+Enable :doc:`views`.
+
 .. _setting-webserver:
 
 ``webserver``
@@ -2220,6 +2232,8 @@ Seconds to cache a list of all known zones. A value of 0 will disable the cache.
 
 If your backends do not respond to unknown or dynamically generated zones, it is suggested to enable :ref:`setting-consistent-backends` (default since 4.5) and leave this option at its default of `300`.
 
+If :ref:`setting-views` are enabled, the zone cache **must** be enabled.
+
 .. _setting-zone-metadata-cache-ttl:
 
 ``zone-metadata-cache-ttl``
diff --git a/docs/views.rst b/docs/views.rst
new file mode 100644 (file)
index 0000000..69b18e7
--- /dev/null
@@ -0,0 +1,223 @@
+Views
+=====
+
+.. versionadded:: 5.0.0
+
+Views are an experimental feature, which allows the scope of zones to be
+narrowed, depending on the originating address of the query, by exposing
+different `variants` of zones.
+
+A simple use case for this feature is to separate internal (trusted) and
+external (untrusted) views of a given domain, without having to rely upon a
+GeoIP-like backend.
+
+Requirements
+------------
+
+The `Views` features is currently only available in the :doc:`LMDB
+<backends/lmdb>` backend, and requires the zone cache to be enabled (by setting
+:ref:`setting-zone-cache-refresh-interval` to a non-zero value).
+
+It must also be explicitly enabled using :ref:`setting-views` in the
+configuration file.
+
+Concepts
+--------
+
+The first piece of the Views puzzle is the `network`. A `network`, specified as
+a base address and a prefix length, is associated to a `view name`. The `view
+name` in turn, will select a set of `zone variants` to be used to answer queries
+for these zones, originating from this network.
+
+Queries originating from no configured network will be answered as in a
+non-views setup, without any restriction.
+
+Zone Variants
+^^^^^^^^^^^^^
+
+A Zone Variant is a zone on its own, written as ``<zone name with trailing dot>.<variant name>``.
+Variant names are made of lowercase letters, digits, underscore and dashes only.
+
+For example, the following variants are valid:
+
+- ``example.org..variant01``
+- ``example.org..1st_variant``
+- ``example.org..othervariant``
+
+and a variant of the root zone would be:
+
+- ``..variant``
+
+Zone variants can be used in any command or operation where a zone name is
+expected, i.e. with :doc:`pdnsutil <manpages/pdnsutil.1>` or the
+:doc:`HTTP API <http-api/index>`.
+
+There is no mechanism to populate a freshly-created variant from the variantless
+zone contents.
+
+Networks
+^^^^^^^^
+
+Networks are set up either with :doc:`pdnsutil <manpages/pdnsutil.1>` or the
+:doc:`HTTP API <http-api/index>`.
+
+Every network is associated to a unique view name.
+
+Note that in PowerDNS, unlike Bind, the order in which networks are configured
+does not matter. When deciding which network to use to answer a DNS query, the
+narrowest (smallest) network will always be chosen.
+
+Views
+^^^^^
+
+Views are set up either with :doc:`pdnsutil <manpages/pdnsutil.1>` or the
+:doc:`HTTP API <http-api/index>`.
+
+Every view is associated to a list of zone variants. It can also include
+regular (variantless) zones, but this is not needed as all zones which do not
+appear in a view will operate as in a non-views setup.
+
+In other words, zones not part of a view are always implicitly available in
+that view, as their variantless contents.
+
+Only one variant per zone may appear in a view; setting a new zone variant will
+replace the previous one in the view.
+
+Resolution Algorithm
+--------------------
+
+When views are enabled, the following operations take place when processing
+a DNS query:
+
+- the source address of the request (or the EDNS subnet option if present) is
+  used to check whether it matches a configured *network*.
+- if so, the *view* associated to that *network* is retrieved; otherwise,
+  views will be bypassed.
+- when searching for a given zone, if there is a specific *variant* for that
+  zone in the *view*, then that zone variant will be used; otherwise,
+  the regular variantless zone will be used.
+
+Configuration tweaks
+--------------------
+
+When views are used, the :ref:`packet-cache` will cache result results for each
+view independently. If your configuration benefits from the packet cache,
+you might need to multiply its capacity
+(:ref:`setting-max-packet-cache-entries`) by the number of views in use.
+
+Examples
+--------
+
+Simple setup
+^^^^^^^^^^^^
+
+In such a setup, we want to provide three different flavours of a given zone:
+one for internal (non-routable) queries, one for trusted origins, and one for
+the rest of the Internet.
+
+Let's start by defining the specific networks::
+
+  pdnsutil set-network 10.0.0.0/8 internal
+  pdnsutil set-network 172.16.0.0/12 internal
+  pdnsutil set-network 192.168.0.0/16 internal
+  pdnsutil set-network fc00::/7 internal
+
+  pdnsutil set-network 198.51.100.0/24 trusted
+  pdnsutil set-network 203.0.113.0/24 trusted
+  pdnsutil set-network 2001:db8::/32 trusted
+
+Once these commands have been run, queries originating from these particular
+networks will select either the "internal" or "trusted" view, while queries
+originating from other addresses will default to the unbiased view, which you
+may consider an always-existing default (nameless) view.
+
+You can check the result of these commands with::
+
+  $ pdnsutil list-networks
+  10.0.0.0/8      internal
+  172.16.0.0/12   internal
+  192.168.0.0/16  internal
+  198.51.100.0/24 trusted
+  203.0.113.0/24  trusted
+  2001:db8::/32   trusted
+  fc00::/7        internal
+
+Since these views have not been set up yet, they are empty, causing no change of
+outcome when resolving domain queries.
+
+Let's differentiate these views now::
+
+  pdnsutil view-add-zone internal example.com..internal
+  pdnsutil view-add-zone internal example2.com..secret
+
+  pdnsutil view-add-zone trusted example.com..trusted
+
+Note that the `view-add-zone` command does not create any zone! You will need
+to create these zones, like you would do for any other "regular" zone::
+
+  pdnsutil create-zone example.com..internal
+  pdnsutil create-zone example2.com..secret
+  pdnsutil create-zone example.com..trusted
+
+and then use `load-zone`, `edit-zone`, or `add-record` to add contents to these
+zones.
+
+With these settings in place, queries for the `example.com.` zone will be
+performed on the `example.com..internal` zone when originating from the internal
+networks, on the `example.com..trusted` zone when originating from the trusted
+network, and on the variantless, unmodified, `example.com.` zone when
+originating from elsewhere; and queries for the `example2.com.` zone will be
+performed on the `example2.com..secret` zone when originating from the internal
+networks, and on the variantless `example2.com.` otherwise.
+
+Queries for all other zones will be unaffected, since no other zone is
+configured in the views.
+
+As seen in this example, a given view may cause multiple zones to be resolved
+differently. At any time, you can check which views are setup, and the details
+of a given view::
+
+  $ pdnsutil list-views
+  internal
+  trusted
+  $ pdnsutil list-view internal
+  example.com..internal
+  example2.com..secret
+  $ pdnsutil list-view trusted
+  example.com..trusted
+
+Bind configuration adaptation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Consider the following Bind configuration, shamelessly borrowed from
+https://www.zytrax.com/books/dns/ch7/view.html::
+
+  view "trusted" {
+   match-clients { 192.168.23.0/24; }; // our network
+    zone "example.com" {
+     type master;
+     // private zone file including local hosts
+     file "internal/master.example.com";
+    };
+    // add required zones
+   };
+  view "badguys" {
+   match-clients {"any"; }; // all other hosts
+   zone "example.com" {
+     type master;
+     // public only hosts
+     file "external/master.example.com";
+    };
+    // add required zones
+   };
+
+The equivalent PowerDNS setup would be::
+
+  pdnsutil set-network 192.168.23.0/24 trusted
+  pdnsutil set-network 0.0.0.0/0 badguys
+
+  pdnsutil view-add-zone trusted master.example.com..internal
+  pdnsutil view-add-zone badguys master.example.com..external
+
+  pdnsutil load-zone example.com..internal internal/master.example.com
+  pdnsutil load-zone example.com..external external/master.example.com