]> granicus.if.org Git - pdns/commitdiff
auth: lua records: document DNSName, DNSHeader, DNSResourceRecord API
authorCharles-Henri Bruyand <charles-henri.bruyand@open-xchange.com>
Thu, 21 Jun 2018 12:16:53 +0000 (14:16 +0200)
committerCharles-Henri Bruyand <charles-henri.bruyand@open-xchange.com>
Wed, 31 Oct 2018 14:16:19 +0000 (15:16 +0100)
13 files changed:
docs/dnsupdate.rst [deleted file]
docs/indexTOC.rst
docs/lua-records/functions.rst [moved from docs/lua-records.rst with 59% similarity]
docs/lua-records/index.rst [new file with mode: 0644]
docs/lua-records/reference/comboaddress.rst [new file with mode: 0644]
docs/lua-records/reference/dnsheader.rst [new file with mode: 0644]
docs/lua-records/reference/dnsname.rst [new file with mode: 0644]
docs/lua-records/reference/dnsresourcerecord.rst [new file with mode: 0644]
docs/lua-records/reference/index.rst [new file with mode: 0644]
docs/lua-records/reference/netmask.rst [new file with mode: 0644]
docs/lua-records/reference/qtype.rst [new file with mode: 0644]
pdns/lua-base4.cc
pdns/lua-record.cc

