mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-30 05:57:52 +00:00
[master] better DDNS in DLZ; mysqldyn
3821. [contrib] Added a new "mysqldyn" DLZ module with dynamic update and transaction support. Thanks to Marty Lee for the contribution. [RT #35656] 3820. [func] The DLZ API doesn't pass the database version to the lookup() function; this can cause DLZ modules that allow dynamic updates to mishandle prerequisite checks. This has been corrected by adding a 'dbversion' field to the dns_clientinfo_t structure. [RT #35656]
This commit is contained in:
parent
1deeb567fa
commit
aefb3e308b
11
CHANGES
11
CHANGES
@ -1,3 +1,14 @@
|
||||
3821. [contrib] Added a new "mysqldyn" DLZ module with dynamic
|
||||
update and transaction support. Thanks to Marty
|
||||
Lee for the contribution. [RT #35656]
|
||||
|
||||
3820. [func] The DLZ API doesn't pass the database version to
|
||||
the lookup() function; this can cause DLZ modules
|
||||
that allow dynamic updates to mishandle prerequisite
|
||||
checks. This has been corrected by adding a
|
||||
'dbversion' field to the dns_clientinfo_t
|
||||
structure. [RT #35656]
|
||||
|
||||
3819. [bug] NSEC3 hashes need to be able to be entered and
|
||||
displayed without padding. This is not a issue for
|
||||
currently defined algorithms but may be for future
|
||||
|
@ -1118,7 +1118,7 @@ query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
|
||||
dns_clientinfo_t ci;
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
tresult = dns_view_searchdlz(client->view, name,
|
||||
zonelabels, &cm, &ci, &tdbp);
|
||||
@ -1254,7 +1254,7 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
|
||||
zone = NULL;
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
/*
|
||||
* We treat type A additional section processing as if it
|
||||
@ -1733,7 +1733,7 @@ query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
|
||||
additionaltype = dns_rdatasetadditional_fromauth;
|
||||
dns_name_init(&cfname, NULL);
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
CTRACE("query_addadditional2");
|
||||
|
||||
@ -2606,7 +2606,7 @@ query_addsoa(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version,
|
||||
node = NULL;
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
/*
|
||||
* Don't add the SOA record for test which set "-T nosoa".
|
||||
@ -2731,7 +2731,7 @@ query_addns(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version) {
|
||||
dns_fixedname_init(&foundname);
|
||||
fname = dns_fixedname_name(&foundname);
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
/*
|
||||
* Get resources and make 'name' be the database origin.
|
||||
@ -2899,7 +2899,7 @@ mark_secure(ns_client_t *client, dns_db_t *db, dns_name_t *name,
|
||||
rdataset->trust = dns_trust_secure;
|
||||
sigrdataset->trust = dns_trust_secure;
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
/*
|
||||
* Save the updated secure state. Ignore failures.
|
||||
@ -2936,7 +2936,7 @@ get_key(ns_client_t *client, dns_db_t *db, dns_rdata_rrsig_t *rrsig,
|
||||
dns_clientinfo_t ci;
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
if (!dns_rdataset_isassociated(keyrdataset)) {
|
||||
result = dns_db_findnodeext(db, &rrsig->signer, ISC_FALSE,
|
||||
@ -3084,7 +3084,7 @@ query_addbestns(ns_client_t *client) {
|
||||
use_zone = ISC_FALSE;
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
/*
|
||||
* Find the right database.
|
||||
@ -3410,7 +3410,7 @@ query_addwildcardproof(ns_client_t *client, dns_db_t *db,
|
||||
node = NULL;
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
/*
|
||||
* Get the NOQNAME proof then if !ispositive
|
||||
@ -4141,8 +4141,9 @@ rpz_get_zbits(ns_client_t *client,
|
||||
*/
|
||||
static isc_result_t
|
||||
rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
|
||||
dns_rpz_type_t rpz_type, dns_db_t **dbp, dns_dbversion_t *version,
|
||||
dns_rdataset_t **rdatasetp, isc_boolean_t resuming)
|
||||
dns_rpz_type_t rpz_type, dns_db_t **dbp,
|
||||
dns_dbversion_t *version, dns_rdataset_t **rdatasetp,
|
||||
isc_boolean_t resuming)
|
||||
{
|
||||
dns_rpz_st_t *st;
|
||||
isc_boolean_t is_zone;
|
||||
@ -4153,6 +4154,9 @@ rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
|
||||
dns_clientinfomethods_t cm;
|
||||
dns_clientinfo_t ci;
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
st = client->query.rpz_st;
|
||||
if ((st->state & DNS_RPZ_RECURSING) != 0) {
|
||||
INSIST(st->r.r_type == type);
|
||||
@ -4207,7 +4211,7 @@ rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
|
||||
dns_fixedname_init(&fixed);
|
||||
found = dns_fixedname_name(&fixed);
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
result = dns_db_findext(*dbp, name, version, type, DNS_DBFIND_GLUEOK,
|
||||
client->now, &node, found,
|
||||
&cm, &ci, *rdatasetp, NULL);
|
||||
@ -4345,6 +4349,9 @@ rpz_find_p(ns_client_t *client, dns_name_t *self_name, dns_rdatatype_t qtype,
|
||||
|
||||
REQUIRE(nodep != NULL);
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
/*
|
||||
* Try to find either a CNAME or the type of record demanded by the
|
||||
* request from the policy zone.
|
||||
@ -4359,8 +4366,7 @@ rpz_find_p(ns_client_t *client, dns_name_t *self_name, dns_rdatatype_t qtype,
|
||||
return (DNS_R_NXDOMAIN);
|
||||
dns_fixedname_init(&foundf);
|
||||
found = dns_fixedname_name(&foundf);
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
|
||||
result = dns_db_findext(*dbp, p_name, *versionp, dns_rdatatype_any, 0,
|
||||
client->now, nodep, found, &cm, &ci,
|
||||
*rdatasetp, NULL);
|
||||
@ -5761,7 +5767,7 @@ query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
|
||||
dns_name_clone(qname, &name);
|
||||
labels = dns_name_countlabels(&name);
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
/*
|
||||
* Map unknown algorithm to known value.
|
||||
@ -5966,7 +5972,7 @@ redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
|
||||
dns_rdataset_init(&trdataset);
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
if (WANTDNSSEC(client) && dns_db_iszone(*dbp) && dns_db_issecure(*dbp))
|
||||
return (ISC_FALSE);
|
||||
@ -6119,7 +6125,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
|
||||
is_staticstub_zone = ISC_FALSE;
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, client);
|
||||
dns_clientinfo_init(&ci, client, NULL);
|
||||
|
||||
if (event != NULL) {
|
||||
/*
|
||||
|
@ -541,9 +541,21 @@ foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
||||
isc_result_t result;
|
||||
dns_dbnode_t *node;
|
||||
dns_rdatasetiter_t *iter;
|
||||
dns_clientinfomethods_t cm;
|
||||
dns_clientinfo_t ci;
|
||||
dns_dbversion_t *oldver = NULL;
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
|
||||
/*
|
||||
* Only set the clientinfo 'versionp' if the new version is
|
||||
* different from the current version
|
||||
*/
|
||||
dns_db_currentversion(db, &oldver);
|
||||
dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL);
|
||||
|
||||
node = NULL;
|
||||
result = dns_db_findnode(db, name, ISC_FALSE, &node);
|
||||
result = dns_db_findnodeext(db, name, ISC_FALSE, &cm, &ci, &node);
|
||||
if (result == ISC_R_NOTFOUND)
|
||||
return (ISC_R_SUCCESS);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
@ -620,6 +632,18 @@ foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
||||
isc_result_t result;
|
||||
dns_dbnode_t *node;
|
||||
dns_rdataset_t rdataset;
|
||||
dns_clientinfomethods_t cm;
|
||||
dns_clientinfo_t ci;
|
||||
dns_dbversion_t *oldver = NULL;
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
|
||||
/*
|
||||
* Only set the clientinfo 'versionp' if the new version is
|
||||
* different from the current version
|
||||
*/
|
||||
dns_db_currentversion(db, &oldver);
|
||||
dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL);
|
||||
|
||||
if (type == dns_rdatatype_any)
|
||||
return (foreach_node_rr(db, ver, name,
|
||||
@ -630,7 +654,8 @@ foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
||||
(type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3))
|
||||
result = dns_db_findnsec3node(db, name, ISC_FALSE, &node);
|
||||
else
|
||||
result = dns_db_findnode(db, name, ISC_FALSE, &node);
|
||||
result = dns_db_findnodeext(db, name, ISC_FALSE,
|
||||
&cm, &ci, &node);
|
||||
if (result == ISC_R_NOTFOUND)
|
||||
return (ISC_R_SUCCESS);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
|
@ -14,8 +14,6 @@
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This provides a very simple example of an external loadable DLZ
|
||||
* driver, with update support.
|
||||
@ -251,7 +249,7 @@ dlz_create(const char *dlzname, unsigned int argc, char *argv[],
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
if (argc < 2) {
|
||||
if (argc < 2 || argv[1][0] == '\0') {
|
||||
if (state->log != NULL)
|
||||
state->log(ISC_LOG_ERROR,
|
||||
"dlz_example: please specify a zone name");
|
||||
@ -259,11 +257,16 @@ dlz_create(const char *dlzname, unsigned int argc, char *argv[],
|
||||
return (ISC_R_FAILURE);
|
||||
}
|
||||
|
||||
state->zone_name = strdup(argv[1]);
|
||||
/* Ensure zone name is absolute */
|
||||
state->zone_name = malloc(strlen(argv[1]) + 2);
|
||||
if (state->zone_name == NULL) {
|
||||
free(state);
|
||||
return (ISC_R_NOMEMORY);
|
||||
}
|
||||
if (argv[1][strlen(argv[1]) - 1] == '.')
|
||||
strcpy(state->zone_name, argv[1]);
|
||||
else
|
||||
sprintf(state->zone_name, "%s.", argv[1]);
|
||||
|
||||
if (strcmp(state->zone_name, ".") == 0)
|
||||
extra = ".root";
|
||||
@ -313,7 +316,6 @@ dlz_destroy(void *dbdata) {
|
||||
free(state);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* See if we handle a given zone
|
||||
*/
|
||||
@ -325,6 +327,7 @@ dlz_findzonedb(void *dbdata, const char *name,
|
||||
struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
|
||||
isc_sockaddr_t *src;
|
||||
char addrbuf[100];
|
||||
char absolute[1024];
|
||||
|
||||
strcpy(addrbuf, "unknown");
|
||||
if (methods != NULL &&
|
||||
@ -335,11 +338,11 @@ dlz_findzonedb(void *dbdata, const char *name,
|
||||
methods->sourceip(clientinfo, &src);
|
||||
fmt_address(src, addrbuf, sizeof(addrbuf));
|
||||
}
|
||||
fprintf(stderr, "findzonedb: connection from: %s\n", addrbuf);
|
||||
|
||||
state->log(ISC_LOG_INFO,
|
||||
"dlz_example: dlz_findzonedb called with name '%s' "
|
||||
"in zone DB '%s'", name, state->zone_name);
|
||||
"in zone DB '%s' from %s",
|
||||
name, state->zone_name, addrbuf);
|
||||
|
||||
/*
|
||||
* Returning ISC_R_NOTFOUND will cause the query logic to
|
||||
@ -374,6 +377,10 @@ dlz_findzonedb(void *dbdata, const char *name,
|
||||
if (strcasecmp(state->zone_name, name) == 0)
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
snprintf(absolute, sizeof(absolute), "%s.", name);
|
||||
if (strcasecmp(state->zone_name, absolute) == 0)
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
return (ISC_R_NOTFOUND);
|
||||
}
|
||||
|
||||
@ -394,6 +401,7 @@ dlz_lookup(const char *zone, const char *name, void *dbdata,
|
||||
isc_result_t result;
|
||||
struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
|
||||
isc_boolean_t found = ISC_FALSE;
|
||||
void *dbversion = NULL;
|
||||
isc_sockaddr_t *src;
|
||||
char full_name[256];
|
||||
char buf[512];
|
||||
@ -410,6 +418,30 @@ dlz_lookup(const char *zone, const char *name, void *dbdata,
|
||||
} else
|
||||
snprintf(full_name, 255, "%s.%s", name, state->zone_name);
|
||||
|
||||
/*
|
||||
* If we need to know the database version (as set in
|
||||
* the 'newversion' dlz function) we can pick it up from the
|
||||
* clientinfo.
|
||||
*
|
||||
* This allows a lookup to query the correct version of the DNS
|
||||
* data, if the DLZ can differentiate between versions.
|
||||
*
|
||||
* For example, if a new database transaction is created by
|
||||
* 'newversion', the lookup should query within the same
|
||||
* transaction scope if it can.
|
||||
*
|
||||
* If the DLZ only operates on 'live' data, then version
|
||||
* wouldn't necessarily be needed.
|
||||
*/
|
||||
if (clientinfo != NULL &&
|
||||
clientinfo->version >= DNS_CLIENTINFO_VERSION) {
|
||||
dbversion = clientinfo->dbversion;
|
||||
if (dbversion != NULL && *(isc_boolean_t *)dbversion)
|
||||
state->log(ISC_LOG_INFO,
|
||||
"dlz_example: lookup against live "
|
||||
"transaction\n");
|
||||
}
|
||||
|
||||
if (strcmp(name, "source-addr") == 0) {
|
||||
strcpy(buf, "unknown");
|
||||
if (methods != NULL &&
|
||||
@ -422,7 +454,8 @@ dlz_lookup(const char *zone, const char *name, void *dbdata,
|
||||
fmt_address(src, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
fprintf(stderr, "lookup: connection from: %s\n", buf);
|
||||
state->log(ISC_LOG_INFO,
|
||||
"dlz_example: lookup connection from %s\n", buf);
|
||||
|
||||
found = ISC_TRUE;
|
||||
result = state->putrr(lookup, "TXT", 0, buf);
|
||||
@ -642,6 +675,7 @@ modrdataset(struct dlz_example_data *state, const char *name,
|
||||
const char *rdatastr, struct record *list)
|
||||
{
|
||||
char *full_name, *dclass, *type, *data, *ttlstr, *buf;
|
||||
char absolute[1024];
|
||||
isc_result_t result;
|
||||
#if defined(WIN32) || defined(_REENTRANT)
|
||||
char *saveptr = NULL;
|
||||
@ -679,6 +713,11 @@ modrdataset(struct dlz_example_data *state, const char *name,
|
||||
if (data == NULL)
|
||||
goto error;
|
||||
|
||||
if (name[strlen(name) - 1] != '.') {
|
||||
snprintf(absolute, sizeof(absolute), "%s.", name);
|
||||
name = absolute;
|
||||
}
|
||||
|
||||
result = add_name(state, list, name, type,
|
||||
strtoul(ttlstr, NULL, 10), data);
|
||||
free(buf);
|
||||
@ -722,7 +761,6 @@ dlz_subrdataset(const char *name, const char *rdatastr,
|
||||
return (modrdataset(state, name, rdatastr, &state->deletes[0]));
|
||||
}
|
||||
|
||||
|
||||
isc_result_t
|
||||
dlz_delrdataset(const char *name, const char *type,
|
||||
void *dbdata, void *version)
|
||||
|
@ -67,6 +67,20 @@ status=`expr $status + $ret`
|
||||
test_update deny.example.nil. TXT "86400 TXT helloworld" "helloworld" should_fail && ret=1
|
||||
status=`expr $status + $ret`
|
||||
|
||||
echo "I:testing prerequisites are checked correctly"
|
||||
ret=0
|
||||
cat > ns1/update.txt << EOF
|
||||
server 10.53.0.1 5300
|
||||
prereq nxdomain testdc3.example.nil
|
||||
update add testdc3.example.nil 86500 in a 10.53.0.12
|
||||
send
|
||||
EOF
|
||||
$NSUPDATE -k ns1/ddns.key ns1/update.txt > /dev/null 2>&1 && ret=1
|
||||
out=`$DIG $DIGOPTS +short a testdc3.example.nil`
|
||||
[ "$out" = "10.53.0.12" ] && ret=1
|
||||
[ "$ret" -eq 0 ] || echo "I:failed"
|
||||
status=`expr $status + $ret`
|
||||
|
||||
echo "I:testing passing client info into DLZ driver"
|
||||
ret=0
|
||||
out=`$DIG $DIGOPTS +short -t txt -q source-addr.example.nil | grep -v '^;'`
|
||||
|
@ -87,6 +87,27 @@ responses. (Normally, this feature would be used to alter responses in
|
||||
some other fashion, e.g., by providing different address records for
|
||||
a particular name depending on the network from which the query arrived.)
|
||||
|
||||
DYNAMIC UPDATES AND TRANSACTIONS:
|
||||
|
||||
If a DLZ module wants to implement dynamic DNS updates (DDNS), the
|
||||
normal calling sequence is
|
||||
- dlz_newversion (start a 'transaction')
|
||||
- dlz_addrdataset (add records)
|
||||
- dlz_subrdataset (remove records)
|
||||
- dlz_closeversion (end a 'transaction')
|
||||
|
||||
However, BIND may also query the database during the transaction
|
||||
(e.g., to check prerequisites), and your DLZ might need to know whether
|
||||
the lookup is against the pre-existing data, or the new data.
|
||||
dlz_lookup() doesn't give you access to the 'versionp' pointer
|
||||
directly, so it must be passed via 'clientinfo' structure if
|
||||
it is needed.
|
||||
|
||||
The dlz_example.c code has sample code to show how to get the 'versionp'
|
||||
pointer from within dlz_lookup(). If it's set to NULL, we query
|
||||
the standard database; if non-NULL, we query against the in-flight
|
||||
data within the appropriate uncommitted transaction.
|
||||
|
||||
IMPLEMENTATION NOTES:
|
||||
|
||||
The minimal set of type definitions, prototypes, and macros needed
|
||||
|
@ -14,8 +14,6 @@
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This provides a very simple example of an external loadable DLZ
|
||||
* driver, with update support.
|
||||
@ -27,7 +25,7 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../modules/dlz_minimal.h"
|
||||
#include "../modules/include/dlz_minimal.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define STRTOK_R(a, b, c) strtok_s(a, b, c)
|
||||
@ -239,7 +237,7 @@ dlz_create(const char *dlzname, unsigned int argc, char *argv[],
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
if (argc < 2) {
|
||||
if (argc < 2 || argv[1][0] == '\0') {
|
||||
if (state->log != NULL)
|
||||
state->log(ISC_LOG_ERROR,
|
||||
"dlz_example: please specify a zone name");
|
||||
@ -247,11 +245,16 @@ dlz_create(const char *dlzname, unsigned int argc, char *argv[],
|
||||
return (ISC_R_FAILURE);
|
||||
}
|
||||
|
||||
state->zone_name = strdup(argv[1]);
|
||||
/* Ensure zone name is absolute */
|
||||
state->zone_name = malloc(strlen(argv[1]) + 2);
|
||||
if (state->zone_name == NULL) {
|
||||
free(state);
|
||||
return (ISC_R_NOMEMORY);
|
||||
}
|
||||
if (argv[1][strlen(argv[1]) - 1] == '.')
|
||||
strcpy(state->zone_name, argv[1]);
|
||||
else
|
||||
sprintf(state->zone_name, "%s.", argv[1]);
|
||||
|
||||
if (strcmp(state->zone_name, ".") == 0)
|
||||
extra = ".root";
|
||||
@ -301,7 +304,6 @@ dlz_destroy(void *dbdata) {
|
||||
free(state);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* See if we handle a given zone
|
||||
*/
|
||||
@ -313,6 +315,7 @@ dlz_findzonedb(void *dbdata, const char *name,
|
||||
struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
|
||||
isc_sockaddr_t *src;
|
||||
char addrbuf[100];
|
||||
char absolute[1024];
|
||||
|
||||
strcpy(addrbuf, "unknown");
|
||||
if (methods != NULL &&
|
||||
@ -324,7 +327,7 @@ dlz_findzonedb(void *dbdata, const char *name,
|
||||
fmt_address(src, addrbuf, sizeof(addrbuf));
|
||||
}
|
||||
state->log(ISC_LOG_INFO,
|
||||
"dlz_example: findzonedb connection from: %s\n", addrbuf);
|
||||
"dlz_example: findzonedb connection from: %s", addrbuf);
|
||||
|
||||
state->log(ISC_LOG_INFO,
|
||||
"dlz_example: dlz_findzonedb called with name '%s' "
|
||||
@ -351,6 +354,10 @@ dlz_findzonedb(void *dbdata, const char *name,
|
||||
if (strcasecmp(state->zone_name, name) == 0)
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
snprintf(absolute, sizeof(absolute), "%s.", name);
|
||||
if (strcasecmp(state->zone_name, absolute) == 0)
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
return (ISC_R_NOTFOUND);
|
||||
}
|
||||
|
||||
@ -372,6 +379,7 @@ dlz_lookup(const char *zone, const char *name, void *dbdata,
|
||||
isc_result_t result;
|
||||
struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
|
||||
isc_boolean_t found = ISC_FALSE;
|
||||
void *dbversion = NULL;
|
||||
isc_sockaddr_t *src;
|
||||
char full_name[256];
|
||||
char buf[512];
|
||||
@ -388,6 +396,30 @@ dlz_lookup(const char *zone, const char *name, void *dbdata,
|
||||
} else
|
||||
snprintf(full_name, 255, "%s.%s", name, state->zone_name);
|
||||
|
||||
/*
|
||||
* If we need to know the database version (as set in
|
||||
* the 'newversion' dlz function) we can pick it up from the
|
||||
* clientinfo.
|
||||
*
|
||||
* This allows a lookup to query the correct version of the DNS
|
||||
* data, if the DLZ can differentiate between versions.
|
||||
*
|
||||
* For example, if a new database transaction is created by
|
||||
* 'newversion', the lookup should query within the same
|
||||
* transaction scope if it can.
|
||||
*
|
||||
* If the DLZ only operates on 'live' data, then version
|
||||
* wouldn't necessarily be needed.
|
||||
*/
|
||||
if (clientinfo != NULL &&
|
||||
clientinfo->version >= DNS_CLIENTINFO_VERSION) {
|
||||
dbversion = clientinfo->dbversion;
|
||||
if (dbversion != NULL && *(isc_boolean_t *)dbversion)
|
||||
state->log(ISC_LOG_INFO,
|
||||
"dlz_example: lookup against live "
|
||||
"transaction\n");
|
||||
}
|
||||
|
||||
if (strcmp(name, "source-addr") == 0) {
|
||||
strcpy(buf, "unknown");
|
||||
if (methods != NULL &&
|
||||
@ -401,7 +433,7 @@ dlz_lookup(const char *zone, const char *name, void *dbdata,
|
||||
}
|
||||
|
||||
state->log(ISC_LOG_INFO,
|
||||
"dlz_example: lookup connection from: %s\n", buf);
|
||||
"dlz_example: lookup connection from: %s", buf);
|
||||
|
||||
found = ISC_TRUE;
|
||||
result = state->putrr(lookup, "TXT", 0, buf);
|
||||
@ -619,6 +651,7 @@ modrdataset(struct dlz_example_data *state, const char *name,
|
||||
const char *rdatastr, struct record *list)
|
||||
{
|
||||
char *full_name, *dclass, *type, *data, *ttlstr, *buf;
|
||||
char absolute[1024];
|
||||
isc_result_t result;
|
||||
#if defined(WIN32) || defined(_REENTRANT)
|
||||
char *saveptr = NULL;
|
||||
@ -656,6 +689,11 @@ modrdataset(struct dlz_example_data *state, const char *name,
|
||||
if (data == NULL)
|
||||
goto error;
|
||||
|
||||
if (name[strlen(name) - 1] != '.') {
|
||||
snprintf(absolute, sizeof(absolute), "%s.", name);
|
||||
name = absolute;
|
||||
}
|
||||
|
||||
result = add_name(state, list, name, type,
|
||||
strtoul(ttlstr, NULL, 10), data);
|
||||
free(buf);
|
||||
@ -699,7 +737,6 @@ dlz_subrdataset(const char *name, const char *rdatastr,
|
||||
return (modrdataset(state, name, rdatastr, &state->deletes[0]));
|
||||
}
|
||||
|
||||
|
||||
isc_result_t
|
||||
dlz_delrdataset(const char *name, const char *type,
|
||||
void *dbdata, void *version)
|
||||
|
@ -14,8 +14,6 @@
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: named.conf,v 1.2 2011/10/20 22:01:48 each Exp $ */
|
||||
|
||||
/*
|
||||
* This is a sample named.conf file that uses the DLZ module defined in
|
||||
* dlz_example.c. It sets up a zone 'example.nil' which can accept DDNS
|
||||
@ -45,6 +43,21 @@ options {
|
||||
recursion no;
|
||||
};
|
||||
|
||||
/*
|
||||
* To test dynamic updates, create a DDNS key:
|
||||
*
|
||||
* ddns-confgen -q -z example.nil > ddns.key
|
||||
*
|
||||
* Then uncomment the following line:
|
||||
*
|
||||
* include "ddns.key";
|
||||
*
|
||||
* Use "nsupdate -k ddns.key" when sending updates. (NOTE: This driver does
|
||||
* not check the key that's used: as long as the update is signed by a key
|
||||
* known to named, the update will be accepted. Only updates to names
|
||||
* that begin with "deny." are rejected.)
|
||||
*/
|
||||
|
||||
dlz "example" {
|
||||
database "dlopen ./dlz_example.so example.nil";
|
||||
};
|
||||
|
@ -80,6 +80,12 @@ typedef uint32_t dns_ttl_t;
|
||||
|
||||
/* other useful definitions */
|
||||
#define UNUSED(x) (void)(x)
|
||||
#define DE_CONST(konst, var) \
|
||||
do { \
|
||||
union { const void *k; void *v; } _u; \
|
||||
_u.k = konst; \
|
||||
var = _u.v; \
|
||||
} while (0)
|
||||
|
||||
/* opaque structures */
|
||||
typedef void *dns_sdlzlookup_t;
|
||||
@ -105,22 +111,26 @@ typedef struct isc_sockaddr {
|
||||
void * link;
|
||||
} isc_sockaddr_t;
|
||||
|
||||
#define DNS_CLIENTINFO_VERSION 1
|
||||
#define DNS_CLIENTINFO_VERSION 2
|
||||
typedef struct dns_clientinfo {
|
||||
uint16_t version;
|
||||
void *data;
|
||||
void *dbversion;
|
||||
} dns_clientinfo_t;
|
||||
|
||||
typedef isc_result_t (*dns_clientinfo_sourceip_t)(dns_clientinfo_t *client,
|
||||
isc_sockaddr_t **addrp);
|
||||
|
||||
#define DNS_CLIENTINFOMETHODS_VERSION 1
|
||||
#define DNS_CLIENTINFOMETHODS_AGE 0
|
||||
typedef isc_result_t (*dns_clientinfo_version_t)(dns_clientinfo_t *client,
|
||||
void **addrp);
|
||||
|
||||
#define DNS_CLIENTINFOMETHODS_VERSION 2
|
||||
#define DNS_CLIENTINFOMETHODS_AGE 1
|
||||
typedef struct dns_clientinfomethods {
|
||||
uint16_t version;
|
||||
uint16_t age;
|
||||
dns_clientinfo_sourceip_t sourceip;
|
||||
dns_clientinfo_version_t dbversion;
|
||||
} dns_clientinfomethods_t;
|
||||
#endif /* DLZ_DLOPEN_VERSION > 1 */
|
||||
|
||||
|
@ -25,12 +25,14 @@
|
||||
#define dlz_mutex_t pthread_mutex_t
|
||||
#define dlz_mutex_init pthread_mutex_init
|
||||
#define dlz_mutex_destroy pthread_mutex_destroy
|
||||
#define dlz_mutex_lock pthread_mutex_lock
|
||||
#define dlz_mutex_trylock pthread_mutex_trylock
|
||||
#define dlz_mutex_unlock pthread_mutex_unlock
|
||||
#else /* !PTHREADS */
|
||||
#define dlz_mutex_t void
|
||||
#define dlz_mutex_init(a, b) (0)
|
||||
#define dlz_mutex_destroy(a) (0)
|
||||
#define dlz_mutex_lock(a) (0)
|
||||
#define dlz_mutex_trylock(a) (0)
|
||||
#define dlz_mutex_unlock(a) (0)
|
||||
#endif
|
||||
|
21
contrib/dlz/modules/mysqldyn/Makefile
Normal file
21
contrib/dlz/modules/mysqldyn/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
prefix = /usr
|
||||
libdir = $(prefix)/lib/bind9
|
||||
|
||||
CFLAGS=-fPIC -g -I../include
|
||||
MYSQL_LIBS=-lmysqlclient
|
||||
|
||||
all: dlz_mysqldyn_mod.so
|
||||
|
||||
dlz_dbi.o: ../common/dlz_dbi.c
|
||||
$(CC) $(CFLAGS) -c ../common/dlz_dbi.c
|
||||
|
||||
dlz_mysqldyn_mod.so: dlz_mysqldyn_mod.c dlz_dbi.o
|
||||
$(CC) $(CFLAGS) -shared -o dlz_mysqldyn_mod.so \
|
||||
dlz_mysqldyn_mod.c dlz_dbi.o $(MYSQL_LIBS)
|
||||
|
||||
clean:
|
||||
rm -f dlz_mysqldyn_mod.so *.o
|
||||
|
||||
install: dlz_mysqldyn_mod.so
|
||||
mkdir -p $(DESTDIR)$(libdir)
|
||||
install dlz_mysqldyn_mod.so $(DESTDIR)$(libdir)
|
60
contrib/dlz/modules/mysqldyn/README
Normal file
60
contrib/dlz/modules/mysqldyn/README
Normal file
@ -0,0 +1,60 @@
|
||||
BIND 9 DLZ MySQL module with support for dynamic DNS (DDNS)
|
||||
|
||||
Adapted from code contributed by Marty Lee, Maui Systems Ltd.
|
||||
|
||||
This is a dynamically loadable zone (DLZ) plugin that uses a fixed-
|
||||
schema MySQL database for back-end storage. It allows zone data
|
||||
to be updated via dynamic DNS updates, and sends DNS NOTIFY packets
|
||||
to other name servers when appropriate.
|
||||
|
||||
The database for this module uses the following schema:
|
||||
|
||||
CREATE TABLE `Zones` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`domain` varchar(128) NOT NULL DEFAULT '',
|
||||
`host` varchar(128) NOT NULL DEFAULT '',
|
||||
`admin` varchar(128) NOT NULL DEFAULT '',
|
||||
`serial` int(11) NOT NULL DEFAULT '1',
|
||||
`expire` int(11) NOT NULL DEFAULT '86400',
|
||||
`refresh` int(11) NOT NULL DEFAULT '86400',
|
||||
`retry` int(11) NOT NULL DEFAULT '86400',
|
||||
`minimum` int(11) NOT NULL DEFAULT '86400',
|
||||
`ttl` int(11) NOT NULL DEFAULT '86400',
|
||||
`writeable` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `domain_idx` (`domain`)
|
||||
);
|
||||
|
||||
CREATE TABLE `ZoneData` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`zone_id` int(11) NOT NULL,
|
||||
`name` varchar(128) NOT NULL DEFAULT '',
|
||||
`type` varchar(16) NOT NULL DEFAULT '',
|
||||
`ttl` int(11) NOT NULL DEFAULT '86400',
|
||||
`data` varchar(128) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `zone_idx` (`zone_id`),
|
||||
KEY `name_idx` (`zone_id`, `name`),
|
||||
KEY `type_idx` (`type`)
|
||||
);
|
||||
|
||||
'Zones' contains information about specific zones:
|
||||
- domain: the zone name
|
||||
- admin: the zone administrator
|
||||
- serial, expire, reresh, retry, minimum: values in the SOA record
|
||||
- ttl: default zone TTL
|
||||
- writeable: set to true if the zone can be updated via DDNS
|
||||
|
||||
'ZoneData' contains the individual records within the zone:
|
||||
- zone_id: the 'id' from the corresponding record in Zones
|
||||
- name: domain name, relative to the zone apex. (Data at the zone
|
||||
apex itself may use a blank name or "@".)
|
||||
- type: the RR type, expressed as text
|
||||
- ttl: the record's TTL
|
||||
- data: the records rdata, expressed as text.
|
||||
|
||||
To configure this module in named.conf:
|
||||
|
||||
dlz "mysqldlz" {
|
||||
database "dlopen <path to>/dlz_mysqldyn_mod.so <dbname> [dbhost [dbuser [dbpass]]]";
|
||||
};
|
1723
contrib/dlz/modules/mysqldyn/dlz_mysqldyn_mod.c
Normal file
1723
contrib/dlz/modules/mysqldyn/dlz_mysqldyn_mod.c
Normal file
File diff suppressed because it is too large
Load Diff
11
contrib/dlz/modules/mysqldyn/testing/README
Normal file
11
contrib/dlz/modules/mysqldyn/testing/README
Normal file
@ -0,0 +1,11 @@
|
||||
These files were used for testing on Ubuntu Linux using MySQL
|
||||
|
||||
To set up a test server:
|
||||
- Install MySQL: sudo apt-get install mysql-server
|
||||
- Run "mysql --user=USER --password=PASSWORD < dlz.schema" to set up database
|
||||
- Run "mysql --user=USER --password=PASSWORD < dlz.data" to populate it
|
||||
- Update named.conf with correct USER and PASSWORD
|
||||
- Generate a TSIG key: "ddns-confgen -qz example.com"
|
||||
|
||||
To query the database, use "dig -p 5300 @localhost"
|
||||
To send dynamic updates, use "nsupdate -p 5300 -k ddns.key"
|
18
contrib/dlz/modules/mysqldyn/testing/dlz.data
Normal file
18
contrib/dlz/modules/mysqldyn/testing/dlz.data
Normal file
@ -0,0 +1,18 @@
|
||||
use BindDB;
|
||||
insert into `Zones`
|
||||
( `id`, `domain`, `host`, `admin`, `serial`, `expire`,
|
||||
`refresh`, `retry`, `minimum`, `ttl`, `writeable`) VALUES
|
||||
(1, 'example.com', '@', 'info', 2014040100, 10800,
|
||||
7200, 604800, 86400, 86400, 1);
|
||||
|
||||
insert into `ZoneData`
|
||||
(`id`, `zone_id`, `name`, `type`, `data`) VALUES
|
||||
('', 1, '@', 'NS', 'ns1.example.com.'),
|
||||
('', 1, '@', 'NS', 'ns2.example.com.'),
|
||||
('', 1, '@', 'MX', '10 mail.example.com.'),
|
||||
('', 1, '@', 'A', '192.168.0.2'),
|
||||
('', 1, '@', 'TXT', '"v=spf1 ip:192.168.0.3 ~all"'),
|
||||
('', 1, 'www', 'CNAME', 'example.com.'),
|
||||
('', 1, 'mail', 'A', '192.168.0.3'),
|
||||
('', 1, 'ns1', 'A', '192.168.1.111'),
|
||||
('', 1, 'ns2', 'A', '192.168.1.222');
|
31
contrib/dlz/modules/mysqldyn/testing/dlz.schema
Normal file
31
contrib/dlz/modules/mysqldyn/testing/dlz.schema
Normal file
@ -0,0 +1,31 @@
|
||||
CREATE DATABASE `BindDB` DEFAULT CHARACTER SET latin1;
|
||||
USE `BindDB`;
|
||||
|
||||
CREATE TABLE `ZoneData` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`zone_id` int(11) NOT NULL,
|
||||
`name` varchar(128) NOT NULL DEFAULT '',
|
||||
`type` varchar(16) NOT NULL DEFAULT '',
|
||||
`data` varchar(128) NOT NULL DEFAULT '',
|
||||
`ttl` int(11) NOT NULL DEFAULT '86400',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `zone_idx` (`zone_id`),
|
||||
KEY `name_idx` (`zone_id`, `name`),
|
||||
KEY `type_idx` (`type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
|
||||
CREATE TABLE `Zones` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`domain` varchar(128) NOT NULL DEFAULT '',
|
||||
`host` varchar(128) NOT NULL DEFAULT '',
|
||||
`admin` varchar(128) NOT NULL DEFAULT '',
|
||||
`serial` int(11) NOT NULL DEFAULT '1',
|
||||
`expire` int(11) NOT NULL DEFAULT '86400',
|
||||
`refresh` int(11) NOT NULL DEFAULT '86400',
|
||||
`retry` int(11) NOT NULL DEFAULT '86400',
|
||||
`minimum` int(11) NOT NULL DEFAULT '86400',
|
||||
`ttl` int(11) NOT NULL DEFAULT '86400',
|
||||
`writeable` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `domain_idx` (`domain`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
42
contrib/dlz/modules/mysqldyn/testing/named.conf
Normal file
42
contrib/dlz/modules/mysqldyn/testing/named.conf
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
controls { };
|
||||
|
||||
options {
|
||||
directory ".";
|
||||
port 5300;
|
||||
pid-file "named.pid";
|
||||
session-keyfile "session.key";
|
||||
listen-on { any; };
|
||||
listen-on-v6 { none; };
|
||||
recursion no;
|
||||
};
|
||||
|
||||
include "ddns.key";
|
||||
|
||||
key rndc_key {
|
||||
secret "1234abcd8765";
|
||||
algorithm hmac-md5;
|
||||
};
|
||||
|
||||
controls {
|
||||
inet 127.0.0.1 port 9953 allow { any; } keys { rndc_key; };
|
||||
};
|
||||
|
||||
dlz "test" {
|
||||
database "dlopen ../dlz_mysqldyn_mod.so BindDB localhost root password";
|
||||
};
|
@ -14,8 +14,6 @@
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: clientinfo.c,v 1.3 2011/10/11 00:25:12 marka Exp $ */
|
||||
|
||||
/*! \file */
|
||||
|
||||
#include "config.h"
|
||||
@ -32,7 +30,8 @@ dns_clientinfomethods_init(dns_clientinfomethods_t *methods,
|
||||
}
|
||||
|
||||
void
|
||||
dns_clientinfo_init(dns_clientinfo_t *ci, void *data) {
|
||||
dns_clientinfo_init(dns_clientinfo_t *ci, void *data, void *versionp) {
|
||||
ci->version = DNS_CLIENTINFO_VERSION;
|
||||
ci->data = data;
|
||||
ci->dbversion = versionp;
|
||||
}
|
||||
|
@ -52,10 +52,11 @@ ISC_LANG_BEGINDECLS
|
||||
***** Types
|
||||
*****/
|
||||
|
||||
#define DNS_CLIENTINFO_VERSION 1
|
||||
#define DNS_CLIENTINFO_VERSION 2
|
||||
typedef struct dns_clientinfo {
|
||||
isc_uint16_t version;
|
||||
void *data;
|
||||
void *dbversion;
|
||||
} dns_clientinfo_t;
|
||||
|
||||
typedef isc_result_t (*dns_clientinfo_sourceip_t)(dns_clientinfo_t *client,
|
||||
@ -78,7 +79,7 @@ dns_clientinfomethods_init(dns_clientinfomethods_t *methods,
|
||||
dns_clientinfo_sourceip_t sourceip);
|
||||
|
||||
void
|
||||
dns_clientinfo_init(dns_clientinfo_t *ci, void *data);
|
||||
dns_clientinfo_init(dns_clientinfo_t *ci, void *data, void *versionp);
|
||||
|
||||
ISC_LANG_ENDDECLS
|
||||
|
||||
|
@ -426,8 +426,7 @@ newversion(dns_db_t *db, dns_dbversion_t **versionp) {
|
||||
}
|
||||
|
||||
static void
|
||||
attachversion(dns_db_t *db, dns_dbversion_t *source,
|
||||
dns_dbversion_t **targetp)
|
||||
attachversion(dns_db_t *db, dns_dbversion_t *source, dns_dbversion_t **targetp)
|
||||
{
|
||||
dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
|
||||
|
||||
@ -854,7 +853,9 @@ findext(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
|
||||
|
||||
REQUIRE(VALID_SDLZDB(sdlz));
|
||||
REQUIRE(nodep == NULL || *nodep == NULL);
|
||||
REQUIRE(version == NULL || version == (void*)&sdlz->dummy_version);
|
||||
REQUIRE(version == NULL ||
|
||||
version == (void*)&sdlz->dummy_version ||
|
||||
version == sdlz->future_version);
|
||||
|
||||
UNUSED(options);
|
||||
UNUSED(sdlz);
|
||||
|
Loading…
x
Reference in New Issue
Block a user