static isc_result_t internal_current4(isc_interfaceiter_t *iter) { struct ifreq *ifrp; struct ifreq ifreq; int family; char strbuf[ISC_STRERRORSIZE]; #if !defined(ISC_PLATFORM_HAVEIF_LADDRREQ) && defined(SIOCGLIFADDR) struct lifreq lifreq; #else char sabuf[256]; #endif int i, bits, prefixlen; REQUIRE(VALID_IFITER(iter)); if (iter->ifc.ifc_len == 0 || iter->pos == (unsigned int)iter->ifc.ifc_len) { #ifdef __linux return (linux_if_inet6_current(iter)); #else return (ISC_R_NOMORE); #endif } INSIST( iter->pos < (unsigned int) iter->ifc.ifc_len); ifrp = (struct ifreq *)((char *) iter->ifc.ifc_req + iter->pos); memset(&ifreq, 0, sizeof(ifreq)); memmove(&ifreq, ifrp, sizeof(ifreq)); family = ifreq.ifr_addr.sa_family; #if defined(ISC_PLATFORM_HAVEIPV6) if (family != AF_INET && family != AF_INET6) #else if (family != AF_INET) #endif return (ISC_R_IGNORE); memset(&iter->current, 0, sizeof(iter->current)); iter->current.af = family; INSIST(sizeof(ifreq.ifr_name) <= sizeof(iter->current.name)); memset(iter->current.name, 0, sizeof(iter->current.name)); memmove(iter->current.name, ifreq.ifr_name, sizeof(ifreq.ifr_name)); get_addr(family, &iter->current.address, (struct sockaddr *)&ifrp->ifr_addr, ifreq.ifr_name); /* * If the interface does not have a address ignore it. */ switch (family) { case AF_INET: if (iter->current.address.type.in.s_addr == htonl(INADDR_ANY)) return (ISC_R_IGNORE); break; case AF_INET6: if (memcmp(&iter->current.address.type.in6, &in6addr_any, sizeof(in6addr_any)) == 0) return (ISC_R_IGNORE); break; } /* * Get interface flags. */ iter->current.flags = 0; /* * Ignore the HP/UX warning about "integer overflow during * conversion. It comes from its own macro definition, * and is really hard to shut up. */ if (ioctl(iter->socket, SIOCGIFFLAGS, (char *) &ifreq) < 0) { isc__strerror(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "%s: getting interface flags: %s", ifreq.ifr_name, strbuf); return (ISC_R_IGNORE); } if ((ifreq.ifr_flags & IFF_UP) != 0) iter->current.flags |= INTERFACE_F_UP; #ifdef IFF_POINTOPOINT if ((ifreq.ifr_flags & IFF_POINTOPOINT) != 0) iter->current.flags |= INTERFACE_F_POINTTOPOINT; #endif if ((ifreq.ifr_flags & IFF_LOOPBACK) != 0) iter->current.flags |= INTERFACE_F_LOOPBACK; if (family == AF_INET) goto inet; #if !defined(ISC_PLATFORM_HAVEIF_LADDRREQ) && defined(SIOCGLIFADDR) memset(&lifreq, 0, sizeof(lifreq)); memmove(lifreq.lifr_name, iter->current.name, sizeof(lifreq.lifr_name)); memmove(&lifreq.lifr_addr, &iter->current.address.type.in6, sizeof(iter->current.address.type.in6)); if (ioctl(iter->socket, SIOCGLIFADDR, &lifreq) < 0) { isc__strerror(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "%s: getting interface address: %s", ifreq.ifr_name, strbuf); return (ISC_R_IGNORE); } prefixlen = lifreq.lifr_addrlen; #else isc_netaddr_format(&iter->current.address, sabuf, sizeof(sabuf)); isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_INTERFACE, ISC_LOG_INFO, isc_msgcat_get(isc_msgcat, ISC_MSGSET_IFITERIOCTL, ISC_MSG_GETIFCONFIG, "prefix length for %s is unknown " "(assume 128)"), sabuf); prefixlen = 128; #endif /* * Netmask already zeroed. */ iter->current.netmask.family = family; for (i = 0; i < 16; i++) { if (prefixlen > 8) { bits = 0; prefixlen -= 8; } else { bits = 8 - prefixlen; prefixlen = 0; } iter->current.netmask.type.in6.s6_addr[i] = (~0 << bits) & 0xff; } return (ISC_R_SUCCESS); inet: if (family != AF_INET) return (ISC_R_IGNORE); #ifdef IFF_POINTOPOINT /* * If the interface is point-to-point, get the destination address. */ if ((iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) { /* * Ignore the HP/UX warning about "integer overflow during * conversion. It comes from its own macro definition, * and is really hard to shut up. */ if (ioctl(iter->socket, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { isc__strerror(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, isc_msgcat_get(isc_msgcat, ISC_MSGSET_IFITERIOCTL, ISC_MSG_GETDESTADDR, "%s: getting " "destination address: %s"), ifreq.ifr_name, strbuf); return (ISC_R_IGNORE); } get_addr(family, &iter->current.dstaddress, (struct sockaddr *)&ifreq.ifr_dstaddr, ifreq.ifr_name); } #endif /* * Get the network mask. */ memset(&ifreq, 0, sizeof(ifreq)); memmove(&ifreq, ifrp, sizeof(ifreq)); /* * Ignore the HP/UX warning about "integer overflow during * conversion. It comes from its own macro definition, * and is really hard to shut up. */ if (ioctl(iter->socket, SIOCGIFNETMASK, (char *)&ifreq) < 0) { isc__strerror(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, isc_msgcat_get(isc_msgcat, ISC_MSGSET_IFITERIOCTL, ISC_MSG_GETNETMASK, "%s: getting netmask: %s"), ifreq.ifr_name, strbuf); return (ISC_R_IGNORE); } get_addr(family, &iter->current.netmask, (struct sockaddr *)&ifreq.ifr_addr, ifreq.ifr_name); return (ISC_R_SUCCESS); }
/* * Convert a resolv.conf file into a config structure. */ isc_result_t ns_lwresd_parseeresolvconf(isc_mem_t *mctx, cfg_parser_t *pctx, cfg_obj_t **configp) { char text[4096]; char str[16]; isc_buffer_t b; lwres_context_t *lwctx = NULL; lwres_conf_t *lwc = NULL; isc_sockaddr_t sa; isc_netaddr_t na; int i; isc_result_t result; lwres_result_t lwresult; lwctx = NULL; lwresult = lwres_context_create(&lwctx, mctx, ns__lwresd_memalloc, ns__lwresd_memfree, LWRES_CONTEXT_SERVERMODE); if (lwresult != LWRES_R_SUCCESS) { result = ISC_R_NOMEMORY; goto cleanup; } lwresult = lwres_conf_parse(lwctx, lwresd_g_resolvconffile); if (lwresult != LWRES_R_SUCCESS) { result = DNS_R_SYNTAX; goto cleanup; } lwc = lwres_conf_get(lwctx); INSIST(lwc != NULL); isc_buffer_init(&b, text, sizeof(text)); CHECK(buffer_putstr(&b, "options {\n")); /* * Build the list of forwarders. */ if (lwc->nsnext > 0) { CHECK(buffer_putstr(&b, "\tforwarders {\n")); for (i = 0; i < lwc->nsnext; i++) { CHECK(lwaddr_sockaddr_fromlwresaddr( &sa, &lwc->nameservers[i], ns_g_port)); isc_netaddr_fromsockaddr(&na, &sa); CHECK(buffer_putstr(&b, "\t\t")); CHECK(isc_netaddr_totext(&na, &b)); CHECK(buffer_putstr(&b, ";\n")); } CHECK(buffer_putstr(&b, "\t};\n")); } /* * Build the sortlist */ if (lwc->sortlistnxt > 0) { CHECK(buffer_putstr(&b, "\tsortlist {\n")); CHECK(buffer_putstr(&b, "\t\t{\n")); CHECK(buffer_putstr(&b, "\t\t\tany;\n")); CHECK(buffer_putstr(&b, "\t\t\t{\n")); for (i = 0; i < lwc->sortlistnxt; i++) { lwres_addr_t *lwaddr = &lwc->sortlist[i].addr; lwres_addr_t *lwmask = &lwc->sortlist[i].mask; unsigned int mask; CHECK(lwaddr_sockaddr_fromlwresaddr(&sa, lwmask, 0)); isc_netaddr_fromsockaddr(&na, &sa); result = isc_netaddr_masktoprefixlen(&na, &mask); if (result != ISC_R_SUCCESS) { char addrtext[ISC_NETADDR_FORMATSIZE]; isc_netaddr_format(&na, addrtext, sizeof(addrtext)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_LWRESD, ISC_LOG_ERROR, "processing sortlist: '%s' is " "not a valid netmask", addrtext); goto cleanup; } CHECK(lwaddr_sockaddr_fromlwresaddr(&sa, lwaddr, 0)); isc_netaddr_fromsockaddr(&na, &sa); CHECK(buffer_putstr(&b, "\t\t\t\t")); CHECK(isc_netaddr_totext(&na, &b)); snprintf(str, sizeof(str), "%u", mask); CHECK(buffer_putstr(&b, "/")); CHECK(buffer_putstr(&b, str)); CHECK(buffer_putstr(&b, ";\n")); } CHECK(buffer_putstr(&b, "\t\t\t};\n")); CHECK(buffer_putstr(&b, "\t\t};\n")); CHECK(buffer_putstr(&b, "\t};\n")); } CHECK(buffer_putstr(&b, "};\n\n")); CHECK(buffer_putstr(&b, "lwres {\n")); /* * Build the search path */ if (lwc->searchnxt > 0) { if (lwc->searchnxt > 0) { CHECK(buffer_putstr(&b, "\tsearch {\n")); for (i = 0; i < lwc->searchnxt; i++) { CHECK(buffer_putstr(&b, "\t\t\"")); CHECK(buffer_putstr(&b, lwc->search[i])); CHECK(buffer_putstr(&b, "\";\n")); } CHECK(buffer_putstr(&b, "\t};\n")); } } /* * Build the ndots line */ if (lwc->ndots != 1) { CHECK(buffer_putstr(&b, "\tndots ")); snprintf(str, sizeof(str), "%u", lwc->ndots); CHECK(buffer_putstr(&b, str)); CHECK(buffer_putstr(&b, ";\n")); } /* * Build the listen-on line */ if (lwc->lwnext > 0) { CHECK(buffer_putstr(&b, "\tlisten-on {\n")); for (i = 0; i < lwc->lwnext; i++) { CHECK(lwaddr_sockaddr_fromlwresaddr(&sa, &lwc->lwservers[i], 0)); isc_netaddr_fromsockaddr(&na, &sa); CHECK(buffer_putstr(&b, "\t\t")); CHECK(isc_netaddr_totext(&na, &b)); CHECK(buffer_putstr(&b, ";\n")); } CHECK(buffer_putstr(&b, "\t};\n")); } CHECK(buffer_putstr(&b, "};\n")); #if 0 printf("%.*s\n", (int)isc_buffer_usedlength(&b), (char *)isc_buffer_base(&b)); #endif lwres_conf_clear(lwctx); lwres_context_destroy(&lwctx); return (cfg_parse_buffer(pctx, &b, &cfg_type_namedconf, configp)); cleanup: if (lwctx != NULL) { lwres_conf_clear(lwctx); lwres_context_destroy(&lwctx); } return (result); }
/* * Perform an update-policy rule check against an external application * over a socket. * * This currently only supports local: for unix domain datagram sockets. * * Note that by using a datagram socket and creating a new socket each * time we avoid the need for locking and allow for parallel access to * the authorization server. */ isc_boolean_t dns_ssu_external_match(dns_name_t *identity, dns_name_t *signer, dns_name_t *name, isc_netaddr_t *tcpaddr, dns_rdatatype_t type, const dst_key_t *key, isc_mem_t *mctx) { char b_identity[DNS_NAME_FORMATSIZE]; char b_signer[DNS_NAME_FORMATSIZE]; char b_name[DNS_NAME_FORMATSIZE]; char b_addr[ISC_NETADDR_FORMATSIZE]; char b_type[DNS_RDATATYPE_FORMATSIZE]; char b_key[DST_KEY_FORMATSIZE]; isc_buffer_t *tkey_token = NULL; int fd; const char *sock_path; unsigned int req_len; isc_region_t token_region; unsigned char *data; isc_buffer_t buf; isc_uint32_t token_len = 0; isc_uint32_t reply; ssize_t ret; /* The identity contains local:/path/to/socket */ dns_name_format(identity, b_identity, sizeof(b_identity)); /* For now only local: is supported */ if (strncmp(b_identity, "local:", 6) != 0) { ssu_e_log(3, "ssu_external: invalid socket path '%s'", b_identity); return (ISC_FALSE); } sock_path = &b_identity[6]; fd = ux_socket_connect(sock_path); if (fd == -1) return (ISC_FALSE); if (key != NULL) { dst_key_format(key, b_key, sizeof(b_key)); tkey_token = dst_key_tkeytoken(key); } else b_key[0] = 0; if (tkey_token != NULL) { isc_buffer_region(tkey_token, &token_region); token_len = token_region.length; } /* Format the request elements */ if (signer != NULL) dns_name_format(signer, b_signer, sizeof(b_signer)); else b_signer[0] = 0; dns_name_format(name, b_name, sizeof(b_name)); if (tcpaddr != NULL) isc_netaddr_format(tcpaddr, b_addr, sizeof(b_addr)); else b_addr[0] = 0; dns_rdatatype_format(type, b_type, sizeof(b_type)); /* Work out how big the request will be */ req_len = sizeof(isc_uint32_t) + /* Format version */ sizeof(isc_uint32_t) + /* Length */ strlen(b_signer) + 1 + /* Signer */ strlen(b_name) + 1 + /* Name */ strlen(b_addr) + 1 + /* Address */ strlen(b_type) + 1 + /* Type */ strlen(b_key) + 1 + /* Key */ sizeof(isc_uint32_t) + /* tkey_token length */ token_len; /* tkey_token */ /* format the buffer */ data = isc_mem_allocate(mctx, req_len); if (data == NULL) { close(fd); return (ISC_FALSE); } isc_buffer_init(&buf, data, req_len); isc_buffer_putuint32(&buf, SSU_EXTERNAL_VERSION); isc_buffer_putuint32(&buf, req_len); /* Strings must be null-terminated */ isc_buffer_putstr(&buf, b_signer); isc_buffer_putuint8(&buf, 0); isc_buffer_putstr(&buf, b_name); isc_buffer_putuint8(&buf, 0); isc_buffer_putstr(&buf, b_addr); isc_buffer_putuint8(&buf, 0); isc_buffer_putstr(&buf, b_type); isc_buffer_putuint8(&buf, 0); isc_buffer_putstr(&buf, b_key); isc_buffer_putuint8(&buf, 0); isc_buffer_putuint32(&buf, token_len); if (tkey_token && token_len != 0) isc_buffer_putmem(&buf, token_region.base, token_len); ENSURE(isc_buffer_availablelength(&buf) == 0); /* Send the request */ ret = write(fd, data, req_len); isc_mem_free(mctx, data); if (ret != (ssize_t) req_len) { char strbuf[ISC_STRERRORSIZE]; isc__strerror(errno, strbuf, sizeof(strbuf)); ssu_e_log(3, "ssu_external: unable to send request - %s", strbuf); close(fd); return (ISC_FALSE); } /* Receive the reply */ ret = read(fd, &reply, sizeof(isc_uint32_t)); if (ret != (ssize_t) sizeof(isc_uint32_t)) { char strbuf[ISC_STRERRORSIZE]; isc__strerror(errno, strbuf, sizeof(strbuf)); ssu_e_log(3, "ssu_external: unable to receive reply - %s", strbuf); close(fd); return (ISC_FALSE); } close(fd); reply = ntohl(reply); if (reply == 0) { ssu_e_log(3, "ssu_external: denied external auth for '%s'", b_name); return (ISC_FALSE); } else if (reply == 1) { ssu_e_log(3, "ssu_external: allowed external auth for '%s'", b_name); return (ISC_TRUE); } ssu_e_log(3, "ssu_external: invalid reply 0x%08x", reply); return (ISC_FALSE); }
isc_result_t cfg_acl_fromconfig2(const cfg_obj_t *caml, const cfg_obj_t *cctx, isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx, unsigned int nest_level, isc_uint16_t family, dns_acl_t **target) { isc_result_t result; dns_acl_t *dacl = NULL, *inneracl = NULL; dns_aclelement_t *de; const cfg_listelt_t *elt; dns_iptable_t *iptab; int new_nest_level = 0; if (nest_level != 0) new_nest_level = nest_level - 1; REQUIRE(target != NULL); REQUIRE(*target == NULL || DNS_ACL_VALID(*target)); if (*target != NULL) { /* * If target already points to an ACL, then we're being * called recursively to configure a nested ACL. The * nested ACL's contents should just be absorbed into its * parent ACL. */ dns_acl_attach(*target, &dacl); dns_acl_detach(target); } else { /* * Need to allocate a new ACL structure. Count the items * in the ACL definition that will require space in the * elements table. (Note that if nest_level is nonzero, * *everything* goes in the elements table.) */ isc_uint32_t nelem; if (nest_level == 0) { result = count_acl_elements(caml, cctx, lctx, ctx, mctx, &nelem, NULL); if (result != ISC_R_SUCCESS) return (result); } else nelem = cfg_list_length(caml, ISC_FALSE); result = dns_acl_create(mctx, nelem, &dacl); if (result != ISC_R_SUCCESS) return (result); } de = dacl->elements; for (elt = cfg_list_first(caml); elt != NULL; elt = cfg_list_next(elt)) { const cfg_obj_t *ce = cfg_listelt_value(elt); isc_boolean_t neg = ISC_FALSE; INSIST(dacl->length <= dacl->alloc); if (cfg_obj_istuple(ce)) { /* Might be a negated element */ const cfg_obj_t *negated = cfg_tuple_get(ce, "negated"); if (! cfg_obj_isvoid(negated)) { neg = ISC_TRUE; dacl->has_negatives = ISC_TRUE; ce = negated; } } /* * If nest_level is nonzero, then every element is * to be stored as a separate, nested ACL rather than * merged into the main iptable. */ iptab = dacl->iptable; if (nest_level != 0) { result = dns_acl_create(mctx, cfg_list_length(ce, ISC_FALSE), &de->nestedacl); if (result != ISC_R_SUCCESS) goto cleanup; iptab = de->nestedacl->iptable; } if (cfg_obj_isnetprefix(ce)) { /* Network prefix */ isc_netaddr_t addr; unsigned int bitlen; cfg_obj_asnetprefix(ce, &addr, &bitlen); if (family != 0 && family != addr.family) { char buf[ISC_NETADDR_FORMATSIZE + 1]; isc_netaddr_format(&addr, buf, sizeof(buf)); cfg_obj_log(ce, lctx, ISC_LOG_WARNING, "'%s': incorrect address family; " "ignoring", buf); if (nest_level != 0) dns_acl_detach(&de->nestedacl); continue; } result = isc_netaddr_prefixok(&addr, bitlen); if (result != ISC_R_SUCCESS) { char buf[ISC_NETADDR_FORMATSIZE + 1]; isc_netaddr_format(&addr, buf, sizeof(buf)); cfg_obj_log(ce, lctx, ISC_LOG_WARNING, "'%s/%u': address/prefix length " "mismatch", buf, bitlen); } /* * If nesting ACLs (nest_level != 0), we negate * the nestedacl element, not the iptable entry. */ result = dns_iptable_addprefix(iptab, &addr, bitlen, ISC_TF(nest_level != 0 || !neg)); if (result != ISC_R_SUCCESS) goto cleanup; if (nest_level > 0) { INSIST(dacl->length < dacl->alloc); de->type = dns_aclelementtype_nestedacl; de->negative = neg; } else continue; } else if (cfg_obj_islist(ce)) { /* * If we're nesting ACLs, put the nested * ACL onto the elements list; otherwise * merge it into *this* ACL. We nest ACLs * in two cases: 1) sortlist, 2) if the * nested ACL contains negated members. */ if (inneracl != NULL) dns_acl_detach(&inneracl); result = cfg_acl_fromconfig(ce, cctx, lctx, ctx, mctx, new_nest_level, &inneracl); if (result != ISC_R_SUCCESS) goto cleanup; nested_acl: if (nest_level > 0 || inneracl->has_negatives) { INSIST(dacl->length < dacl->alloc); de->type = dns_aclelementtype_nestedacl; de->negative = neg; if (de->nestedacl != NULL) dns_acl_detach(&de->nestedacl); dns_acl_attach(inneracl, &de->nestedacl); dns_acl_detach(&inneracl); /* Fall through. */ } else { INSIST(dacl->length + inneracl->length <= dacl->alloc); dns_acl_merge(dacl, inneracl, ISC_TF(!neg)); de += inneracl->length; /* elements added */ dns_acl_detach(&inneracl); INSIST(dacl->length <= dacl->alloc); continue; } } else if (cfg_obj_istype(ce, &cfg_type_keyref)) { /* Key name. */ INSIST(dacl->length < dacl->alloc); de->type = dns_aclelementtype_keyname; de->negative = neg; dns_name_init(&de->keyname, NULL); result = convert_keyname(ce, lctx, mctx, &de->keyname); if (result != ISC_R_SUCCESS) goto cleanup; #ifdef HAVE_GEOIP } else if (cfg_obj_istuple(ce) && cfg_obj_isvoid(cfg_tuple_get(ce, "negated"))) { INSIST(dacl->length < dacl->alloc); result = parse_geoip_element(ce, lctx, ctx, de); if (result != ISC_R_SUCCESS) goto cleanup; de->type = dns_aclelementtype_geoip; de->negative = neg; #endif /* HAVE_GEOIP */ } else if (cfg_obj_isstring(ce)) { /* ACL name. */ const char *name = cfg_obj_asstring(ce); if (strcasecmp(name, "any") == 0) { /* Iptable entry with zero bit length. */ result = dns_iptable_addprefix(iptab, NULL, 0, ISC_TF(nest_level != 0 || !neg)); if (result != ISC_R_SUCCESS) goto cleanup; if (nest_level != 0) { INSIST(dacl->length < dacl->alloc); de->type = dns_aclelementtype_nestedacl; de->negative = neg; } else continue; } else if (strcasecmp(name, "none") == 0) { /* none == !any */ /* * We don't unconditional set * dacl->has_negatives and * de->negative to true so we can handle * "!none;". */ result = dns_iptable_addprefix(iptab, NULL, 0, ISC_TF(nest_level != 0 || neg)); if (result != ISC_R_SUCCESS) goto cleanup; if (!neg) dacl->has_negatives = !neg; if (nest_level != 0) { INSIST(dacl->length < dacl->alloc); de->type = dns_aclelementtype_nestedacl; de->negative = !neg; } else continue; } else if (strcasecmp(name, "localhost") == 0) { INSIST(dacl->length < dacl->alloc); de->type = dns_aclelementtype_localhost; de->negative = neg; } else if (strcasecmp(name, "localnets") == 0) { INSIST(dacl->length < dacl->alloc); de->type = dns_aclelementtype_localnets; de->negative = neg; } else { if (inneracl != NULL) dns_acl_detach(&inneracl); /* * This call should just find the cached * of the named acl. */ result = convert_named_acl(ce, cctx, lctx, ctx, mctx, new_nest_level, &inneracl); if (result != ISC_R_SUCCESS) goto cleanup; goto nested_acl; } } else { cfg_obj_log(ce, lctx, ISC_LOG_WARNING, "address match list contains " "unsupported element type"); result = ISC_R_FAILURE; goto cleanup; } /* * This should only be reached for localhost, localnets * and keyname elements, and nested ACLs if nest_level is * nonzero (i.e., in sortlists). */ if (de->nestedacl != NULL && de->type != dns_aclelementtype_nestedacl) dns_acl_detach(&de->nestedacl); dacl->node_count++; de->node_num = dacl->node_count; dacl->length++; de++; INSIST(dacl->length <= dacl->alloc); } dns_acl_attach(dacl, target); result = ISC_R_SUCCESS; cleanup: if (inneracl != NULL) dns_acl_detach(&inneracl); dns_acl_detach(&dacl); return (result); }