diff --git a/docs/dnsupdate.rst b/docs/dnsupdate.rst
deleted file mode 100644 (file)
index 3d212ac..0000000
+++ /dev/null
@@ -1,519 +0,0 @@
-Dynamic DNS Update (RFC2136)
-============================
-
-Starting with the PowerDNS Authoritative Server 3.4.0, DNS update
-support is available. There are a number of items NOT supported:
-
--  There is no support for GSS\*TSIG and SIG (TSIG is supported);
--  WKS records are specifically mentioned in the RFC, we don't
-   specifically care about WKS records;
--  Anything we forgot....
-
-The implementation requires the backend to support a number of new
-operations. Currently, the following backends have been modified to
-support DNS update:
-
-- :doc:`gmysql <backends/generic-mysql>`
-- :doc:`gpgsql <backends/generic-postgresql>`
-- :doc:`gsqlite3 <backends/generic-sqlite3>`
-- :doc:`goracle <backends/generic-oracle>`
-- :doc:`godbc <backends/generic-odbc>`
-
-.. _dnsupdate-configuration-options:
-
-Configuration options
----------------------
-
-There are two configuration parameters that can be used within the
-powerdns configuration file.
-
-``dnsupdate``
-~~~~~~~~~~~~~
-
-A setting to enable/disable DNS update support completely. The default
-is no, which means that DNS updates are ignored by PowerDNS (no message
-is logged about this!). Change the setting to ``dnsupdate=yes`` to
-enable DNS update support. Default is ``no``.
-
-``allow-dnsupdate-from``
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-A list of IP ranges that are allowed to perform updates on any domain.
-The default is ``127.0.0.0/8``, which means that all loopback addresses are accepted.
-Multiple entries can be used on this line
-(``allow-dnsupdate-from=198.51.100.0/8 203.0.113.2/32``). The option can
-be left empty to disallow everything, this then should be used in
-combination with the ``ALLOW-DNSUPDATE-FROM`` :doc:`domain metadata <domainmetadata>` setting per
-zone. Setting a range here and in ``ALLOW-DNSUPDATE-FROM`` enables updates
-from either address range.
-
-``forward-dnsupdate``
-~~~~~~~~~~~~~~~~~~~~~
-
-Tell PowerDNS to forward to the master server if the zone is configured
-as slave. Masters are determined by the masters field in the domains
-table. The default behaviour is enabled (yes), which means that it will
-try to forward. In the processing of the update packet, the
-``allow-dnsupdate-from`` and ``TSIG-ALLOW-DNSUPDATE`` are processed
-first, so those permissions apply before the ``forward-dnsupdate`` is
-used. It will try all masters that you have configured until one is
-successful.
-
-.. _dnsupdate-lua-dnsupdate-policy-script:
-
-``lua-dnsupdate-policy-script``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Use this Lua script containing function ``updatepolicy`` to validate
-each update. This will ``TURN OFF`` all other
-authorization methods, and you are expected to take care of everything
-yourself. See :ref:`dnsupdate-update-policy` for details and
-examples.
-
-The semantics are that first a dynamic update has to be allowed either
-by the global :ref:`setting-allow-dnsupdate-from` setting, or by a per-zone
-``ALLOW-DNSUPDATE-FROM`` metadata setting.
-
-Secondly, if a zone has a ``TSIG-ALLOW-DNSUPDATE`` metadata setting, that
-must match too.
-
-So to only allow dynamic DNS updates to a zone based on TSIG key, and
-regardless of IP address, set :ref:`setting-allow-dnsupdate-from` to empty, set
-``ALLOW-DNSUPDATE-FROM`` to "0.0.0.0/0" and "::/0" and set the
-``TSIG-ALLOW-DNSUPDATE`` to the proper key name.
-
-Further information can be found :ref:`below <dnsupdate-how-it-works>`.
-
-.. _dnsupdate-metadata:
-
-Per zone settings
------------------
-
-For permissions, a number of per zone settings are available via the
-:doc:`domain metadata `<domainmetadata>`.
-
-.. _metadata-allow-dnsupdate-from:
-
-ALLOW-DNSUPDATE-FROM
-~~~~~~~~~~~~~~~~~~~~
-
-This setting has the same function as described in the configuration
-options (See ref:`above <dnsupdate-configuration-options>`). Only one item is
-allowed per row, but multiple rows can be added. An example:
-
-::
-
-    sql> select id from domains where name='example.org';
-    5
-    sql> insert into domainmetadata(domain_id, kind, content) values(5, ‘ALLOW-DNSUPDATE-FROM’,’198.51.100.0/8’);
-    sql> insert into domainmetadata(domain_id, kind, content) values(5, ‘ALLOW-DNSUPDATE-FROM’,’203.0.113.2/32’);
-
-This will allow 198.51.100.0/8 and 203.0.113.2/32 to send DNS update
-messages for the example.org domain.
-
-.. _metadata-tsig-allow-dnsupdate:
-
-TSIG-ALLOW-DNSUPDATE
-~~~~~~~~~~~~~~~~~~~~
-
-This setting allows you to set the TSIG key required to do an DNS
-update. If you have GSS-TSIG enabled, you can use Kerberos principals
-here. An example, using :program:`pdnsutil` to create the key:
-
-::
-
-    pdnsutil generate-tsig-key test hmac-md5
-    Create new TSIG key test hmac-md5 kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys=
-    
-    sql> insert into tsigkeys (name, algorithm, secret) values ('test', 'hmac-md5', 'kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys=');
-    sql> select id from domains where name='example.org';
-    5
-    sql> insert into domainmetadata (domain_id, kind, content) values (5, 'TSIG-ALLOW-DNSUPDATE', 'test');
-
-An example of how to use a TSIG key with the :program:`nsupdate` command:
-
-::
-
-    nsupdate <<!
-    server <ip> <port>
-    zone example.org
-    update add test1.example.org 3600 A 203.0.113.1
-    key test kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys=
-    send
-    !
-
-If a TSIG key is set for the domain, it is required to be used for the
-update. The TSIG is an alternative means of securing updates, instead of using the
-``ALLOW-DNSUPDATE-FROM`` setting. If a TSIG key is set, and if ``ALLOW-DNSUPDATE-FROM`` is set,
-the IP(-range) of the updater still needs to be allowed via ``ALLOW-DNSUPDATE-FROM``. 
-
-.. _metadata-forward-dnsupdate:
-
-FORWARD-DNSUPDATE
-~~~~~~~~~~~~~~~~~
-
-See `Configuration options <dnsupdate-configuration-options>` for what it does,
-but per domain.
-
-::
-
-    sql> select id from domains where name='example.org';
-    5
-    sql> insert into domainmetadata(domain_id, kind, content) values(5, ‘FORWARD-DNSUPDATE’,’’);
-
-There is no content, the existence of the entry enables the forwarding.
-This domain-specific setting is only useful when the configuration
-option :ref:`setting-forward-dnsupdate` is set to 'no', as that will disable it
-globally. Using the domainmetadata setting than allows you to enable it
-per domain.
-
-.. _metadata-notify-dnsupdate:
-
-NOTIFY-DNSUPDATE
-~~~~~~~~~~~~~~~~
-
-Send a notification to all slave servers after every update. This will
-speed up the propagation of changes and is very useful for acme
-verification.
-
-::
-
-    sql> select id from domains where name='example.org';
-    5
-    sql> insert into domainmetadata(domain_id, kind, content) values(5, ‘NOTIFY-DNSUPDATE’,’1’);
-
-.. _metadata-soa-edit-dnsupdate:
-
-SOA-EDIT-DNSUPDATE
-~~~~~~~~~~~~~~~~~~
-
-This configures how the soa serial should be updated. See
-:ref:`below <dnsupdate-soa-serial-updates>`.
-
-.. _dnsupdate-soa-serial-updates:
-
-SOA Serial Updates
-------------------
-
-After every update, the soa serial is updated as this is required by
-section 3.7 of :rfc:`2136`. The behaviour is configurable via domainmetadata
-with the ``SOA-EDIT-DNSUPDATE`` option. It has a number of options listed
-below. If no behaviour is specified, DEFAULT is used.
-
-:rfc:`2136, Section 3.6 <2136#section-3.6>` defines some specific behaviour for updates of SOA
-records. Whenever the SOA record is updated via the update message, the
-logic to change the SOA is not executed.
-
-.. note::
-  Powerdns will always use :ref:`metadata-soa-edit` when serving SOA
-  records, thus a query for the SOA record of the recently update domain,
-  might have an unexpected result due to a SOA-EDIT setting.
-
-An example:
-
-::
-
-    sql> select id from domains where name='example.org';
-    5
-    sql> insert into domainmetadata(domain_id, kind, content) values(5, ‘SOA-EDIT-DNSUPDATE’,’INCREASE’);
-
-This will make the SOA Serial increase by one, for every successful
-update.
-
-SOA-EDIT-DNSUPDATE settings
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-These are the settings available for **SOA-EDIT-DNSUPDATE**.
-
--  DEFAULT: Generate a soa serial of YYYYMMDD01. If the current serial
-   is lower than the generated serial, use the generated serial. If the
-   current serial is higher or equal to the generated serial, increase
-   the current serial by 1.
--  INCREASE: Increase the current serial by 1.
--  EPOCH: Change the serial to the number of seconds since the EPOCH,
-   aka unixtime.
--  SOA-EDIT: Change the serial to whatever SOA-EDIT would provide. See
-   :doc:`Domain metadata <domainmetadata>`
--  SOA-EDIT-INCREASE: Change the serial to whatever SOA-EDIT would
-   provide. If what SOA-EDIT provides is lower than the current serial,
-   increase the current serial by 1.
-
-DNS update How-to: Setup dyndns/rfc2136 with dhcpd
---------------------------------------------------
-
-DNS update is often used with DHCP to automatically provide a hostname
-whenever a new IP-address is assigned by the DHCP server. This section
-describes how you can setup PowerDNS to receive DNS updates from ISC's
-dhcpd (version 4.1.1-P1).
-
-Setting up dhcpd
-~~~~~~~~~~~~~~~~
-
-We're going to use a TSIG key for security. We're going to generate a
-key using the following command:
-
-::
-
-    dnssec-keygen -a hmac-md5 -b 128 -n USER dhcpdupdate
-
-This generates two files (Kdhcpdupdate.*.key and
-Kdhcpdupdate.*.private). You're interested in the .key file:
-
-::
-
-    # ls -l Kdhcp*
-    -rw------- 1 root root  53 Aug 26 19:29 Kdhcpdupdate.+157+20493.key
-    -rw------- 1 root root 165 Aug 26 19:29 Kdhcpdupdate.+157+20493.private
-
-    # cat Kdhcpdupdate.+157+20493.key
-    dhcpdupdate. IN KEY 0 3 157 FYhvwsW1ZtFZqWzsMpqhbg==
-
-The important bits are the name of the key (**dhcpdupdate**) and the
-hash of the key (**FYhvwsW1ZtFZqWzsMpqhbg==**
-
-Using the details from the key you've just generated. Add the following
-to your dhcpd.conf:
-
-::
-
-    key "dhcpdupdate" {
-            algorithm hmac-md5;
-            secret "FYhvwsW1ZtFZqWzsMpqhbg==";
-    };
-
-You must also tell dhcpd that you want dynamic dns to work, add the
-following section:
-
-::
-
-    ddns-updates on;
-    ddns-update-style interim;
-    update-static-leases on;
-
-This tells dhcpd to:
-
-1. Enable Dynamic DNS
-2. Which style it must use (interim)
-3. Update static leases as well
-
-For more information on this, consult the dhcpd.conf manual.
-
-Per subnet, you also have to tell **dhcpd** which (reverse-)domain it
-should update and on which master domain server it is running.
-
-::
-
-    ddns-domainname "example.org";
-    ddns-rev-domainname "in-addr.arpa.";
-
-    zone example.org {
-        primary 127.0.0.1;
-        key dhcpdupdate;
-    }
-
-    zone 1.168.192.in-addr.arpa. {
-        primary 127.0.0.1;
-        key dhcpdupdate;
-    }
-
-This tells **dhcpd** a number of things:
-
-1. Which domain to use (**ddns-domainname "example.org";**)
-2. Which reverse-domain to use (**dnssec-rev-domainname
-   "in-addr.arpa.";**)
-3. For the zones, where the primary master is located (**primary
-   127.0.0.1;**)
-4. Which TSIG key to use (**key dhcpdupdate;**). We defined the key
-   earlier.
-
-This concludes the changes that are needed to the **dhcpd**
-configuration file.
-
-Setting up PowerDNS
-~~~~~~~~~~~~~~~~~~~
-
-A number of small changes are needed to powerdns to make it accept
-dynamic updates from **dhcpd**.
-
-Enabled DNS update (:rfc:`2136`) support functionality in PowerDNS by adding
-the following to the PowerDNS configuration file (pdns.conf).
-
-::
-
-    dnsupdate=yes
-    allow-dnsupdate-from=
-
-This tells PowerDNS to:
-
-1. Enable DNS update support(:ref:`setting-dnsupdate`)
-2. Allow updates from NO ip-address (":ref:`setting-allow-dnsupdate-from`\ =")
-
-We just told powerdns (via the configuration file) that we accept
-updates from nobody via the :ref:`setting-allow-dnsupdate-from`
-parameter. That's not very useful, so we're going to give permissions
-per zone (including the appropriate reverse zone), via the
-domainmetadata table.
-
-::
-
-    sql> select id from domains where name='example.org';
-    5
-    sql> insert into domainmetadata(domain_id, kind, content) values(5, 'ALLOW-DNSUPDATE-FROM','127.0.0.1');
-    sql> select id from domains where name='1.168.192.in-addr.arpa';
-    6
-    sql> insert into domainmetadata(domain_id, kind, content) values(6, 'ALLOW-DNSUPDATE-FROM','127.0.0.1');
-
-This gives the ip '127.0.0.1' access to send update messages. Make sure
-you use the ip address of the machine that runs **dhcpd**.
-
-Another thing we want to do, is add TSIG security. This can only be done
-via the domainmetadata table:
-
-::
-
-    sql> insert into tsigkeys (name, algorithm, secret) values ('dhcpdupdate', 'hmac-md5', 'FYhvwsW1ZtFZqWzsMpqhbg==');
-    sql> select id from domains where name='example.org';
-    5
-    sql> insert into domainmetadata (domain_id, kind, content) values (5, 'TSIG-ALLOW-DNSUPDATE', 'dhcpdupdate');
-    sql> select id from domains where name='1.168.192.in-addr.arpa';
-    6
-    sql> insert into domainmetadata (domain_id, kind, content) values (6, 'TSIG-ALLOW-DNSUPDATE', 'dhcpdupdate');
-
-This will:
-
-1. Add the 'dhcpdupdate' key to our PowerDNSinstallation
-2. Associate the domains with the given TSIG key
-
-Restart PowerDNS and you should be ready to go!
-
-.. _dnsupdate-how-it-works:
-
-How it works
-------------
-
-This is a short description of how DNS update messages are processed by
-PowerDNS.
-
-1.  The DNS update message is received. If it is TSIG signed, the TSIG
-    is validated against the tsigkeys table. If it is not valid, Refused
-    is returned to the requestor.
-2.  A check is performed on the zone to see if it is a valid zone.
-    ServFail is returned when not valid.
-3.  The **dnsupdate** setting is checked. Refused is returned when the
-    setting is 'no'.
-4.  If update policy Lua script is provided then next two steps are
-    skipped.
-5.  If the **ALLOW-DNSUPDATE-FROM** has a value (from both
-    domainmetadata and the configuration file), a check on the value is
-    performed. If the requestor (sender of the update message) does not
-    match the values in **ALLOW-DNSUPDATE-FROM**, Refused is returned.
-6.  If the message is TSIG signed, the TSIG keyname is compared with the
-    TSIG keyname in domainmetadata. If they do not match, a Refused is
-    send. The TSIG-ALLOW-DNSUPDATE domainmetadata setting is used to
-    find which key belongs to the domain.
-7.  The backends are queried to find the backend for the given domain.
-8.  If the domain is a slave domain, the **forward-dnsupdate** option
-    and domainmetadata settings are checked. If forwarding to a master
-    is enabled, the message is forward to the master. If that fails, the
-    next master is tried until all masters are tried. If all masters
-    fail, ServFail is returned. If a master succeeds, the result from
-    that master is returned.
-9.  A check is performed to make sure all updates/prerequisites are for
-    the given zone. NotZone is returned if this is not the case.
-10. The transaction with the backend is started.
-11. The prerequisite checks are performed (section 3.2 of :rfc:`2136 <2136#section-3.2>`). If a
-    check fails, the corresponding RCode is returned. No further
-    processing will happen.
-12. Per record in the update message, a the prescan checks are
-    performed. If the prescan fails, the corresponding RCode is
-    returned. If the prescan for the record is correct, the actual
-    update/delete/modify of the record is performed. If the update fails
-    (for whatever reason), ServFail is returned. After changes to the
-    records have been applied, the ordername and auth flag are set to
-    make sure DNSSEC remains working. The cache for that record is
-    purged.
-13. If there are records updated and the SOA record was not modified,
-    the SOA serial is updated. See :ref:`dnsupdate-soa-serial-updates`. The cache for this record is
-    purged.
-14. The transaction with the backend is committed. If this fails,
-    ServFail is returned.
-15. NoError is returned.
-
-.. _dnsupdate-update-policy:
-
-Update policy
--------------
-
-.. versionadded:: 4.1.0
-
-You can define a Lua script to handle DNS UPDATE message
-authorization. The Lua script is to contain at least function called
-``updatepolicy`` which accepts one parameter. This parameter is an
-object, containing all the information for the request. To permit
-change, return true, otherwise return false. The script is called for
-each record at a time and you can approve or reject any or all.
-
-The object has following methods available:
-
-- DNSName getQName() - name to update
-- DNSName getZonename() - zone name
-- int getQType() - record type, it can be 255(ANY) for delete.
-- ComboAddress getLocal() - local socket address
-- ComboAddress getRemote() - remote socket address
-- Netmask getRealRemote() - real remote address (or netmask if EDNS Subnet is used)
-- DNSName getTsigName() - TSIG **key** name (you can assume it is validated here)
-- string getPeerPrincipal() - Return peer principal name (user@DOMAIN, service/machine.name@DOMAIN, host/MACHINE$@DOMAIN)
-
-There are many same things available as in recursor Lua scripts, but
-there is also resolve(qname, qtype) which returns array of records.
-Example:
-
-::
-
-    resolve("www.google.com", pdns.A)
-
-You can use this to perform DNS lookups. If your resolver cannot find
-your local records, then this will not find them either. In other words,
-resolve does not perform local lookup.
-
-Simple example script:
-
-.. code:: lua
-
-    --- This script is not suitable for production use
-
-    function strpos (haystack, needle, offset)
-      local pattern = string.format("(%s)", needle)
-      local i       = string.find (haystack, pattern, (offset or 0))
-      return (i ~= nil and i or false)
-    end
-
-    function updatepolicy(input)
-      princ = input:getPeerPrincipal()
-
-      if princ == ""
-      then
-        return false
-      end
-
-      if princ == "admin@DOMAIN" or input:getRemote():toString() == "192.168.1.1"
-      then
-        return true
-      end
-
-      if (input:getQType() == pdns.A or input:getQType() == pdns.AAAA) and princ:sub(5,5) == '/' and strpos(princ, "@", 0) ~= false
-      then
-        i = strpos(princ, "@", 0)
-        if princ:sub(i) ~= "@DOMAIN"
-        then
-          return false
-        end
-        hostname = princ:sub(6, i-1)
-        if input:getQName():toString() == hostname .. "." or input:getQName():toString() == hostname .. "." .. input:getZoneName():toString()
-        then
-          return true
-        end
-      end
-
-      return false
-    end
index 3bdb5485e342dec6d56223114608184c7cacdf7f..a47aa950d6ddf25a271919476c92a6849a6fb95b 100644 (file)
@@ -17,7 +17,7 @@ PowerDNS Authoritative Server
     domainmetadata
     dnsupdate
     tsig
-    lua-records
+    lua-records/index
     guides/index
     backends/index
     http-api/index
similarity index 59%
rename from docs/lua-records.rst
rename to docs/lua-records/functions.rst
index 740e8cc6f2cb2b37d16c1a0883dfeff590cc1a05..da4937f6c9426d6cfbc797613d381a4b865cb518 100644 (file)
@@ -1,179 +1,3 @@
-Lua Records
-===========
-
-To facilitate dynamic behaviour, such as Global Server Load Balancing,
-PowerDNS Authoritative Server version 4.2 and later support dynamic DNS
-records.
-
-These records contain small snippets of configuration that enable dynamic
-behaviour based on requester IP address, requester's EDNS Client Subnet,
-server availability or other factors.
-
-Capabilities range from very simple to highly advanced multi-pool
-geographically & weighed load balanced IP address selection.
-
-Although users need not be aware, PowerDNS dynamic DNS records are actually
-tiny (or larger) `Lua <https://www.lua.org>`_ statements. 
-
-.. note::
-  This is a PowerDNS specific feature, and is not (yet) standardized by the
-  IETF or other standards bodies. We are committed however to
-  interoperability, and strive to turn this functionality into a broadly
-  supported standard.
-
-To enable this feature, either set 'enable-lua-record' in the configuration,
-or set the 'ENABLE-LUA-RECORD' per-zone metadata item to 1.  
-
-In addition, to benefit from the geographical features, make sure the PowerDNS
-launch statement includes the ``geoip`` backend.
-
-Examples
---------
-
-Before delving into the details, some examples may be of use to explain what
-dynamic records can do.
-
-Here is a very basic example::
-
-     www    IN    LUA    A    "ifportup(443, {'192.0.2.1', '192.0.2.2'})"
-
-This turns the 'www' name within a zone into a special record that will
-randomly return 192.0.2.1 or 192.0.2.2, as long as both of these IP
-addresses listen on port 443. 
-
-If either IP address stops listening, only the other address will be
-returned. If all IP addresses are down, all candidates are returned.
-
-Because DNS queries require rapid answers, server availability is not checked
-synchronously. In the background, a process periodically determines if IP
-addresses mentioned in availability rules are, in fact, available.
-
-Another example::
-  
-    www    IN    LUA    A    "pickclosest({'192.0.2.1','192.0.2.2','198.51.100.1'})"
-
-This uses the GeoIP backend to find indications of the geographical location of
-the requester and the listed IP addresses. It will return with one of the closest
-addresses.
-
-``pickclosest`` and ifportup can be combined as follows::
-
-  www    IN    LUA    A    ("ifportup(443, {'192.0.2.1', '192.0.2.2', '198.51.100.1'}"
-                            ", {selector='pickclosest'})                                 ")
-
-This will pick from the viable IP addresses the one deemed closest to the user.                         
-
-Using LUA Records with Generic SQL backends
--------------------------------------------
-
-It's possible to use Lua records with the Generic SQL backends such as gmysql and gpgsql.
-
-Be aware that due to the fact that Lua records uses both double and single quotes, you will
-need to appropriately escape them in INSERT/UPDATE queries.
-
-Here is an example from the previous section (``pickclosest``) which should work 
-for both **MySQL** and **PostgreSQL**::
-
-    -- Create the zone example.com
-    INSERT INTO domains (id, name, type) VALUES (1, 'example.com', 'NATIVE');
-
-    -- Enable Lua records for the zone (if not enabled globally)
-    INSERT INTO domainmetadata (domain_id, kind, content) 
-    VALUES (1, 'ENABLE-LUA-RECORD', 1);
-
-    -- Create a pickClosest() Lua A record.
-    -- Double single quotes are used to escape single quotes in both MySQL and PostgreSQL
-    INSERT INTO records (domain_id, name, type, content, ttl)
-    VALUES (
-      1, 
-      'www.example.com',
-      'LUA', 
-      'A "pickclosest({''192.0.2.1'',''192.0.2.2'',''198.51.100.1''})"',
-      600
-    );
-
-The above queries create a zone ``example.com``, enable Lua records for the zone using ``ENABLE-LUA-RECORD``,
-and finally insert a LUA A record for the ``www`` subdomain using the previous pickclosest example.
-
-See `Details & Security`_ for more information about enabling Lua records, and the risks involved.
-
-Record format
--------------
-.. note::
-  The fine authors of the Lua programming language insist that it is Lua and
-  not LUA. Lua means 'moon' in Portuguese, and it is not an abbreviation.
-  Sadly, it is DNS convention for record types to be all uppercase. Sorry. 
-
-The LUA record consists of an initial query type, which is the selector on
-which the snippet will trigger. Optionally this query type itself can be LUA
-again for configuration scripts. The query type is then followed by the
-actual Lua snippet.
-
-LUA records can have TTL settings, and these will be honoured. In addition,
-LUA record output can be DNSSEC signed like any other record, but see below
-for further details.
-
-More powerful example
----------------------
-
-A more powerful example::
-
-    west    IN    LUA    A    ( "ifurlup('https://www.lua.org/',                  "
-                                "{{'192.0.2.1', '192.0.2.2'}, {'198.51.100.1'}},  "
-                                "{stringmatch='Programming in Lua'})              " )
-
-In this case, IP addresses are tested to see if they will serve
-https for 'www.lua.org', and if that page contains the string 'Programming
-in Lua'.
-
-Two sets of IP addresses are supplied.  If an IP address from the first set
-is available, it will be returned. If no addresses work in the first set,
-the second set is tried.
-
-This configuration makes sense in the following context::
-
-    www    IN    LUA    CNAME   ( ";if(continent('EU')) then return 'west.powerdns.org' "
-                                  "else return 'usa.powerdns.org' end" )
-
-
-This sends queries that are geolocated to Europe to 'west.powerdns.org', and
-the rest to 'usa.powerdns.org'. The configuration for that name would then
-be::
-
-    usa    IN    LUA    A    ( "ifurlup('https://www.lua.org/',           "
-                               "{{'198.51.100.1'}, {'192.0.2.1', '192.0.2.2'}},  "
-                               "{stringmatch='Programming in Lua'})              " )
-
-Note that the sets of IP addresses have reversed order - visitors geolocated
-outside of Europe will hit 198.51.100.1 as long as it is available, and the
-192.0.2.1 and 192.0.2.2 servers as backup.
-
-Advanced topics
----------------
-By default, LUA records are executed with 'return ' prefixed to them. This saves
-a lot of typing for common cases. To run actual Lua scripts, start a record with a ';'
-which indicates no 'return ' should be prepended.
-
-To keep records more concise and readable, configuration can be stored in
-separate records. The full example from above can also be written as::
-
-    config    IN    LUA    LUA ("settings={stringmatch='Programming in Lua'}  "
-                                "EUips={'192.0.2.1', '192.0.2.2'}             "
-                                "USAips={'198.51.100.1'}                      ")
-
-    www       IN    LUA    CNAME ( ";if(continent('EU')) then return 'west.powerdns.org' "
-                                   "else return 'usa.powerdns.org' end" )
-
-    usa       IN    LUA    A    ( ";include('config')                               "
-                                  "return ifurlup('https://www.lua.org/',        "
-                                  "{USAips, EUips}, settings)                    " )
-
-    west      IN    LUA    A    ( ";include('config')                               "
-                                  "return ifurlup('https://www.lua.org/',        "
-                                  "{EUips, USAips}, settings)                    " )
-
-
-
 Preset variables
 ----------------
 
@@ -341,7 +165,7 @@ Record creation functions
   See :func:`pickwhashed` for an example.
 
 Reverse DNS functions
-~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~
 
 .. warning::
   The reverse DNS functions are under active development. **They may**
@@ -480,7 +304,6 @@ Reverse DNS functions
     $ dig +short AAAA 2001-a-b--1.static6.example.com @ns1.example.com
     2001:a:b::1
 
-
 Helper functions
 ~~~~~~~~~~~~~~~~
 
@@ -516,28 +339,3 @@ Helper functions
   Returns true if ``bestwho`` is within any of the listed subnets.
 
   :param [string] netmasks: The list of IP addresses to check against
-
-Details & Security
-------------------
-LUA records are synthesized on query. They can also be transferred via AXFR
-to other PowerDNS servers.
-
-LUA records themselves can not be queried however, as this would allow third parties to see load balancing internals
-they do not need to see.
-
-A non-supporting DNS server will also serve a zone with LUA records, but
-they will not function, and will in fact leak the contents of the LUA record.
-
-.. note::
-  Under NO circumstances serve LUA records from zones from untrusted sources! 
-  LUA records will be able to bring down your system and possible take over
-  control of it. Use TSIG on AXFR even from trusted sources!
-
-LUA records can be DNSSEC signed, but because they are dynamic, it is not
-possible to combine pre-signed DNSSEC zone and LUA records. In other words,
-the signing key must be available on the server creating answers based on
-LUA records.
-
-Note that to protect operators, support for the LUA record must be enabled
-explicitly, either globally (``enable-lua-record``) or per zone
-(``ENABLE-LUA-RECORD`` = 1).
diff --git a/docs/lua-records/index.rst b/docs/lua-records/index.rst
new file mode 100644 (file)
index 0000000..5f2a19a
--- /dev/null
@@ -0,0 +1,207 @@
+Lua Records
+===========
+
+To facilitate dynamic behaviour, such as Global Server Load Balancing,
+PowerDNS Authoritative Server version 4.2 and later support dynamic DNS
+records.
+
+These records contain small snippets of configuration that enable dynamic
+behaviour based on requester IP address, requester's EDNS Client Subnet,
+server availability or other factors.
+
+Capabilities range from very simple to highly advanced multi-pool
+geographically & weighed load balanced IP address selection.
+
+Although users need not be aware, PowerDNS dynamic DNS records are actually
+tiny (or larger) `Lua <https://www.lua.org>`_ statements.
+
+.. note::
+  This is a PowerDNS specific feature, and is not (yet) standardized by the
+  IETF or other standards bodies. We are committed however to
+  interoperability, and strive to turn this functionality into a broadly
+  supported standard.
+
+To enable this feature, either set 'enable-lua-record' in the configuration,
+or set the 'ENABLE-LUA-RECORD' per-zone metadata item to 1.
+
+In addition, to benefit from the geographical features, make sure the PowerDNS
+launch statement includes the ``geoip`` backend.
+
+Examples
+--------
+
+Before delving into the details, some examples may be of use to explain what
+dynamic records can do.
+
+Here is a very basic example::
+
+     www    IN    LUA    A    "ifportup(443, {'192.0.2.1', '192.0.2.2'})"
+
+This turns the 'www' name within a zone into a special record that will
+randomly return 192.0.2.1 or 192.0.2.2, as long as both of these IP
+addresses listen on port 443.
+
+If either IP address stops listening, only the other address will be
+returned. If all IP addresses are down, all candidates are returned.
+
+Because DNS queries require rapid answers, server availability is not checked
+synchronously. In the background, a process periodically determines if IP
+addresses mentioned in availability rules are, in fact, available.
+
+Another example::
+
+    www    IN    LUA    A    "pickclosest({'192.0.2.1','192.0.2.2','198.51.100.1'})"
+
+This uses the GeoIP backend to find indications of the geographical location of
+the requester and the listed IP addresses. It will return with one of the closest
+addresses.
+
+``pickclosest`` and ifportup can be combined as follows::
+
+  www    IN    LUA    A    ("ifportup(443, {'192.0.2.1', '192.0.2.2', '198.51.100.1'}"
+                            ", {selector='pickclosest'})                             ")
+
+This will pick from the viable IP addresses the one deemed closest to the user.
+
+Using LUA Records with Generic SQL backends
+-------------------------------------------
+
+It's possible to use Lua records with the Generic SQL backends such as gmysql and gpgsql.
+
+Be aware that due to the fact that Lua records uses both double and single quotes, you will
+need to appropriately escape them in INSERT/UPDATE queries.
+
+Here is an example from the previous section (``pickclosest``) which should work 
+for both **MySQL** and **PostgreSQL**::
+
+    -- Create the zone example.com
+    INSERT INTO domains (id, name, type) VALUES (1, 'example.com', 'NATIVE');
+
+    -- Enable Lua records for the zone (if not enabled globally)
+    INSERT INTO domainmetadata (domain_id, kind, content) 
+    VALUES (1, 'ENABLE-LUA-RECORD', 1);
+
+    -- Create a pickClosest() Lua A record.
+    -- Double single quotes are used to escape single quotes in both MySQL and PostgreSQL
+    INSERT INTO records (domain_id, name, type, content, ttl)
+    VALUES (
+      1, 
+      'www.example.com',
+      'LUA', 
+      'A "pickclosest({''192.0.2.1'',''192.0.2.2'',''198.51.100.1''})"',
+      600
+    );
+
+The above queries create a zone ``example.com``, enable Lua records for the zone using ``ENABLE-LUA-RECORD``,
+and finally insert a LUA A record for the ``www`` subdomain using the previous pickclosest example.
+
+See `Details & Security`_ for more information about enabling Lua records, and the risks involved.
+
+Record format
+-------------
+.. note::
+  The fine authors of the Lua programming language insist that it is Lua and
+  not LUA. Lua means 'moon' in Portuguese, and it is not an abbreviation.
+  Sadly, it is DNS convention for record types to be all uppercase. Sorry.
+
+The LUA record consists of an initial query type, which is the selector on
+which the snippet will trigger. Optionally this query type itself can be LUA
+again for configuration scripts. The query type is then followed by the
+actual Lua snippet.
+
+LUA records can have TTL settings, and these will be honoured. In addition,
+LUA record output can be DNSSEC signed like any other record, but see below
+for further details.
+
+More powerful example
+---------------------
+
+A more powerful example::
+
+    west    IN    LUA    A    ( "ifurlup('https://www.lua.org/',                  "
+                                "{{'192.0.2.1', '192.0.2.2'}, {'198.51.100.1'}},  "
+                                "{stringmatch='Programming in Lua'})              " )
+
+In this case, IP addresses are tested to see if they will serve
+https for 'www.lua.org', and if that page contains the string 'Programming
+in Lua'.
+
+Two sets of IP addresses are supplied.  If an IP address from the first set
+is available, it will be returned. If no addresses work in the first set,
+the second set is tried.
+
+This configuration makes sense in the following context::
+
+    www    IN    LUA    CNAME   ( ";if(continent('EU')) then return 'west.powerdns.org' "
+                                  "else return 'usa.powerdns.org' end" )
+
+
+This sends queries that are geolocated to Europe to 'west.powerdns.org', and
+the rest to 'usa.powerdns.org'. The configuration for that name would then
+be::
+
+    usa    IN    LUA    A    ( "ifurlup('https://www.lua.org/',           "
+                               "{{'198.51.100.1'}, {'192.0.2.1', '192.0.2.2'}},  "
+                               "{stringmatch='Programming in Lua'})              " )
+
+Note that the sets of IP addresses have reversed order - visitors geolocated
+outside of Europe will hit 198.51.100.1 as long as it is available, and the
+192.0.2.1 and 192.0.2.2 servers as backup.
+
+Advanced topics
+---------------
+By default, LUA records are executed with 'return ' prefixed to them. This saves
+a lot of typing for common cases. To run actual Lua scripts, start a record with a ';'
+which indicates no 'return ' should be prepended.
+
+To keep records more concise and readable, configuration can be stored in
+separate records. The full example from above can also be written as::
+
+    config    IN    LUA    LUA ("settings={stringmatch='Programming in Lua'}  "
+                                "EUips={'192.0.2.1', '192.0.2.2'}             "
+                                "USAips={'198.51.100.1'}                      ")
+
+    www       IN    LUA    CNAME ( ";if(continent('EU')) then return 'west.powerdns.org' "
+                                   "else return 'usa.powerdns.org' end" )
+
+    usa       IN    LUA    A    ( ";include('config')                               "
+                                  "return ifurlup('https://www.lua.org/',        "
+                                  "{USAips, EUips}, settings)                    " )
+
+    west      IN    LUA    A    ( ";include('config')                               "
+                                  "return ifurlup('https://www.lua.org/',        "
+                                  "{EUips, USAips}, settings)                    " )
+
+Details & Security
+------------------
+LUA records are synthesized on query. They can also be transferred via AXFR
+to other PowerDNS servers.
+
+LUA records themselves can not be queried however, as this would allow third parties to see load balancing internals
+they do not need to see.
+
+A non-supporting DNS server will also serve a zone with LUA records, but
+they will not function, and will in fact leak the contents of the LUA record.
+
+.. note::
+  Under NO circumstances serve LUA records from zones from untrusted sources!
+  LUA records will be able to bring down your system and possible take over
+  control of it. Use TSIG on AXFR even from trusted sources!
+
+LUA records can be DNSSEC signed, but because they are dynamic, it is not
+possible to combine pre-signed DNSSEC zone and LUA records. In other words,
+the signing key must be available on the server creating answers based on
+LUA records.
+
+Note that to protect operators, support for the LUA record must be enabled
+explicitly, either globally (``enable-lua-record``) or per zone
+(``ENABLE-LUA-RECORD`` = 1).
+
+Reference
+---------
+
+ .. toctree::
+  :maxdepth: 2
+
+  functions
+  reference/index
diff --git a/docs/lua-records/reference/comboaddress.rst b/docs/lua-records/reference/comboaddress.rst
new file mode 100644 (file)
index 0000000..56156e2
--- /dev/null
@@ -0,0 +1,112 @@
+.. _ComboAddress:
+
+ComboAddress objects
+^^^^^^^^^^^^^^^^^^^^
+
+IP addresses are moved around in a native format, called a ComboAddress.
+ComboAddresses can be IPv4 or IPv6, and unless you want to know, you don’t need to.
+
+Functions and methods of a ``ComboAddress``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. function:: newCA(address) -> ComboAddress
+
+  Returns a new :class:`ComboAddress` object based on address
+
+  :param string address: The IP address, with optional port, to represent
+
+  .. code-block:: lua
+
+    addr = newCA("1.2.3.4")
+
+.. class:: ComboAddress
+
+  A ``ComboAddress`` represents an IP address with possibly a port number.
+  The object can be an IPv4 or an IPv6 address.
+  It has these methods:
+
+  .. method:: ComboAddress:getPort() -> int
+
+    Returns the port number.
+
+  .. method:: ComboAddress:isIPv4() -> bool
+
+    Returns true if the address is an IPv4, false otherwise
+
+  .. method:: ComboAddress:isIPv6() -> bool
+
+    Returns true if the address is an IPv6, false otherwise
+
+  .. method:: ComboAddress:isMappedIPv4() -> bool
+
+    Returns true if the address is an IPv4 mapped into an IPv6, false otherwise
+
+  .. method:: ComboAddress:mapToIPv4() -> ComboAddress
+
+    Convert an IPv4 address mapped in a v6 one into an IPv4.
+    Returns a new ComboAddress
+
+  .. method:: ComboAddress:toString() -> string
+
+    Returns in human-friendly format
+
+  .. method:: ComboAddress:getRaw() -> string
+
+    Returns in raw bytes format format
+
+  .. method:: ComboAddress:toStringWithPort() -> string
+
+    Returns in human-friendly format, with port number
+
+  .. method:: ComboAddress:truncate(bits)
+
+    Truncate the ComboAddress to the specified number of bits.
+    This essentially zeroes all bits after ``bits``.
+
+    :param int bits: Amount of bits to truncate to
+
+.. _ComboAddressSet:
+
+ComboAddressSet objects
+^^^^^^^^^^^^^^^^^^^^^^^
+
+We provide a convenient object class that can store unique ComboAddresses in no particular
+order and allows fast retrieval of individual elements based on their values
+
+  .. code-block:: lua
+
+    addr = newCA("1.2.3.4")
+    myset = newCAS()
+    myset:add(addr)
+    if myset:check(addr) then -- prints "found!"
+      print('found!')
+    end
+
+Functions and methods of a ``ComboAddressSet``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. function:: newCAS() -> ComboAddressSet
+
+  Returns an empty :class:`ComboAddressSet` object
+
+.. class:: ComboAddressSet
+
+  A ``ComboAddressSet`` can store multiple `ComboAddress`
+
+  It has these methods:
+
+  .. method:: ComboAddressSet:add(addr)
+
+    Add the given `addr` to set. `addr` can be of the following types
+
+    :param ComboAddress addr: The `ComboAddress` object to add to set
+    :param string addr: Handy way to add `ComboAddress` from its string representation
+    :param [string] addr: Add the given list of addresses to the set
+
+  .. code-block:: lua
+
+    addr = newCA("1.2.3.4")
+    myset = newCAS()
+    myset:add(addr)
+    myset:add("5.6.7.8")
+    myset:add({"::1/128", "10.11.12.13"})
diff --git a/docs/lua-records/reference/dnsheader.rst b/docs/lua-records/reference/dnsheader.rst
new file mode 100644 (file)
index 0000000..125de29
--- /dev/null
@@ -0,0 +1,60 @@
+.. _DNSHeader:
+
+DNSHeader (``dh``) object
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. class:: DNSHeader
+
+  This object holds a representation of a DNS packet's header.
+
+  .. method:: DNSHeader:getRD() -> bool
+
+    Get recursion desired flag.
+
+  .. method:: DNSHeader:getCD() -> bool
+
+    Get checking disabled flag.
+
+  .. method:: DNSHeader:getID() -> bool
+
+    Get header's ID
+
+  .. method:: DNSHeader:getTC() -> bool
+
+    Truncated message bit
+
+  .. method:: DNSHeader:getRA() -> bool
+
+    Recursion available
+
+  .. method:: DNSHeader:getAD() -> bool
+
+    Authenticated data from named
+
+  .. method:: DNSHeader:getAA() -> bool
+
+    Authoritative answer
+
+  .. method:: DNSHeader:getRCODE() -> int
+
+    Response code
+
+  .. method:: DNSHeader:getOPCODE() -> int
+
+    Purpose of message
+
+  .. method:: DNSHeader:getQDCOUNT() -> int
+
+    Number of question entries
+
+  .. method:: DNSHeader:getANCOUNT() -> int
+
+    Number of answer entries
+
+  .. method:: DNSHeader:getNSCOUNT() -> int
+
+    Number of authority entries
+
+  .. method:: DNSHeader:getARCOUNT() -> int
+
+    Number of resource entries
diff --git a/docs/lua-records/reference/dnsname.rst b/docs/lua-records/reference/dnsname.rst
new file mode 100644 (file)
index 0000000..2707269
--- /dev/null
@@ -0,0 +1,99 @@
+.. _DNSName:
+
+DNSName objects
+^^^^^^^^^^^^^^^
+
+A :class:`DNSName` object represents a name in the DNS. It has serveral functions that can manipulate it without conversions to strings.
+Creating a ``DNSName`` is done with the :func:`newDN`::
+
+  myname = newDN("www.example.com")
+
+PowerDNS will complain loudly if the name is invalid (e.g. too long, dot in the wrong place).
+
+The ``myname`` variable has several functions to get information from it
+
+.. code-block:: lua
+
+  print(myname:countLabels()) -- prints "3"
+  print(myname:wireLength()) -- prints "17"
+  name2 = newDN("example.com")
+  if myname:isPartOf(name2) then -- prints "it is"
+    print('it is')
+  end
+
+Functions and methods of a ``DNSName``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. function:: newDN(name) -> DNSName
+
+  Returns the :class:`DNSName` object of ``name``.
+
+  :param string name: The name to create a DNSName for
+
+.. class:: DNSName
+
+  A ``DNSName`` object represents a name in the DNS.
+  It is returned by several functions and has several functions to programmatically interact with it.
+
+  .. method:: DNSName:canonCompare(name) -> bool
+
+    Performs a comparaison of DNS names in canonical order.
+    Returns true if the DNSName comes before ``name``.
+    See https://tools.ietf.org/html/rfc4034#section-6
+
+    :param DNSName name: The name to compare to
+
+  .. method:: DNSName:makeRelative(name) -> DNSName
+
+    Returns a new DNSName that is relative to ``name``
+
+    .. code-block:: lua
+
+      name = newDN("bb.a.example.com.")
+      parent = newDN("example.com.")
+      rel = name:makeRelative(parent) -- contains DNSName("bb.a.")
+
+    :param DNSName name: The name to compare to
+
+  .. method:: DNSName:isPartOf(name) -> bool
+
+    Returns true if the DNSName is part of the DNS tree of ``name``.
+
+    :param DNSName name: The name to check against
+
+  .. method:: DNSName:toString() -> string
+
+    Returns a human-readable form of the DNSName
+
+  .. method:: DNSName:toStringNoDot() -> string
+
+    Returns a human-readable form of the DNSName without the trailing dot
+
+  .. method:: DNSName:chopOff() -> bool
+
+    Removes the left-most label and returns ``true``.
+    ``false`` is returned if no label was removed
+
+  .. method:: DNSName:countLabels() -> int
+
+    Returns the number of DNSLabels in the name
+
+  .. method:: DNSName:wireLength() -> int
+
+    Returns the length in bytes of the DNSName as it would be on the wire.
+
+  .. method:: DNSName::getRawLabels() -> [ string ]
+
+    Returns a table that contains the raw labels of the DNSName
+
+  .. method:: DNSName::countLabels() -> int
+
+    Returns the number of labels of the DNSName
+
+  .. method:: DNSName::equal(name) -> bool
+
+    Perform a comparaison of the DNSName to the given ``name``.
+    You can also compare directly two DNSName objects using
+    the ``==`` operator
+
+    :param string name: The name to compare to
diff --git a/docs/lua-records/reference/dnsresourcerecord.rst b/docs/lua-records/reference/dnsresourcerecord.rst
new file mode 100644 (file)
index 0000000..dba9428
--- /dev/null
@@ -0,0 +1,87 @@
+.. _DNSResourceRecord:
+
+DNSResourceRecord objects
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A :class:`DNSResourceRecord` object represents a resource record in the DNS.
+Creating a ``DNSResourceRecord`` is done with the :func:`newDRR`.
+
+.. todo
+   Add a lua example and some useful things to do with that.
+
+Functions and methods of a ``DNSResourceRecord``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. function:: newDRR(name, type, ttl, content[, domainId[, auth]]) -> DNSResourceRecord
+
+  Returns a new :class:`DNSResourceRecord` object.
+  .. todo describe the auth param
+
+  :param DNSName name: The name to the new record
+  :param string type: The name to create a DNSName for
+  :param int ttl: The TTL of the record
+  :param string content: The content of the record
+  :param int domainId: The optional domain ID of the zone the record belongs to
+  :param int auth: ?
+
+  .. todo complete LUA example bellow
+  .. code-block:: lua
+
+    name = newDN("www.example.org.")
+    rr = new DRR(name, "IN", 3600, )
+
+.. class:: DNSResourceRecord
+
+  A ``DNSResourceRecord`` object represents a DNS record.
+
+  .. method:: DNSResourceRecord:toString() -> string
+
+    Returns the full content of the record as a string
+
+  .. method:: DNSResourceRecord:qname() -> DNSName
+
+    Returns the name of the record
+
+  .. method:: DNSResourceRecord:wildcardName() -> DNSName
+
+    Returns the wildcard name of the record the record was matched against
+
+  .. method:: DNSResourceRecord:content() -> string
+
+    Returns what the record points to
+
+  .. method:: DNSResourceRecord:lastModified() -> int
+
+    If unzero, last time this record was changed
+
+  .. method:: DNSResourceRecord:ttl() -> int
+
+    TTL (Time To Live) of this record
+
+  .. method:: DNSResourceRecord:signttl() -> int
+
+    If non-zero, TTL that will be used in the RRSIG of the record
+
+  .. method:: DNSResourceRecord:domainId() -> int
+
+    Backend related domain ID of the zone the record belongs to
+
+  .. method:: DNSResourceRecord:qtype() -> int
+
+    Type of the record (A, CNAME, MX, ...)
+
+  .. method:: DNSResourceRecord:qclass() -> int
+
+    Class of the record (IN, CH, ...)
+
+  .. method:: DNSResourceRecord:scopeMask() -> int
+
+    .. todo
+
+  .. method:: DNSResourceRecord:auth() -> bool
+
+    .. auth
+
+  .. method:: DNSResourceRecord:disabled() -> bool
+
+    .. todo
diff --git a/docs/lua-records/reference/index.rst b/docs/lua-records/reference/index.rst
new file mode 100644 (file)
index 0000000..6cd1b8e
--- /dev/null
@@ -0,0 +1,12 @@
+LUA Reference
+-------------
+
+ .. toctree::
+  :maxdepth: 2
+
+  comboaddress
+  dnsheader
+  dnsname
+  dnsresourcerecord
+  netmask
+  qtype
diff --git a/docs/lua-records/reference/netmask.rst b/docs/lua-records/reference/netmask.rst
new file mode 100644 (file)
index 0000000..3b9c396
--- /dev/null
@@ -0,0 +1,105 @@
+.. _scripting-netmasks:
+
+Netmasks and NetMaskGroups
+==========================
+
+There are two classes in the PowerDNS Recursor that can be used to match IP addresses against.
+
+Netmask class
+-------------
+The :class:`Netmask` class represents an IP netmask.
+
+.. code-block:: Lua
+
+    mask = newNetmask("192.0.2.1/24")
+    mask:isIPv4() -- true
+    mask:match("192.0.2.8") -- true
+
+.. function:: newNetmask(mask) -> Netmask
+
+  Creates a new :class:`Netmask`.
+
+  :param str mask: The mask to convert.
+
+.. class:: Netmask
+
+  Represents a netmask.
+
+  .. method:: Netmask:empty() -> bool
+
+      True if the netmask doesn't contain a valid address.
+
+  .. method:: Netmask:getBits() -> int
+
+      The number of bits in the address.
+
+  .. method:: Netmask:getNetwork() -> ComboAddress
+
+      Returns a :class:`ComboAddress` representing the network (no mask applied).
+
+  .. method:: Netmask:getMaskedNetwork() -> ComboAddress
+
+      Returns a :class:`ComboAddress` representing the network (truncating according to the mask).
+
+  .. method:: Netmask:isIpv4() -> bool
+
+      True if the netmask is an IPv4 netmask.
+
+  .. method:: Netmask:isIpv6() -> bool
+
+      True if the netmask is an IPv6 netmask.
+
+  .. method:: Netmask:match(address) -> bool
+
+      True if the address passed in address matches
+
+      :param str address: IP Address to match against.
+
+  .. method:: Netmask:toString() -> str
+
+      Returns a human-friendly representation.
+
+NetMaskGroup class
+------------------
+
+NetMaskGroups are more powerful than plain Netmasks.
+They can be matched against netmasks objects:
+
+.. code-block:: lua
+
+  nmg = newNMG()
+  nmg:addMask("127.0.0.0/8")
+  nmg:addMasks({"213.244.168.0/24", "130.161.0.0/16"})
+  nmg:addMasks(dofile("bad.ips")) -- contains return {"ip1","ip2"..}
+
+  if nmg:match(dq.remoteaddr) then
+    print("Intercepting query from ", dq.remoteaddr)
+  end
+
+Prefixing a mask with ``!`` excludes that mask from matching.
+
+.. function:: newNMG() -> NetMaskGroup
+
+  Returns a new, empty :class:`NetMaskGroup`.
+
+.. class:: NetMaskGroup
+
+  IP addresses are passed to Lua in native format.
+
+  .. method:: NetMaskGroup:addMask(mask)
+
+      Adds ``mask`` to the NetMaskGroup.
+
+      :param str mask: The mask to add.
+
+  .. method:: NetMaskGroup:addMasks(masks)
+
+      Adds ``masks`` to the NetMaskGroup.
+
+      :param {str} mask: The masks to add.
+
+  .. method:: NetMaskGroup:match(address) -> bool
+
+      Returns true if ``address`` matches any of the masks in the group.
+
+      :param ComboAddress address: The IP addres to match the netmasks against.
diff --git a/docs/lua-records/reference/qtype.rst b/docs/lua-records/reference/qtype.rst
new file mode 100644 (file)
index 0000000..5b22888
--- /dev/null
@@ -0,0 +1,39 @@
+.. _QType:
+
+QType objects
+=========================
+
+The QType class lets you deal easily with the different kind
+of resource types, like 'A', 'NS', 'CNAME', etc. These types have
+both a name and a number. This class helps seamlessly move
+between them.
+
+Functions and methods of a ``QType``
+----------------------------------------------
+
+.. function:: newQType(name) -> QType
+
+  Returns a new QType object from `name`. Name can either contain the code
+  of the type prefixed with a sharp character, or its name directly
+
+  :param string name: The name of the QType
+
+  .. code-block:: lua
+
+    type = newQType("CNAME")
+    anothertype = newQType("#5")
+    if type == anothertype then -- prints "equal!"
+      print('equal!')
+    end
+
+.. class:: QType
+
+  It has these methods:
+
+  .. method:: QType:getCode() -> int
+
+  Returns the numeric code corresponding to the type
+
+  .. method:: QType:getName() -> string
+
+  Returns the name of the type
index ee596c8eb6ac3373abceb865f2ae1738b04ca479..34be790dad9d03554059b42258485a67077f6457 100644 (file)
@@ -64,10 +64,9 @@ void BaseLua4::prepareContext() {
   d_lw->registerFunction("isPartOf", &DNSName::isPartOf);
   d_lw->registerFunction("getRawLabels", &DNSName::getRawLabels);
   d_lw->registerFunction<unsigned int(DNSName::*)()>("countLabels", [](const DNSName& name) { return name.countLabels(); });
-  d_lw->registerFunction<size_t(DNSName::*)()>("wirelength", [](const DNSName& name) { return name.wirelength(); });
+  d_lw->registerFunction<size_t(DNSName::*)()>("wireLength", [](const DNSName& name) { return name.wirelength(); });
   d_lw->registerFunction<bool(DNSName::*)(const std::string&)>("equal", [](const DNSName& lhs, const std::string& rhs) { return lhs==DNSName(rhs); });
   d_lw->registerEqFunction(&DNSName::operator==);
-
   d_lw->registerToStringFunction<string(DNSName::*)()>([](const DNSName&dn ) { return dn.toString(); });
   d_lw->registerFunction<string(DNSName::*)()>("toString", [](const DNSName&dn ) { return dn.toString(); });
   d_lw->registerFunction<string(DNSName::*)()>("toStringNoDot", [](const DNSName&dn ) { return dn.toStringNoDot(); });
@@ -88,14 +87,15 @@ void BaseLua4::prepareContext() {
   });
   d_lw->registerEqFunction(&DNSResourceRecord::operator==);
   d_lw->registerFunction("__lt", &DNSResourceRecord::operator<);
+  d_lw->registerToStringFunction<string(DNSResourceRecord::*)()>([](const DNSResourceRecord& rec) { return rec.getZoneRepresentation(); });
   d_lw->registerFunction<string(DNSResourceRecord::*)()>("toString", [](const DNSResourceRecord& rec) { return rec.getZoneRepresentation();} );
   d_lw->registerFunction<DNSName(DNSResourceRecord::*)()>("qname", [](DNSResourceRecord& rec) { return rec.qname; });
-  d_lw->registerFunction<DNSName(DNSResourceRecord::*)()>("wildcardname", [](DNSResourceRecord& rec) { return rec.wildcardname; });
+  d_lw->registerFunction<DNSName(DNSResourceRecord::*)()>("wildcardName", [](DNSResourceRecord& rec) { return rec.wildcardname; });
   d_lw->registerFunction<string(DNSResourceRecord::*)()>("content", [](DNSResourceRecord& rec) { return rec.content; });
-  d_lw->registerFunction<time_t(DNSResourceRecord::*)()>("last_modified", [](DNSResourceRecord& rec) { return rec.last_modified; });
+  d_lw->registerFunction<time_t(DNSResourceRecord::*)()>("lastModified", [](DNSResourceRecord& rec) { return rec.last_modified; });
   d_lw->registerFunction<uint32_t(DNSResourceRecord::*)()>("ttl", [](DNSResourceRecord& rec) { return rec.ttl; });
   d_lw->registerFunction<uint32_t(DNSResourceRecord::*)()>("signttl", [](DNSResourceRecord& rec) { return rec.signttl; });
-  d_lw->registerFunction<int(DNSResourceRecord::*)()>("domain_id", [](DNSResourceRecord& rec) { return rec.domain_id; });
+  d_lw->registerFunction<int(DNSResourceRecord::*)()>("domainId", [](DNSResourceRecord& rec) { return rec.domain_id; });
   d_lw->registerFunction<uint16_t(DNSResourceRecord::*)()>("qtype", [](DNSResourceRecord& rec) { return rec.qtype.getCode(); });
   d_lw->registerFunction<uint16_t(DNSResourceRecord::*)()>("qclass", [](DNSResourceRecord& rec) { return rec.qclass; });
   d_lw->registerFunction<uint8_t(DNSResourceRecord::*)()>("scopeMask", [](DNSResourceRecord& rec) { return rec.scopeMask; });
@@ -105,13 +105,13 @@ void BaseLua4::prepareContext() {
   // ComboAddress
   d_lw->registerFunction<bool(ComboAddress::*)()>("isIPv4", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET; });
   d_lw->registerFunction<bool(ComboAddress::*)()>("isIPv6", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET6; });
