Commit e214e872 authored by Mark Andrews's avatar Mark Andrews

Merge branches 'rt28261' and 'rt27597' of repo.isc.org:/proj/git/prod/bind9

......@@ -798,6 +798,7 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL,
&zone);
if (result == DNS_R_PARTIALMATCH)
partial = ISC_TRUE;
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
......@@ -1028,11 +1029,14 @@ query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
/* See how many labels are in the zone's name. */
if (result == ISC_R_SUCCESS && zone != NULL)
zonelabels = dns_name_countlabels(dns_zone_getorigin(zone));
/*
* If # zone labels < # name labels, try to find an even better match
* Only try if a DLZ driver is loaded for this view
* Only try if DLZ drivers are loaded for this view
*/
if (zonelabels < namelabels && client->view->dlzdatabase != NULL) {
if (zonelabels < namelabels &&
!ISC_LIST_EMPTY(client->view->dlz_searched))
{
tresult = dns_dlzfindzone(client->view, name,
zonelabels, &tdbp);
/* If we successful, we found a better match. */
......
......@@ -1347,7 +1347,7 @@ cache_sharable(dns_view_t *originview, dns_view_t *view,
* Callback from DLZ configure when the driver sets up a writeable zone
*/
static isc_result_t
dlzconfigure_callback(dns_view_t *view, dns_zone_t *zone) {
dlzconfigure_callback(dns_view_t *view, dns_dlzdb_t *dlzdb, dns_zone_t *zone) {
dns_name_t *origin = dns_zone_getorigin(zone);
dns_rdataclass_t zclass = view->rdclass;
isc_result_t result;
......@@ -1357,8 +1357,7 @@ dlzconfigure_callback(dns_view_t *view, dns_zone_t *zone) {
return (result);
dns_zone_setstats(zone, ns_g_server->zonestats);
return (ns_zone_configure_writeable_dlz(view->dlzdatabase,
zone, zclass, origin));
return (ns_zone_configure_writeable_dlz(dlzdb, zone, zclass, origin));
}
static isc_result_t
......@@ -1569,6 +1568,7 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
const cfg_obj_t *forwarders;
const cfg_obj_t *alternates;
const cfg_obj_t *zonelist;
const cfg_obj_t *dlzlist;
const cfg_obj_t *dlz;
unsigned int dlzargc;
char **dlzargv;
......@@ -1759,18 +1759,27 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
/*
* Create Dynamically Loadable Zone driver.
*/
dlz = NULL;
dlzlist = NULL;
if (voptions != NULL)
(void)cfg_map_get(voptions, "dlz", &dlz);
(void)cfg_map_get(voptions, "dlz", &dlzlist);
else
(void)cfg_map_get(config, "dlz", &dlz);
(void)cfg_map_get(config, "dlz", &dlzlist);
obj = NULL;
if (dlz != NULL) {
(void)cfg_map_get(cfg_tuple_get(dlz, "options"),
"database", &obj);
for (element = cfg_list_first(dlzlist);
element != NULL;
element = cfg_list_next(element))
{
obj = NULL;
const cfg_obj_t *dlzopts;
dlz = cfg_listelt_value(element);
dlzopts = cfg_tuple_get(dlz, "options");
(void)cfg_map_get(dlzopts, "database", &obj);
if (obj != NULL) {
dns_dlzdb_t *dlzdb = NULL;
const cfg_obj_t *name, *search = NULL;
char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
if (s == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
......@@ -1782,10 +1791,10 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
goto cleanup;
}
obj = cfg_tuple_get(dlz, "name");
result = dns_dlzcreate(mctx, cfg_obj_asstring(obj),
name = cfg_tuple_get(dlz, "name");
result = dns_dlzcreate(mctx, cfg_obj_asstring(name),
dlzargv[0], dlzargc, dlzargv,
&view->dlzdatabase);
&dlzdb);
isc_mem_free(mctx, s);
isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv));
if (result != ISC_R_SUCCESS)
......@@ -1795,9 +1804,19 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
* If the dlz backend supports configuration,
* then call its configure method now.
*/
result = dns_dlzconfigure(view, dlzconfigure_callback);
result = dns_dlzconfigure(view, dlzdb,
dlzconfigure_callback);
if (result != ISC_R_SUCCESS)
goto cleanup;
(void)cfg_map_get(dlzopts, "search", &search);
if (search == NULL || cfg_obj_asboolean(search))
ISC_LIST_APPEND(view->dlz_searched,
dlzdb, link);
else
ISC_LIST_APPEND(view->dlz_unsearched,
dlzdb, link);
}
}
......@@ -3463,7 +3482,8 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
signing = NULL;
if ((strcasecmp(ztypestr, "master") == 0 ||
strcasecmp(ztypestr, "slave") == 0) &&
cfg_map_get(zoptions, "inline-signing", &signing) == ISC_R_SUCCESS &&
cfg_map_get(zoptions, "inline-signing", &signing)
== ISC_R_SUCCESS &&
cfg_obj_asboolean(signing))
{
dns_zone_getraw(zone, &raw);
......
......@@ -449,7 +449,9 @@ dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit,
* Called on startup to configure any writeable zones
*/
static isc_result_t
dlopen_dlz_configure(dns_view_t *view, void *driverarg, void *dbdata) {
dlopen_dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb,
void *driverarg, void *dbdata)
{
dlopen_data_t *cd = (dlopen_data_t *) dbdata;
isc_result_t result;
......@@ -460,7 +462,7 @@ dlopen_dlz_configure(dns_view_t *view, void *driverarg, void *dbdata) {
MAYBE_LOCK(cd);
cd->in_configure = ISC_TRUE;
result = cd->dlz_configure(view, cd->dbdata);
result = cd->dlz_configure(view, dlzdb, cd->dbdata);
cd->in_configure = ISC_FALSE;
MAYBE_UNLOCK(cd);
......
......@@ -807,7 +807,8 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
* Normal zone table does not have a match.
* Try the DLZ database
*/
if (client->view->dlzdatabase != NULL) {
// Temporary: only searching the first DLZ database
if (! ISC_LIST_EMPTY(client->view->dlz_searched)) {
result = dns_dlzallowzonexfr(client->view,
question_name,
&client->peeraddr,
......
......@@ -801,10 +801,11 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
isc_sockaddr_t *addrs;
dns_name_t **keynames;
isc_uint32_t count;
char *cpval;
unsigned int dbargc;
char **dbargv;
static char default_dbtype[] = "rbt";
static char dlz_dbtype[] = "dlz";
char *cpval = default_dbtype;
isc_mem_t *mctx = dns_zone_getmctx(zone);
dns_dialuptype_t dialup = dns_dialuptype_no;
dns_zonetype_t ztype;
......@@ -867,12 +868,28 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
result = cfg_map_get(zoptions, "database", &obj);
if (result == ISC_R_SUCCESS)
cpval = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
else
cpval = default_dbtype;
if (cpval == NULL)
return(ISC_R_NOMEMORY);
obj = NULL;
result = cfg_map_get(zoptions, "dlz", &obj);
if (result == ISC_R_SUCCESS) {
const char *dlzname = cfg_obj_asstring(obj);
size_t len;
if (cpval != default_dbtype) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"zone '%s': both 'database' and 'dlz' "
"specified", zname);
return (ISC_R_FAILURE);
}
len = strlen(dlzname) + 5;
cpval = isc_mem_allocate(mctx, len);
snprintf(cpval, len, "dlz %s", dlzname);
}
result = strtoargv(mctx, cpval, &dbargc, &dbargv);
if (result != ISC_R_SUCCESS && cpval != default_dbtype) {
isc_mem_free(mctx, cpval);
......@@ -886,7 +903,7 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
*/
result = dns_zone_setdbtype(zone, dbargc, (const char * const *)dbargv);
isc_mem_put(mctx, dbargv, dbargc * sizeof(*dbargv));
if (cpval != default_dbtype)
if (cpval != default_dbtype && cpval != dlz_dbtype)
isc_mem_free(mctx, cpval);
if (result != ISC_R_SUCCESS)
return (result);
......
......@@ -239,14 +239,6 @@
<filename>keyboard</filename> indicates that keyboard input
should be used. This option may be specified multiple times.
</para>
<para>
The <option>-T</option> and <option>-P</option> print out a
lists of non-meta types for which the type specific presentation
format is known. <option>-T</option> prints out the list of
assigned types. <option>-P</option> prints out the list of
private types. These options may be combined. nsupdate will
exit after the lists are printed.
</para>
<para>
Other types can be entered using "TYPEXXXXX" where "XXXXX" is the
decimal value of the type with no leading zeros. The rdata,
......@@ -254,6 +246,15 @@
(&lt;backslash&gt; &lt;hash&gt; &lt;space&gt; &lt;length&gt;
&lt;space&gt; &lt;hexstring&gt;).
</para>
<para>
The <option>-T</option> and <option>-P</option> options print out
lists of non-meta types for which the type-specific presentation
formats are known. <option>-T</option> prints out the list of
IANA-assigned types. <option>-P</option> prints out the list of
private types specific to <command>named<command>. These options
may be combined. <command>nsupdate</command> will exit after the
lists are printed.
</para>
</refsect1>
<refsect1>
......
dlz one {
database "one";
};
dlz two {
database "two";
search no;
};
zone master {
type master;
database "none";
dlz two;
};
......@@ -51,5 +51,11 @@ $CHECKCONF dnssec.3 2>&1 | grep '.*' && ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I: checking named-checkconf DLZ warnings"
ret=0
$CHECKCONF dlz-bad.conf 2>&1 | grep "'dlz' and 'database'" > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:exit status: $status"
exit $status
......@@ -7,3 +7,4 @@ rm -f ns1/update.txt
rm -f */named.memstats
rm -f ns1/ddns.key
rm -f random.data
rm -f dig.out*
......@@ -463,7 +463,7 @@ dlz_closeversion(const char *zone, isc_boolean_t commit,
* Configure a writeable zone
*/
isc_result_t
dlz_configure(dns_view_t *view, void *dbdata) {
dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb, void *dbdata) {
struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
isc_result_t result;
......@@ -475,7 +475,7 @@ dlz_configure(dns_view_t *view, void *dbdata) {
return (ISC_R_FAILURE);
}
result = state->writeable_zone(view, state->zone_name);
result = state->writeable_zone(view, dlzdb, state->zone_name);
if (result != ISC_R_SUCCESS) {
state->log(ISC_LOG_ERROR,
"dlz_example: failed to configure zone %s",
......
......@@ -70,4 +70,21 @@ done
[ "$ret" -eq 0 ] || echo "I:failed"
status=`expr $status + $ret`
echo "I:testing multiple DLZ drivers"
test_update testdc1.alternate.nil. A "86400 A 10.53.0.10" "10.53.0.10" || ret=1
status=`expr $status + $ret`
echo "I:testing AXFR from DLZ drivers"
$DIG $DIGOPTS +noall +answer axfr example.nil > dig.out.ns1.1
n=`cat dig.out.ns1.1 | wc -l`
[ "$n" -eq 7 ] || ret=1
$DIG $DIGOPTS +noall +answer axfr alternate.nil > dig.out.ns1.2
n=`cat dig.out.ns1.2 | wc -l`
[ "$n" -eq 5 ] || ret=1
status=`expr $status + $ret`
echo "I:testing unsearched DLZ driver"
$DIG $DIGOPTS +noall +answer ns other.nil > dig.out.ns1.3
cat dig.out.ns1.3
exit $status
......@@ -466,7 +466,8 @@ dlz_configure(dns_view_t *view, void *dbdata) {
return (ISC_R_FAILURE);
}
result = state->writeable_zone(view, state->zone_name);
result = state->writeable_zone(view, view->dlzdatabase,
state->zone_name);
if (result != ISC_R_SUCCESS) {
state->log(ISC_LOG_ERROR,
"dlz_example: failed to configure zone %s",
......
......@@ -1280,6 +1280,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
isc_buffer_t b;
isc_boolean_t root = ISC_FALSE;
const cfg_listelt_t *element;
isc_boolean_t dlz;
static optionstable options[] = {
{ "allow-query", MASTERZONE | SLAVEZONE | STUBZONE | REDIRECTZONE |
......@@ -1701,11 +1702,24 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
* require file clauses.
*/
obj = NULL;
dlz = ISC_FALSE;
tresult = cfg_map_get(zoptions, "dlz", &obj);
if (tresult == ISC_R_SUCCESS)
dlz = ISC_TRUE;
obj = NULL;
tresult = cfg_map_get(zoptions, "database", &obj);
if (tresult == ISC_R_NOTFOUND ||
if (dlz && tresult == ISC_R_SUCCESS) {
cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
"zone '%s': cannot specify both 'dlz' "
"and 'database'", znamestr);
result = ISC_R_FAILURE;
} else if (!dlz &&
(tresult == ISC_R_NOTFOUND ||
(tresult == ISC_R_SUCCESS &&
(strcmp("rbt", cfg_obj_asstring(obj)) == 0 ||
strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) {
strcmp("rbt64", cfg_obj_asstring(obj)) == 0))))
{
obj = NULL;
tresult = cfg_map_get(zoptions, "file", &obj);
if (tresult != ISC_R_SUCCESS &&
......
......@@ -60,10 +60,11 @@
#include <config.h>
#include <dns/db.h>
#include <dns/dlz.h>
#include <dns/fixedname.h>
#include <dns/log.h>
#include <dns/master.h>
#include <dns/dlz.h>
#include <dns/ssu.h>
#include <dns/zone.h>
......@@ -113,26 +114,41 @@ isc_result_t
dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name,
isc_sockaddr_t *clientaddr, dns_db_t **dbp)
{
isc_result_t result;
isc_result_t result = ISC_R_NOTFOUND;
dns_dlzallowzonexfr_t allowzonexfr;
dns_dlzdb_t *dlzdatabase;
dns_dlzdb_t *dlzdb;
/*
* Performs checks to make sure data is as we expect it to be.
*/
REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
REQUIRE(name != NULL);
REQUIRE(dbp != NULL && *dbp == NULL);
/* ask driver if the zone is supported */
dlzdatabase = view->dlzdatabase;
allowzonexfr = dlzdatabase->implementation->methods->allowzonexfr;
result = (*allowzonexfr)(dlzdatabase->implementation->driverarg,
dlzdatabase->dbdata, dlzdatabase->mctx,
view->rdclass, name, clientaddr, dbp);
/*
* Find a driver in which the zone exists and transfer is supported
*/
for (dlzdb = ISC_LIST_HEAD(view->dlz_searched);
dlzdb != NULL;
dlzdb = ISC_LIST_NEXT(dlzdb, link))
{
REQUIRE(DNS_DLZ_VALID(dlzdb));
allowzonexfr = dlzdb->implementation->methods->allowzonexfr;
result = (*allowzonexfr)(dlzdb->implementation->driverarg,
dlzdb->dbdata, dlzdb->mctx,
view->rdclass, name, clientaddr, dbp);
/*
* if ISC_R_NOPERM, we found the right database but
* the zone may not transfer.
*/
if (result == ISC_R_SUCCESS || result == ISC_R_NOPERM)
return (result);
}
if (result == ISC_R_NOTIMPLEMENTED)
return (ISC_R_NOTFOUND);
result = ISC_R_NOTFOUND;
return (result);
}
......@@ -191,6 +207,9 @@ dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername,
(*dbp)->implementation = impinfo;
if (dlzname != NULL)
(*dbp)->dlzname = isc_mem_strdup(mctx, dlzname);
/* Create a new database using implementation 'drivername'. */
result = ((impinfo->methods->create)(mctx, dlzname, argc, argv,
impinfo->driverarg,
......@@ -238,9 +257,12 @@ dns_dlzdestroy(dns_dlzdb_t **dbp) {
}
#endif
/* call the drivers destroy method */
if ((*dbp) != NULL) {
mctx = (*dbp)->mctx;
if ((*dbp)->dlzname != NULL)
isc_mem_free(mctx, (*dbp)->dlzname);
destroy = (*dbp)->implementation->methods->destroy;
(*destroy)((*dbp)->implementation->driverarg,(*dbp)->dbdata);
/* return memory */
......@@ -253,8 +275,8 @@ dns_dlzdestroy(dns_dlzdb_t **dbp) {
isc_result_t
dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels,
dns_db_t **dbp)
dns_dlzfindzone(dns_view_t *view, dns_name_t *name,
unsigned int minlabels, dns_db_t **dbp)
{
dns_fixedname_t fname;
dns_name_t *zonename;
......@@ -262,12 +284,13 @@ dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels,
unsigned int i;
isc_result_t result;
dns_dlzfindzone_t findzone;
dns_dlzdb_t *dlzdatabase;
dns_dlzdb_t *dlzdb;
dns_db_t *db, *best = NULL;
/*
* Performs checks to make sure data is as we expect it to be.
*/
REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
REQUIRE(view != NULL);
REQUIRE(name != NULL);
REQUIRE(dbp != NULL && *dbp == NULL);
......@@ -278,33 +301,53 @@ dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels,
/* count the number of labels in the name */
namelabels = dns_name_countlabels(name);
/*
* loop through starting with the longest domain name and
* trying shorter names portions of the name until we find a
* match, have an error, or are below the 'minlabels'
* threshold. minlabels is 0, if the standard database didn't
* have a zone name match. Otherwise minlabels is the number
* of labels in that name. We need to beat that for a
* "better" match for the DLZ database to be authoritative
* instead of the standard database.
*/
for (i = namelabels; i > minlabels && i > 1; i--) {
if (i == namelabels) {
result = dns_name_copy(name, zonename, NULL);
if (result != ISC_R_SUCCESS)
return (result);
} else
dns_name_split(name, i, NULL, zonename);
/* ask SDLZ driver if the zone is supported */
dlzdatabase = view->dlzdatabase;
findzone = dlzdatabase->implementation->methods->findzone;
result = (*findzone)(dlzdatabase->implementation->driverarg,
dlzdatabase->dbdata, dlzdatabase->mctx,
view->rdclass, zonename, dbp);
if (result != ISC_R_NOTFOUND)
return (result);
for (dlzdb = ISC_LIST_HEAD(view->dlz_searched);
dlzdb != NULL;
dlzdb = ISC_LIST_NEXT(dlzdb, link))
{
REQUIRE(DNS_DLZ_VALID(dlzdb));
/*
* loop through starting with the longest domain name and
* trying shorter names portions of the name until we find a
* match, have an error, or are below the 'minlabels'
* threshold. minlabels is 0, if neither the standard
* database nor any previous DLZ database had a zone name
* match. Otherwise minlabels is the number of labels
* in that name. We need to beat that for a "better"
* match for this DLZ database to be authoritative.
*/
for (i = namelabels; i > minlabels && i > 1; i--) {
if (i == namelabels) {
result = dns_name_copy(name, zonename, NULL);
if (result != ISC_R_SUCCESS)
return (result);
} else
dns_name_split(name, i, NULL, zonename);
/* ask SDLZ driver if the zone is supported */
db = NULL;
findzone = dlzdb->implementation->methods->findzone;
result = (*findzone)(dlzdb->implementation->driverarg,
dlzdb->dbdata, dlzdb->mctx,
view->rdclass, zonename, &db);
if (result != ISC_R_NOTFOUND) {
if (best != NULL)
dns_db_detach(&best);
dns_db_attach(db, &best);
minlabels = i;
} else if (db != NULL)
dns_db_detach(&db);
}
}
if (best != NULL) {
dns_db_attach(best, dbp);
dns_db_detach(&best);
return (ISC_R_SUCCESS);
}
return (ISC_R_NOTFOUND);
}
......@@ -528,20 +571,19 @@ dns_dlzunregister(dns_dlzimplementation_t **dlzimp) {
* specific functionality on the zone
*/
isc_result_t
dns_dlz_writeablezone(dns_view_t *view, const char *zone_name) {
dns_dlz_writeablezone(dns_view_t *view, dns_dlzdb_t *dlzdb,
const char *zone_name)
{
dns_zone_t *zone = NULL;
dns_zone_t *dupzone = NULL;
isc_result_t result;
isc_buffer_t buffer;
dns_fixedname_t fixorigin;
dns_name_t *origin;
dns_dlzdb_t *dlzdatabase;
REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
REQUIRE(DNS_DLZ_VALID(dlzdb));
dlzdatabase = view->dlzdatabase;
REQUIRE(dlzdatabase->configure_callback != NULL);
REQUIRE(dlzdb->configure_callback != NULL);
isc_buffer_init(&buffer, zone_name, strlen(zone_name));
isc_buffer_add(&buffer, strlen(zone_name));
......@@ -572,16 +614,15 @@ dns_dlz_writeablezone(dns_view_t *view, const char *zone_name) {
dns_zone_setadded(zone, ISC_TRUE);
if (dlzdatabase->ssutable == NULL) {
result = dns_ssutable_createdlz(dlzdatabase->mctx,
&dlzdatabase->ssutable,
view->dlzdatabase);
if (dlzdb->ssutable == NULL) {
result = dns_ssutable_createdlz(dlzdb->mctx,
&dlzdb->ssutable, dlzdb);
if (result != ISC_R_SUCCESS)
goto cleanup;
}
dns_zone_setssutable(zone, dlzdatabase->ssutable);
dns_zone_setssutable(zone, dlzdb->ssutable);
result = dlzdatabase->configure_callback(view, zone);
result = dlzdb->configure_callback(view, dlzdb, zone);
if (result != ISC_R_SUCCESS)
goto cleanup;
......@@ -603,34 +644,31 @@ dns_dlz_writeablezone(dns_view_t *view, const char *zone_name) {
* the backend an opportunity to configure parameters related to DLZ.
*/
isc_result_t
dns_dlzconfigure(dns_view_t *view, isc_result_t (*callback)(dns_view_t *,
dns_zone_t *))
dns_dlzconfigure(dns_view_t *view, dns_dlzdb_t *dlzdb,
dlzconfigure_callback_t callback)
{
dns_dlzimplementation_t *impl;