+  d_lw->registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
   d_lw->registerFunction<bool(ComboAddress::*)()>("isMappedIPv4", [](const ComboAddress& ca) { return ca.isMappedIPv4(); });
   d_lw->registerFunction<ComboAddress(ComboAddress::*)()>("mapToIPv4", [](const ComboAddress& ca) { return ca.mapToIPv4(); });
   d_lw->registerFunction<void(ComboAddress::*)(unsigned int)>("truncate", [](ComboAddress& ca, unsigned int bits) { ca.truncate(bits); });
   d_lw->registerFunction<string(ComboAddress::*)()>("toString", [](const ComboAddress& ca) { return ca.toString(); });
   d_lw->registerToStringFunction<string(ComboAddress::*)()>([](const ComboAddress& ca) { return ca.toString(); });
   d_lw->registerFunction<string(ComboAddress::*)()>("toStringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
-  d_lw->registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
   d_lw->registerFunction<string(ComboAddress::*)()>("getRaw", [](const ComboAddress& ca) {
       if(ca.sin4.sin_family == AF_INET) {
         auto t=ca.sin4.sin_addr.s_addr; return string((const char*)&t, 4);
@@ -122,9 +122,10 @@ void BaseLua4::prepareContext() {
 
   d_lw->writeFunction("newCA", [](const std::string& a) { return ComboAddress(a); });
   typedef std::unordered_set<ComboAddress,ComboAddress::addressOnlyHash,ComboAddress::addressOnlyEqual> cas_t;
-  d_lw->writeFunction("newCAS", []{ return cas_t(); });
+  d_lw->registerFunction<bool(ComboAddress::*)(const ComboAddress&)>("equal", [](const ComboAddress& lhs, const ComboAddress& rhs) { return ComboAddress::addressOnlyEqual()(lhs, rhs); });
 
   // cas_t
+  d_lw->writeFunction("newCAS", []{ return cas_t(); });
   d_lw->registerFunction<void(cas_t::*)(boost::variant<string,ComboAddress, vector<pair<unsigned int,string> > >)>("add",
     [](cas_t& cas, const boost::variant<string,ComboAddress,vector<pair<unsigned int,string> > >& in)
     {
@@ -142,7 +143,6 @@ void BaseLua4::prepareContext() {
       catch(std::exception& e) { g_log <<Logger::Error<<e.what()<<endl; }
     });
   d_lw->registerFunction<bool(cas_t::*)(const ComboAddress&)>("check",[](const cas_t& cas, const ComboAddress&ca) { return cas.count(ca)>0; });
-  d_lw->registerFunction<bool(ComboAddress::*)(const ComboAddress&)>("equal", [](const ComboAddress& lhs, const ComboAddress& rhs) { return ComboAddress::addressOnlyEqual()(lhs, rhs); });
 
   // QType
   d_lw->writeFunction("newQType", [](const string& s) { QType q; q = s; return q; });
index ad070e8a9fa6a2d77895817b6194f5ba8ceb6bbc..4a617a8a78147d87adde7a8d1096dcded17f05e2 100644 (file)
@@ -376,7 +376,6 @@ static ComboAddress pickclosest(const ComboAddress& bestwho, const vector<ComboA
   getLatLon(bestwho.toString(), wlat, wlon);
   //        cout<<"bestwho "<<wlat<<", "<<wlon<<endl;
   vector<string> ret;
-  g_log<<Logger::Debug<< __FILE__ << ":" << __LINE__<< " " << __func__ << " wips.size()="<< wips.size() <<endl;
   for(const auto& c : wips) {
     double lat=0, lon=0;
     getLatLon(c.toString(), lat, lon);
@@ -389,9 +388,6 @@ static ComboAddress pickclosest(const ComboAddress& bestwho, const vector<ComboA
     //          cout<<"    distance: "<<sqrt(dist2) * 40000.0/360<<" km"<<endl; // length of a degree
     ranked[dist2].push_back(c);
   }
-  g_log<<Logger::Debug<< __FILE__ << ":" << __LINE__<< " " << __func__ << " ranked.size()="<< ranked.size()<<endl;
-  ranked.begin()->second.size();
-  g_log<<Logger::Debug<< __FILE__ << ":" << __LINE__<< " " << __func__ << " ranked.size()="<< ranked.size()<<endl;
   return ranked.begin()->second[random() % ranked.begin()->second.size()];
 }