Apply the semantic patch to catch all the places where we pass 'char' to the <ctype.h> family of functions (isalpha() and friends, toupper(), tolower()).
2232 lines
49 KiB
C
2232 lines
49 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
/*
|
|
* Limited implementation of the DNSRPS API for testing purposes.
|
|
*
|
|
* Copyright (c) 2016-2017 Farsight Security, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <arpa/inet.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <isc/endian.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/librpz.h>
|
|
|
|
#include "test-data.h"
|
|
#include "trpz.h"
|
|
|
|
librpz_log_fnc_t *g_log_fnc = NULL;
|
|
const char *g_prog_nm = NULL;
|
|
bool g_scan_data_file_for_errors = true;
|
|
|
|
typedef struct {
|
|
void *mutex_ctx;
|
|
void *log_ctx;
|
|
librpz_mutex_t *mutex_lock_fn;
|
|
librpz_mutex_t *mutex_unlock_fn;
|
|
librpz_mutex_t *mutex_destroy_fn;
|
|
} trpz_clist_t;
|
|
|
|
typedef struct {
|
|
char *cstr;
|
|
bool uses_expired;
|
|
trpz_clist_t *pclist;
|
|
} trpz_client_t;
|
|
|
|
typedef struct {
|
|
size_t idx; /* value only used for node iteration */
|
|
trpz_client_t *client;
|
|
bool have_rd;
|
|
char zone[256];
|
|
char domain[256];
|
|
size_t zidx;
|
|
trpz_result_t rstack[LIBRPZ_RSP_STACK_DEPTH];
|
|
size_t stack_idx;
|
|
trpz_zone_t *all_zones;
|
|
trpz_result_t *all_nodes;
|
|
size_t num_zones, num_nodes;
|
|
ssize_t last_zone;
|
|
ssize_t *base_zones;
|
|
size_t nbase_zones;
|
|
} trpz_rsp_t;
|
|
|
|
librpz_log_level_t g_log_level = LIBRPZ_LOG_TRACE2;
|
|
FILE *g_log_outf = NULL;
|
|
|
|
static int
|
|
apply_all_updates(trpz_rsp_t *trsp);
|
|
static void
|
|
clear_all_updates(trpz_rsp_t *trsp);
|
|
|
|
static bool
|
|
domain_ntop(const u_char *src, char *dst, size_t dstsiz);
|
|
static bool
|
|
domain_pton2(const char *src, u_char *dst, size_t dstsiz, size_t *dstlen,
|
|
bool lower);
|
|
|
|
void
|
|
trpz_set_log(librpz_log_fnc_t *new_log, const char *prog_nm);
|
|
void
|
|
trpz_vlog(librpz_log_level_t level, void *ctx, const char *p, va_list args)
|
|
LIBRPZ_PF(3, 0);
|
|
void
|
|
trpz_log(librpz_log_level_t level, void *ctx, const char *p, ...)
|
|
LIBRPZ_PF(3, 4);
|
|
librpz_log_level_t
|
|
trpz_log_level_val(librpz_log_level_t level);
|
|
void
|
|
trpz_vpemsg(librpz_emsg_t *emsg, const char *p, va_list args) LIBRPZ_PF(2, 0);
|
|
void
|
|
trpz_pemsg(librpz_emsg_t *emsg, const char *fmt, ...) LIBRPZ_PF(2, 3);
|
|
librpz_clist_t *
|
|
trpz_clist_create(librpz_emsg_t *emsg, librpz_mutex_t *lock,
|
|
librpz_mutex_t *unlock, librpz_mutex_t *mutex_destroy,
|
|
void *mutex_ctx, void *log_ctx);
|
|
void
|
|
trpz_clist_detach(librpz_clist_t **clistp);
|
|
bool
|
|
trpz_connect(librpz_emsg_t *emsg, librpz_client_t *client, bool optional);
|
|
librpz_client_t *
|
|
trpz_client_create(librpz_emsg_t *emsg, librpz_clist_t *clist, const char *cstr,
|
|
bool use_expired);
|
|
void
|
|
trpz_client_detach(librpz_client_t **clientp);
|
|
bool
|
|
trpz_rsp_create(librpz_emsg_t *emsg, librpz_rsp_t **rspp, int *min_ns_dotsp,
|
|
librpz_client_t *client, bool have_rd, bool have_do);
|
|
void
|
|
trpz_rsp_detach(librpz_rsp_t **rspp);
|
|
bool
|
|
trpz_rsp_push(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_rsp_pop(librpz_emsg_t *emsg, librpz_result_t *result, librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_rsp_pop_discard(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_rsp_domain(librpz_emsg_t *emsg, librpz_domain_buf_t *owner,
|
|
librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_rsp_result(librpz_emsg_t *emsg, librpz_result_t *result, bool recursed,
|
|
const librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_rsp_soa(librpz_emsg_t *emsg, uint32_t *ttlp, librpz_rr_t **rrp,
|
|
librpz_domain_buf_t *origin, librpz_result_t *result,
|
|
librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_rsp_rr(librpz_emsg_t *emsg, uint16_t *typep, uint16_t *classp,
|
|
uint32_t *ttlp, librpz_rr_t **rrp, librpz_result_t *result,
|
|
const uint8_t *qname, size_t qname_size, librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_ck_domain(librpz_emsg_t *emsg, const uint8_t *domain, size_t domain_size,
|
|
librpz_trig_t trig, librpz_result_id_t hit_id, bool recursed,
|
|
librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_ck_ip(librpz_emsg_t *emsg, const void *addr, uint family,
|
|
librpz_trig_t trig, librpz_result_id_t hit_id, bool recursed,
|
|
librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_rsp_clientip_prefix(librpz_emsg_t *emsg, librpz_prefix_t *prefix,
|
|
librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_have_trig(librpz_trig_t trig, bool ipv6, const librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_rsp_forget_zone(librpz_emsg_t *emsg, librpz_cznum_t znum,
|
|
librpz_rsp_t *rsp);
|
|
char *
|
|
trpz_vers_stats(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
|
|
bool
|
|
trpz_soa_serial(librpz_emsg_t *emsg, uint32_t *serialp, const char *domain_nm,
|
|
librpz_rsp_t *rsp);
|
|
const char *
|
|
trpz_policy2str(librpz_policy_t policy, char *buf, size_t buf_size);
|
|
|
|
#define BASE_ZONE_ANY -1
|
|
#define BASE_ZONE_INVALID -2
|
|
|
|
librpz_0_t LIBRPZ_DEF = {
|
|
.dnsrpzd_path = "test-only",
|
|
.version = "0.0",
|
|
.log_level_val = trpz_log_level_val,
|
|
.set_log = trpz_set_log,
|
|
.vpemsg = trpz_vpemsg,
|
|
.pemsg = trpz_pemsg,
|
|
.vlog = trpz_vlog,
|
|
.log = trpz_log,
|
|
.clist_create = trpz_clist_create,
|
|
.clist_detach = trpz_clist_detach,
|
|
.client_create = trpz_client_create,
|
|
.connect = trpz_connect,
|
|
.client_detach = trpz_client_detach,
|
|
.rsp_create = trpz_rsp_create,
|
|
.rsp_detach = trpz_rsp_detach,
|
|
.rsp_result = trpz_rsp_result,
|
|
.have_trig = trpz_have_trig,
|
|
.rsp_domain = trpz_rsp_domain,
|
|
.rsp_rr = trpz_rsp_rr,
|
|
.rsp_soa = trpz_rsp_soa,
|
|
.soa_serial = trpz_soa_serial,
|
|
.rsp_push = trpz_rsp_push,
|
|
.rsp_pop = trpz_rsp_pop,
|
|
.rsp_pop_discard = trpz_rsp_pop_discard,
|
|
.rsp_forget_zone = trpz_rsp_forget_zone,
|
|
.ck_ip = trpz_ck_ip,
|
|
.ck_domain = trpz_ck_domain,
|
|
.policy2str = trpz_policy2str,
|
|
};
|
|
|
|
/*
|
|
* Returns whether or not searching in the specified zone, by index, is
|
|
* permitted. A client/RSP state can support a variable number of configured
|
|
* zones.
|
|
*/
|
|
static bool
|
|
has_base_zone(trpz_rsp_t *trsp, ssize_t zone) {
|
|
size_t n;
|
|
|
|
if (trsp == NULL || trsp->base_zones == NULL || trsp->nbase_zones == 0)
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
for (n = 0; n < trsp->nbase_zones; n++) {
|
|
if (trsp->base_zones[n] == BASE_ZONE_ANY ||
|
|
trsp->base_zones[n] == zone)
|
|
{
|
|
return (true);
|
|
}
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
static bool
|
|
pack_soa_record(unsigned char *rdatap, size_t rbufsz, size_t *rdlenp,
|
|
const rpz_soa_t *psoa) {
|
|
size_t needed = (sizeof(uint32_t) * 5) + strlen(psoa->mname) + 2 +
|
|
strlen(psoa->rname) + 2;
|
|
size_t mlen = 0, rlen = 0, used = 0;
|
|
|
|
if (needed > rbufsz) {
|
|
return (false);
|
|
}
|
|
|
|
if (!domain_pton2(psoa->mname, rdatap, rbufsz, &rlen, true)) {
|
|
return (false);
|
|
}
|
|
|
|
if (!domain_pton2(psoa->rname, rdatap + rlen, rbufsz - rlen, &mlen,
|
|
true))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
used = rlen + mlen;
|
|
|
|
rdatap += rlen + mlen;
|
|
ISC_U32TO8_BE(rdatap, psoa->serial);
|
|
rdatap += 4;
|
|
ISC_U32TO8_BE(rdatap, psoa->refresh);
|
|
rdatap += 4;
|
|
ISC_U32TO8_BE(rdatap, psoa->retry);
|
|
rdatap += 4;
|
|
ISC_U32TO8_BE(rdatap, psoa->expire);
|
|
rdatap += 4;
|
|
ISC_U32TO8_BE(rdatap, psoa->minimum);
|
|
used += (4 * 5);
|
|
|
|
SET_IF_NOT_NULL(rdlenp, used);
|
|
|
|
return (true);
|
|
}
|
|
|
|
static void
|
|
do_log(librpz_log_level_t level, void *ctx, const char *fmt, va_list args)
|
|
LIBRPZ_PF(3, 0);
|
|
static void
|
|
do_log(librpz_log_level_t level, void *ctx, const char *fmt, va_list args) {
|
|
if (level > g_log_level) {
|
|
return;
|
|
}
|
|
|
|
if (g_log_fnc != NULL) {
|
|
char lbuf[8192] = { 0 };
|
|
|
|
vsnprintf(lbuf, sizeof(lbuf) - 1, fmt, args);
|
|
g_log_fnc(level, ctx, lbuf);
|
|
return;
|
|
}
|
|
|
|
if (g_log_outf == NULL) {
|
|
return;
|
|
}
|
|
|
|
vfprintf(g_log_outf, fmt, args);
|
|
fprintf(g_log_outf, "\n");
|
|
return;
|
|
}
|
|
|
|
void
|
|
trpz_vlog(librpz_log_level_t level, void *ctx, const char *p, va_list args) {
|
|
do_log(level, ctx, p, args);
|
|
return;
|
|
}
|
|
|
|
void
|
|
trpz_log(librpz_log_level_t level, void *ctx, const char *p, ...) {
|
|
va_list ap;
|
|
|
|
va_start(ap, p);
|
|
trpz_vlog(level, ctx, p, ap);
|
|
va_end(ap);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
trpz_set_log(librpz_log_fnc_t *new_log, const char *prog_nm) {
|
|
assert(new_log != NULL || prog_nm != NULL);
|
|
|
|
if (new_log != NULL) {
|
|
g_log_fnc = new_log;
|
|
}
|
|
|
|
if (prog_nm != NULL) {
|
|
g_prog_nm = prog_nm;
|
|
}
|
|
}
|
|
|
|
librpz_log_level_t
|
|
trpz_log_level_val(librpz_log_level_t level) {
|
|
if (level >= LIBRPZ_LOG_INVALID) {
|
|
return (g_log_level);
|
|
}
|
|
|
|
g_log_level = (level < LIBRPZ_LOG_FATAL) ? LIBRPZ_LOG_FATAL : level;
|
|
|
|
return (g_log_level);
|
|
}
|
|
|
|
void
|
|
trpz_vpemsg(librpz_emsg_t *emsg, const char *p, va_list args) {
|
|
if (emsg == NULL) {
|
|
return;
|
|
}
|
|
|
|
vsnprintf(emsg->c, sizeof(emsg->c), p, args);
|
|
emsg->c[sizeof(emsg->c) - 1] = 0;
|
|
return;
|
|
}
|
|
|
|
void
|
|
trpz_pemsg(librpz_emsg_t *emsg, const char *fmt, ...) {
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
trpz_vpemsg(emsg, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Scan the data file for errors and log anything found that's critically
|
|
* wrong.
|
|
*/
|
|
static void
|
|
scan_data_file_for_errors(void *lctx) {
|
|
char *updfile = NULL, *fname = NULL, *last = NULL;
|
|
char *tmp = NULL;
|
|
|
|
updfile = getenv("DNSRPS_TEST_UPDATE_FILE");
|
|
if (updfile == NULL) {
|
|
return;
|
|
}
|
|
|
|
tmp = strdup(updfile);
|
|
if (tmp == NULL) {
|
|
return;
|
|
}
|
|
|
|
fname = strtok_r(tmp, ":", &last);
|
|
|
|
while (fname) {
|
|
char *errp = NULL;
|
|
int ret;
|
|
|
|
ret = sanity_check_data_file(fname, &errp);
|
|
|
|
if ((ret < 0) && errp) {
|
|
trpz_log(LIBRPZ_LOG_ERROR, lctx, "%s", errp);
|
|
free(errp);
|
|
}
|
|
|
|
fname = strtok_r(NULL, ":", &last);
|
|
}
|
|
free(tmp);
|
|
|
|
return;
|
|
}
|
|
|
|
librpz_clist_t *
|
|
trpz_clist_create(librpz_emsg_t *emsg, librpz_mutex_t *lock,
|
|
librpz_mutex_t *unlock, librpz_mutex_t *mutex_destroy,
|
|
void *mutex_ctx, void *log_ctx) {
|
|
trpz_clist_t *result = NULL;
|
|
|
|
result = calloc(1, sizeof(*result));
|
|
if (result == NULL) {
|
|
trpz_pemsg(emsg, "calloc: %s", strerror(errno));
|
|
return (NULL);
|
|
}
|
|
|
|
result->mutex_ctx = mutex_ctx;
|
|
result->log_ctx = log_ctx;
|
|
result->mutex_lock_fn = lock;
|
|
result->mutex_unlock_fn = unlock;
|
|
result->mutex_destroy_fn = mutex_destroy;
|
|
|
|
if (g_scan_data_file_for_errors) {
|
|
scan_data_file_for_errors(log_ctx);
|
|
}
|
|
|
|
return ((librpz_clist_t *)result);
|
|
}
|
|
|
|
void
|
|
trpz_clist_detach(librpz_clist_t **clistp) {
|
|
if (clistp != NULL && *clistp != NULL) {
|
|
librpz_clist_t *clist = *clistp;
|
|
*clistp = NULL;
|
|
free(clist);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
bool
|
|
trpz_connect(librpz_emsg_t *emsg, librpz_client_t *client, bool optional) {
|
|
UNUSED(optional);
|
|
|
|
if (client == NULL) {
|
|
trpz_pemsg(emsg, "Can't connect to null client");
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
const char *
|
|
trpz_policy2str(librpz_policy_t policy, char *buf, size_t buf_size) {
|
|
const char *pname = NULL;
|
|
|
|
if (buf == NULL || buf_size == 0) {
|
|
return (NULL);
|
|
}
|
|
|
|
switch (policy) {
|
|
case LIBRPZ_POLICY_UNDEFINED:
|
|
pname = "UNDEFINED";
|
|
break;
|
|
case LIBRPZ_POLICY_DELETED:
|
|
pname = "DELETED";
|
|
break;
|
|
case LIBRPZ_POLICY_PASSTHRU:
|
|
pname = "PASSTHRU";
|
|
break;
|
|
case LIBRPZ_POLICY_DROP:
|
|
pname = "DROP";
|
|
break;
|
|
case LIBRPZ_POLICY_TCP_ONLY:
|
|
pname = "TCP-ONLY";
|
|
break;
|
|
case LIBRPZ_POLICY_NXDOMAIN:
|
|
pname = "NXDOMAIN";
|
|
break;
|
|
case LIBRPZ_POLICY_NODATA:
|
|
pname = "NODATA";
|
|
break;
|
|
case LIBRPZ_POLICY_RECORD:
|
|
pname = "RECORD";
|
|
break;
|
|
case LIBRPZ_POLICY_GIVEN:
|
|
pname = "GIVEN";
|
|
break;
|
|
case LIBRPZ_POLICY_DISABLED:
|
|
pname = "DISABLED";
|
|
break;
|
|
case LIBRPZ_POLICY_CNAME:
|
|
pname = "CNAME";
|
|
break;
|
|
default:
|
|
pname = "UNKNOWN";
|
|
break;
|
|
}
|
|
|
|
strncpy(buf, pname, buf_size);
|
|
buf[buf_size - 1] = 0;
|
|
return (buf);
|
|
}
|
|
|
|
/*
|
|
* Get the entire set of zones configured by the config string,
|
|
* and bind then to the specified RSP state.
|
|
*
|
|
* The array of active zone indices is returned by the function on success,
|
|
* or NULL on failure. The total number of zones is stored in pnzones.
|
|
*/
|
|
static ssize_t *
|
|
get_cstr_zones(const char *cstr, trpz_rsp_t *trsp, size_t *pnzones) {
|
|
char tmpc[8192] = { 0 };
|
|
char *tptr = tmpc, *tok = NULL;
|
|
size_t nzones = 0, cur_idx = 0;
|
|
ssize_t *result = NULL;
|
|
unsigned long zflags = 0;
|
|
|
|
result = calloc(trsp->num_zones + 1, sizeof(*result));
|
|
if (result == NULL) {
|
|
perror("calloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (cstr == NULL) {
|
|
result[0] = BASE_ZONE_ANY;
|
|
*pnzones = 1;
|
|
return (result);
|
|
}
|
|
|
|
strncpy(tmpc, cstr, sizeof(tmpc) - 1);
|
|
*pnzones = 0;
|
|
|
|
while (tptr != NULL && *tptr != '\0') {
|
|
tok = strsep(&tptr, ";\n");
|
|
|
|
while (isspace((unsigned char)*tok)) {
|
|
tok++;
|
|
}
|
|
|
|
if (strncasecmp(tok, "zone ", 5) == 0) {
|
|
char zcmd[1024] = { 0 };
|
|
char *qend = NULL;
|
|
size_t zind = 0, old_zct = trsp->num_zones;
|
|
unsigned long zopts = 0;
|
|
|
|
tok += 5;
|
|
|
|
while (isspace((unsigned char)*tok)) {
|
|
tok++;
|
|
}
|
|
|
|
if (*tok == '"') {
|
|
qend = strchr(++tok, '"');
|
|
if (qend == NULL) {
|
|
fprintf(stderr, "Error parsing cstr "
|
|
"contents!\n");
|
|
free(result);
|
|
return (NULL);
|
|
}
|
|
|
|
*qend++ = 0;
|
|
|
|
if (tok[strlen(tok) - 1] == '.') {
|
|
tok[strlen(tok) - 1] = 0;
|
|
}
|
|
|
|
} else {
|
|
qend = tok;
|
|
}
|
|
|
|
while (*qend != '\0' && !isspace((unsigned char)*qend))
|
|
{
|
|
qend++;
|
|
}
|
|
|
|
if (*qend != '\0') {
|
|
*qend++ = '\0';
|
|
zopts = parse_zone_options(qend);
|
|
}
|
|
|
|
snprintf(zcmd, sizeof(zcmd) - 1, "zone %s 1", tok);
|
|
|
|
if (apply_update(zcmd, &(trsp->all_nodes),
|
|
&(trsp->num_nodes), &(trsp->all_zones),
|
|
&(trsp->num_zones), 0, zopts,
|
|
NULL) < 0)
|
|
{
|
|
fprintf(stderr, "Internal error {%s}!\n", zcmd);
|
|
free(result);
|
|
return (NULL);
|
|
}
|
|
|
|
if (trsp->num_zones > old_zct) {
|
|
result = realloc(result,
|
|
((trsp->num_zones + 1) *
|
|
sizeof(*result)));
|
|
if (result == NULL) {
|
|
perror("realloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
for (zind = 0; zind < trsp->num_zones; zind++) {
|
|
if (!strcmp(trsp->all_zones[zind].name, tok)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (zind == trsp->num_zones) {
|
|
free(result);
|
|
return (NULL);
|
|
}
|
|
|
|
result[cur_idx++] = zind;
|
|
*pnzones = cur_idx;
|
|
nzones++;
|
|
} else {
|
|
unsigned long flags;
|
|
|
|
flags = parse_zone_options(tok);
|
|
zflags |= (flags & (ZOPT_QNAME_AS_NS | ZOPT_IP_AS_NS |
|
|
ZOPT_RECURSIVE_ONLY |
|
|
ZOPT_NOT_RECURSIVE_ONLY |
|
|
ZOPT_NO_QNAME_WAIT_RECURSE |
|
|
ZOPT_NO_NSIP_WAIT_RECURSE));
|
|
}
|
|
|
|
tok = NULL;
|
|
}
|
|
|
|
if (nzones == 0) {
|
|
free(result);
|
|
return (NULL);
|
|
}
|
|
|
|
if (zflags != 0) {
|
|
size_t n;
|
|
|
|
for (n = 0; n < trsp->num_zones; n++) {
|
|
if (zflags & ZOPT_QNAME_AS_NS) {
|
|
trsp->all_zones[n].qname_as_ns = true;
|
|
}
|
|
|
|
if (zflags & ZOPT_IP_AS_NS) {
|
|
trsp->all_zones[n].ip_as_ns = true;
|
|
}
|
|
|
|
if (zflags & ZOPT_RECURSIVE_ONLY) {
|
|
trsp->all_zones[n].not_recursive_only = false;
|
|
} else if (zflags & ZOPT_NOT_RECURSIVE_ONLY) {
|
|
trsp->all_zones[n].not_recursive_only = true;
|
|
}
|
|
|
|
if (zflags & ZOPT_NO_QNAME_WAIT_RECURSE) {
|
|
trsp->all_zones[n].no_qname_wait_recurse = true;
|
|
}
|
|
|
|
if (zflags & ZOPT_NO_NSIP_WAIT_RECURSE) {
|
|
trsp->all_zones[n].no_nsip_wait_recurse = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
librpz_client_t *
|
|
trpz_client_create(librpz_emsg_t *emsg, librpz_clist_t *clist, const char *cstr,
|
|
bool use_expired) {
|
|
trpz_client_t *result = NULL;
|
|
|
|
if (clist == NULL) {
|
|
trpz_pemsg(emsg, "clist was NULL\n");
|
|
return (NULL);
|
|
}
|
|
|
|
result = calloc(1, sizeof(*result));
|
|
if (result == NULL) {
|
|
trpz_pemsg(emsg, "calloc: %s", strerror(errno));
|
|
return (NULL);
|
|
}
|
|
|
|
result->cstr = strdup(cstr);
|
|
if (result->cstr == NULL) {
|
|
trpz_pemsg(emsg, "strdup: %s", strerror(errno));
|
|
free(result);
|
|
return (NULL);
|
|
}
|
|
|
|
result->uses_expired = use_expired;
|
|
result->pclist = (trpz_clist_t *)clist;
|
|
|
|
return ((librpz_client_t *)result);
|
|
}
|
|
|
|
void
|
|
trpz_client_detach(librpz_client_t **clientp) {
|
|
if (clientp != NULL && *clientp != NULL) {
|
|
trpz_client_t *client = (trpz_client_t *)(*clientp);
|
|
if (client->cstr != NULL) {
|
|
free(client->cstr);
|
|
}
|
|
free(client);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If the DNSRPS_TEST_UPDATE_FILE env variable is set,
|
|
* load the current list of test nodes from the specified data file.
|
|
*
|
|
* Any existing nodes are first destroyed.
|
|
*/
|
|
static int
|
|
apply_all_updates(trpz_rsp_t *trsp) {
|
|
char *updfile = NULL, *fname = NULL, *last = NULL;
|
|
char *tmp = NULL;
|
|
|
|
updfile = getenv("DNSRPS_TEST_UPDATE_FILE");
|
|
if (updfile == NULL) {
|
|
return (0);
|
|
}
|
|
|
|
tmp = strdup(updfile);
|
|
if (tmp == NULL) {
|
|
return (-1);
|
|
}
|
|
|
|
fname = strtok_r(updfile, ":", &last);
|
|
while (fname != NULL) {
|
|
char *errp = NULL;
|
|
int ret;
|
|
|
|
ret = load_all_updates(fname, &trsp->all_nodes,
|
|
&trsp->num_nodes, &trsp->all_zones,
|
|
&trsp->num_zones, &errp);
|
|
|
|
if (errp != NULL) {
|
|
fprintf(stderr, "Error loading updates: %s\n", errp);
|
|
free(errp);
|
|
}
|
|
|
|
if (ret < 0) {
|
|
free(tmp);
|
|
return (-1);
|
|
}
|
|
|
|
fname = strtok_r(NULL, ":", &last);
|
|
}
|
|
free(tmp);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
clear_all_updates(trpz_rsp_t *trsp) {
|
|
if (trsp == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (trsp->all_zones != NULL) {
|
|
free(trsp->all_zones);
|
|
}
|
|
|
|
trsp->all_zones = NULL;
|
|
trsp->num_zones = 0;
|
|
|
|
if (trsp->all_nodes != NULL) {
|
|
size_t n;
|
|
|
|
for (n = 0; n < trsp->num_nodes; n++) {
|
|
if (trsp->all_nodes[n].canonical != NULL) {
|
|
free(trsp->all_nodes[n].canonical);
|
|
}
|
|
if (trsp->all_nodes[n].dname != NULL) {
|
|
free(trsp->all_nodes[n].dname);
|
|
}
|
|
if (trsp->all_nodes[n].rrs) {
|
|
size_t m;
|
|
|
|
for (m = 0; m < trsp->all_nodes[n].nrrs; m++) {
|
|
if (trsp->all_nodes[n].rrs[m].rdata) {
|
|
free(trsp->all_nodes[n]
|
|
.rrs[m]
|
|
.rdata);
|
|
}
|
|
}
|
|
|
|
free(trsp->all_nodes[n].rrs);
|
|
}
|
|
}
|
|
|
|
free(trsp->all_nodes);
|
|
}
|
|
|
|
trsp->all_nodes = NULL;
|
|
trsp->num_nodes = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Start a set of RPZ queries for a single DNS response.
|
|
*/
|
|
bool
|
|
trpz_rsp_create(librpz_emsg_t *emsg, librpz_rsp_t **rspp, int *min_ns_dotsp,
|
|
librpz_client_t *client, bool have_rd, bool have_do) {
|
|
trpz_client_t *cli = (trpz_client_t *)client;
|
|
trpz_rsp_t *result = NULL;
|
|
|
|
UNUSED(min_ns_dotsp);
|
|
UNUSED(have_do);
|
|
|
|
if (client == NULL) {
|
|
trpz_pemsg(emsg, "client was NULL");
|
|
return (false);
|
|
} else if (rspp == NULL) {
|
|
trpz_pemsg(emsg, "rspp was NULL");
|
|
return (false);
|
|
} else if (cli->cstr == NULL) {
|
|
trpz_pemsg(emsg, "no valid policy zone specified");
|
|
return (false);
|
|
}
|
|
|
|
result = calloc(1, sizeof(*result));
|
|
if (result == NULL) {
|
|
trpz_pemsg(emsg, "calloc: %s", strerror(errno));
|
|
return (false);
|
|
}
|
|
|
|
result->idx = 0;
|
|
result->client = cli;
|
|
result->have_rd = have_rd;
|
|
result->stack_idx = 1;
|
|
result->last_zone = -1;
|
|
|
|
assert(*rspp == NULL);
|
|
|
|
clear_all_updates(result);
|
|
result->base_zones = get_cstr_zones(cli->cstr, result,
|
|
&(result->nbase_zones));
|
|
|
|
if (result->base_zones == NULL) {
|
|
trpz_pemsg(emsg, "no valid policy zone specified");
|
|
clear_all_updates(result);
|
|
free(result);
|
|
return (false);
|
|
}
|
|
|
|
if (apply_all_updates(result) < 0) {
|
|
trpz_pemsg(emsg, "internal error loading test data 1");
|
|
clear_all_updates(result);
|
|
free(result->base_zones);
|
|
free(result);
|
|
return (false);
|
|
}
|
|
|
|
*rspp = (librpz_rsp_t *)result;
|
|
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
trpz_rsp_push(librpz_emsg_t *emsg, librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
|
|
UNUSED(emsg);
|
|
|
|
if (trsp->stack_idx == 0) {
|
|
memset(&(trsp->rstack[0]), 0, sizeof(trsp->rstack[0]) * 2);
|
|
trsp->stack_idx++;
|
|
return (true);
|
|
} else if (trsp->stack_idx >= LIBRPZ_RSP_STACK_DEPTH) {
|
|
return (false);
|
|
}
|
|
|
|
memmove(&(trsp->rstack[1]), &(trsp->rstack[0]),
|
|
(trsp->stack_idx * sizeof(trsp->rstack[0])));
|
|
trsp->stack_idx++;
|
|
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
trpz_rsp_pop(librpz_emsg_t *emsg, librpz_result_t *result, librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
|
|
UNUSED(emsg);
|
|
|
|
if (trsp->stack_idx <= 1) {
|
|
return (false);
|
|
}
|
|
|
|
memmove(&(trsp->rstack[0]), &(trsp->rstack[1]),
|
|
((trsp->stack_idx - 1) * sizeof(trsp->rstack[0])));
|
|
memmove(result, &(trsp->rstack[0].result), sizeof(*result));
|
|
trsp->stack_idx--;
|
|
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
trpz_rsp_pop_discard(librpz_emsg_t *emsg, librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
|
|
UNUSED(emsg);
|
|
|
|
if (trsp->stack_idx == 0) {
|
|
return (false);
|
|
} else if (trsp->stack_idx == 1) {
|
|
return (true);
|
|
}
|
|
|
|
if (trsp->stack_idx > 1) {
|
|
memmove(&(trsp->rstack[1]), &(trsp->rstack[2]),
|
|
((trsp->stack_idx - 2) * sizeof(trsp->rstack[0])));
|
|
}
|
|
|
|
trsp->stack_idx--;
|
|
|
|
return (true);
|
|
}
|
|
|
|
void
|
|
trpz_rsp_detach(librpz_rsp_t **rspp) {
|
|
if (rspp != NULL && *rspp != NULL) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)*rspp;
|
|
*rspp = NULL;
|
|
clear_all_updates(trsp);
|
|
if (trsp->base_zones != NULL) {
|
|
free(trsp->base_zones);
|
|
}
|
|
free(trsp);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
bool
|
|
trpz_rsp_domain(librpz_emsg_t *emsg, librpz_domain_buf_t *owner,
|
|
librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
const char *tstr = "";
|
|
char tmpname[256] = { 0 };
|
|
size_t osz = 0;
|
|
uint32_t n;
|
|
|
|
if (rsp == NULL) {
|
|
trpz_pemsg(emsg, "rsp was NULL");
|
|
return (false);
|
|
} else if (trsp->stack_idx == 0) {
|
|
trpz_pemsg(emsg, "domain not found [1]");
|
|
return (false);
|
|
} else if (trsp->rstack[0].result.policy == LIBRPZ_POLICY_UNDEFINED) {
|
|
trpz_pemsg(emsg, "domain not found [2]");
|
|
return (false);
|
|
}
|
|
|
|
if (trsp->all_zones[trsp->rstack[0].result.dznum].forgotten) {
|
|
trpz_pemsg(emsg, "domain not found [3]");
|
|
memset(owner, 0, sizeof(*owner));
|
|
return (true);
|
|
}
|
|
|
|
switch (trsp->rstack[0].result.trig) {
|
|
case LIBRPZ_TRIG_CLIENT_IP:
|
|
tstr = "rpz-client-ip.";
|
|
break;
|
|
case LIBRPZ_TRIG_IP:
|
|
tstr = "rpz-ip.";
|
|
break;
|
|
case LIBRPZ_TRIG_NSDNAME:
|
|
tstr = "rpz-nsdname.";
|
|
break;
|
|
case LIBRPZ_TRIG_NSIP:
|
|
tstr = "rpz-nsip.";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
n = snprintf(tmpname, sizeof(tmpname), "%s.%s%s", trsp->rstack[0].dname,
|
|
tstr, trsp->all_zones[trsp->rstack[0].result.dznum].name);
|
|
if (n > sizeof(tmpname)) {
|
|
trpz_pemsg(emsg, "%s truncated", tmpname);
|
|
return (false);
|
|
}
|
|
|
|
if (!domain_pton2(tmpname, owner->d, sizeof(owner->d), &osz, true)) {
|
|
trpz_pemsg(emsg, "unable to read hostname from rsp!");
|
|
return (false);
|
|
}
|
|
|
|
owner->size = osz;
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
trpz_rsp_result(librpz_emsg_t *emsg, librpz_result_t *result, bool recursed,
|
|
const librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
|
|
UNUSED(recursed);
|
|
|
|
if (rsp == NULL) {
|
|
trpz_pemsg(emsg, "rsp was NULL!");
|
|
return (false);
|
|
} else if (result == NULL) {
|
|
trpz_pemsg(emsg, "result was NULL");
|
|
return (false);
|
|
}
|
|
|
|
if (trsp->stack_idx == 0) {
|
|
memset(result, 0, sizeof(*result));
|
|
result->policy = LIBRPZ_POLICY_UNDEFINED;
|
|
} else {
|
|
if (trsp->rstack[0].result.policy && trsp->rstack[0].nrrs &&
|
|
(trsp->rstack[0].result.policy != LIBRPZ_POLICY_DISABLED))
|
|
{
|
|
trsp->rstack[0].result.next_rr =
|
|
trsp->rstack[0].rrs[0].rrn;
|
|
}
|
|
|
|
trsp->rstack[0].rridx = 0;
|
|
|
|
memmove(result, &(trsp->rstack[0].result), sizeof(*result));
|
|
|
|
if (result->policy && trsp->rstack[0].poverride) {
|
|
result->policy = trsp->rstack[0].poverride;
|
|
}
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
trpz_rsp_soa(librpz_emsg_t *emsg, uint32_t *ttlp, librpz_rr_t **rrp,
|
|
librpz_domain_buf_t *origin, librpz_result_t *result,
|
|
librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
librpz_rr_t *rres = NULL;
|
|
rpz_soa_t tmpsoa;
|
|
unsigned char *rdbuf = NULL;
|
|
char tmp_rname[1024] = { 0 };
|
|
size_t rdlen = 0;
|
|
|
|
UNUSED(ttlp);
|
|
|
|
if (result == NULL) {
|
|
trpz_pemsg(emsg, "result was NULL!");
|
|
return (false);
|
|
} else if (rsp == NULL) {
|
|
trpz_pemsg(emsg, "rsp was NULL!");
|
|
return (false);
|
|
}
|
|
|
|
if (trsp->zidx >= trsp->num_zones) {
|
|
trpz_pemsg(emsg, "bad zone");
|
|
return (false);
|
|
}
|
|
|
|
rdbuf = calloc(1024, 1);
|
|
if (rdbuf == NULL) {
|
|
trpz_pemsg(emsg, "calloc: %s", strerror(errno));
|
|
return (false);
|
|
}
|
|
|
|
rres = (librpz_rr_t *)rdbuf;
|
|
|
|
rres->type = htons(6);
|
|
rres->class = htons(1);
|
|
rres->ttl = htonl(60);
|
|
|
|
memmove(&tmpsoa, &g_soa_record, sizeof(tmpsoa));
|
|
tmpsoa.serial = trsp->all_zones[result->dznum].serial;
|
|
tmpsoa.mname = trsp->all_zones[result->dznum].name;
|
|
|
|
snprintf(tmp_rname, sizeof(tmp_rname) - 1, "hostmaster.ns.%s",
|
|
tmpsoa.mname);
|
|
tmpsoa.rname = tmp_rname;
|
|
|
|
if (tmpsoa.serial == 0) {
|
|
tmpsoa.serial = time(NULL);
|
|
}
|
|
|
|
if (!pack_soa_record(rres->rdata, 1024 - sizeof(*rres), &rdlen,
|
|
&tmpsoa))
|
|
{
|
|
trpz_pemsg(emsg, "Error packing SOA reply");
|
|
free(rdbuf);
|
|
return (false);
|
|
}
|
|
|
|
rres->rdlength = htons(rdlen);
|
|
|
|
if (origin != NULL) {
|
|
uint8_t *buf = NULL;
|
|
int nbytes;
|
|
|
|
if ((nbytes = wdns_str_to_name(tmpsoa.mname, &buf, 1)) < 0) {
|
|
trpz_pemsg(emsg, "Error packing domain");
|
|
free(rdbuf);
|
|
return (false);
|
|
}
|
|
|
|
memset(origin, 0, sizeof(*origin));
|
|
memmove(origin->d, buf, nbytes);
|
|
origin->size = nbytes;
|
|
free(buf);
|
|
}
|
|
|
|
if (rrp != NULL) {
|
|
*rrp = rres;
|
|
} else {
|
|
free(rdbuf);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
/*
|
|
* Compare a query domain against a record, allowing for the possibility of
|
|
* a wildcard match.
|
|
*/
|
|
static int
|
|
domain_cmp(const char *query, const char *record, bool *wildp) {
|
|
const char *end = NULL;
|
|
size_t cmplen;
|
|
|
|
end = record + strlen(record);
|
|
|
|
cmplen = end - record;
|
|
|
|
*wildp = false;
|
|
|
|
if (record != NULL && *record == '*' && record[1] == '.') {
|
|
const char *rptr = record + 2;
|
|
|
|
if ((cmplen - 2) < strlen(query)) {
|
|
const char *qptr = NULL;
|
|
|
|
qptr = query + strlen(query) - (cmplen - 2);
|
|
|
|
if (strncmp(qptr, rptr, (cmplen - 2)) == 0) {
|
|
*wildp = true;
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strlen(query) > cmplen) {
|
|
return (1);
|
|
} else if (strlen(query) < cmplen) {
|
|
return (-1);
|
|
}
|
|
|
|
return ((strncmp(record, query, cmplen)));
|
|
}
|
|
|
|
/*
|
|
* Count the number of labels in the given domain name.
|
|
*/
|
|
static size_t
|
|
count_labels(const char *domain) {
|
|
const char *dptr = NULL;
|
|
size_t result = 1;
|
|
|
|
if (domain == NULL || *domain == '\0') {
|
|
return (0);
|
|
}
|
|
|
|
dptr = domain + strlen(domain);
|
|
|
|
while (1) {
|
|
while ((dptr >= domain) && (*dptr != '.')) {
|
|
dptr--;
|
|
}
|
|
|
|
if (dptr <= domain) {
|
|
break;
|
|
}
|
|
|
|
result++;
|
|
dptr--;
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* Does the newly found result supercede the old result in precedence?
|
|
* This function is used to determine whether a match on the result stack
|
|
* should be overwritten by another match of higher precedence - or whether
|
|
* the old match should remain, as-is.
|
|
*
|
|
* 1. "CNAME or DNAME Chain Position" Precedence Rule
|
|
* 2. "RPZ Ordering" Precedence Rule [zone order]
|
|
* 3. "Domain Name Matching" Precedence Rule [QNAME/NSDNAME - label count]
|
|
* 4. "Trigger Type" Precedence Rule
|
|
* 5. "Name Order" Precedence Rule [NSDNAME]
|
|
* 6. "Prefix Length" Precedence Rule
|
|
* 7. "IP Address Order" Precedence Rule
|
|
*/
|
|
static bool
|
|
result_supercedes(const trpz_result_t *new, const trpz_result_t *old) {
|
|
size_t nsz, osz;
|
|
|
|
if (old == NULL || old->result.policy == 0 || old->dname == NULL ||
|
|
old->dname[0] == '\0')
|
|
{
|
|
return (true);
|
|
}
|
|
|
|
if (new->result.dznum < old->result.dznum) {
|
|
return (true);
|
|
} else if (new->result.dznum > old->result.dznum) {
|
|
return (false);
|
|
}
|
|
|
|
nsz = count_labels(new->dname);
|
|
osz = count_labels(old->dname);
|
|
|
|
/* More matching labels is better. */
|
|
if (nsz > osz) {
|
|
return (true);
|
|
} else if (nsz < osz) {
|
|
return (false);
|
|
}
|
|
|
|
if (new->result.trig < old->result.trig) {
|
|
return (true);
|
|
} else if (new->result.trig > old->result.trig) {
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
static bool
|
|
result_supercedes_address(const trpz_result_t *new, const trpz_result_t *old) {
|
|
if (old == NULL || old->result.policy == 0 || old->dname == NULL ||
|
|
old->dname[0] == '\0')
|
|
{
|
|
return (true);
|
|
}
|
|
|
|
if (new->result.dznum < old->result.dznum) {
|
|
return (true);
|
|
} else if (new->result.dznum > old->result.dznum) {
|
|
return (false);
|
|
}
|
|
|
|
if (new->result.trig < old->result.trig) {
|
|
return (true);
|
|
} else if (new->result.trig > old->result.trig) {
|
|
return (false);
|
|
}
|
|
|
|
if ((new->flags &NODE_FLAG_IPV6_ADDRESS) &&
|
|
!(old->flags & NODE_FLAG_IPV6_ADDRESS))
|
|
{
|
|
return (true);
|
|
}
|
|
|
|
/*
|
|
* XXX: this is broken. Needs proper address comparison. For
|
|
* example, by most specific prefix match.
|
|
*/
|
|
if (strcmp(old->dname, new->dname) < 0) {
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
trpz_ck_domain(librpz_emsg_t *emsg, const uint8_t *domain, size_t domain_size,
|
|
librpz_trig_t trig, librpz_result_id_t hit_id, bool recursed,
|
|
librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
char dname[256] = { 0 };
|
|
librpz_trig_t toverride = LIBRPZ_TRIG_BAD;
|
|
ssize_t fidx = -1, nfidx = -1;
|
|
bool wild;
|
|
size_t n;
|
|
|
|
if (rsp == NULL) {
|
|
trpz_pemsg(emsg, "rsp was NULL!");
|
|
return (false);
|
|
} else if (domain == NULL || domain_size == 0) {
|
|
trpz_pemsg(emsg, "domain was empty");
|
|
return (false);
|
|
} else if (trig != LIBRPZ_TRIG_QNAME && trig != LIBRPZ_TRIG_NSDNAME) {
|
|
trpz_pemsg(emsg, "invalid trigger type");
|
|
return (false);
|
|
} else if (!domain_ntop(domain, dname, sizeof(dname))) {
|
|
trpz_pemsg(emsg, "domain was invalid");
|
|
return (false);
|
|
}
|
|
|
|
if (trsp->stack_idx == 0) {
|
|
trsp->stack_idx = 1;
|
|
}
|
|
|
|
for (n = 0; n < trsp->num_nodes; n++) {
|
|
int pos = 0;
|
|
|
|
if (!trsp->have_rd &&
|
|
!trsp->all_zones[trsp->all_nodes[n].result.dznum]
|
|
.not_recursive_only)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pos ||
|
|
((trig == trsp->all_nodes[n].result.trig) &&
|
|
(!domain_cmp(dname, trsp->all_nodes[n].dname, &wild))))
|
|
{
|
|
if ((trsp->all_zones[trsp->all_nodes[n].result.dznum]
|
|
.no_qname_wait_recurse ||
|
|
trsp->all_zones[trsp->all_nodes[n].result.dznum]
|
|
.not_recursive_only ||
|
|
recursed) &&
|
|
has_base_zone(trsp,
|
|
trsp->all_nodes[n].result.dznum) &&
|
|
!trsp->all_zones[trsp->all_nodes[n].result.dznum]
|
|
.forgotten)
|
|
{
|
|
if (fidx < 0) {
|
|
fidx = n;
|
|
toverride = LIBRPZ_TRIG_BAD;
|
|
}
|
|
}
|
|
if (!wild) {
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Some inelegant special handling for qname_as_ns
|
|
* feature.
|
|
*/
|
|
} else if (trsp->all_zones[trsp->all_nodes[n].result.dznum]
|
|
.qname_as_ns &&
|
|
(((trig == LIBRPZ_TRIG_QNAME) &&
|
|
(trsp->all_nodes[n].result.trig ==
|
|
LIBRPZ_TRIG_NSDNAME)) ||
|
|
((trig == LIBRPZ_TRIG_NSDNAME) &&
|
|
(trsp->all_nodes[n].result.trig ==
|
|
LIBRPZ_TRIG_QNAME))))
|
|
{
|
|
if (!domain_cmp(dname, trsp->all_nodes[n].dname, &wild))
|
|
{
|
|
if (recursed &&
|
|
has_base_zone(
|
|
trsp,
|
|
trsp->all_nodes[n].result.dznum) &&
|
|
!trsp->all_zones[trsp->all_nodes[n]
|
|
.result.dznum]
|
|
.forgotten)
|
|
{
|
|
if (fidx < 0) {
|
|
fidx = n;
|
|
toverride = trig;
|
|
}
|
|
}
|
|
if (!wild) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!trsp->have_rd && (fidx < 0) && (nfidx < 0)) {
|
|
goto out;
|
|
}
|
|
|
|
if (recursed && (fidx < 0) && trsp->rstack[0].hidden_policy) {
|
|
if (trsp->rstack[0].result.trig <= trig) {
|
|
trsp->rstack[0].result.policy =
|
|
trsp->rstack[0].hidden_policy;
|
|
trsp->rstack[0].result.zpolicy =
|
|
trsp->rstack[0].hidden_policy;
|
|
trsp->rstack[0].hidden_policy = LIBRPZ_POLICY_UNDEFINED;
|
|
return (true);
|
|
}
|
|
}
|
|
|
|
if (fidx >= 0) {
|
|
bool disabled = false;
|
|
|
|
if (!result_supercedes(&(trsp->all_nodes[fidx]),
|
|
&(trsp->rstack[0])))
|
|
{
|
|
return (true);
|
|
}
|
|
|
|
strncpy(trsp->domain, dname, sizeof(trsp->domain));
|
|
memmove(&(trsp->rstack[0]), &(trsp->all_nodes[fidx]),
|
|
sizeof(trsp->rstack[0]));
|
|
trsp->rstack[0].result.hit_id = hit_id;
|
|
trsp->last_zone = trsp->rstack[0].result.dznum;
|
|
|
|
if (trsp->all_zones[trsp->rstack[0].result.dznum].flags) {
|
|
unsigned long flags =
|
|
trsp->all_zones[trsp->rstack[0].result.dznum]
|
|
.flags;
|
|
librpz_policy_t force_policy = 0;
|
|
|
|
if (flags & ZOPT_POLICY_NODATA) {
|
|
force_policy = LIBRPZ_POLICY_NODATA;
|
|
} else if (flags & ZOPT_POLICY_PASSTHRU) {
|
|
force_policy = LIBRPZ_POLICY_PASSTHRU;
|
|
} else if (flags & ZOPT_POLICY_NXDOMAIN) {
|
|
force_policy = LIBRPZ_POLICY_NXDOMAIN;
|
|
} else if (flags & ZOPT_POLICY_DROP) {
|
|
force_policy = LIBRPZ_POLICY_DROP;
|
|
} else if (flags & ZOPT_POLICY_TCP_ONLY) {
|
|
force_policy = LIBRPZ_POLICY_TCP_ONLY;
|
|
} else if (flags & ZOPT_POLICY_DISABLED) {
|
|
disabled = true;
|
|
}
|
|
|
|
if (force_policy) {
|
|
trsp->rstack[0].result.policy = force_policy;
|
|
trsp->rstack[0].result.zpolicy = force_policy;
|
|
trsp->rstack[0].poverride = force_policy;
|
|
}
|
|
}
|
|
|
|
if (toverride) {
|
|
trsp->rstack[0].result.trig = toverride;
|
|
}
|
|
|
|
if (disabled) {
|
|
trsp->rstack[0].poverride = LIBRPZ_POLICY_DISABLED;
|
|
} else if (!recursed) {
|
|
ssize_t m;
|
|
|
|
/*
|
|
* If recursed is not set, then an earlier zone of
|
|
* higher precedence may not contain a rule of
|
|
* trigger types rpz-ip, rpz-nsip, or rpz-nsdname -
|
|
* which are by nature post-recursion rules.
|
|
*/
|
|
for (m = trsp->all_nodes[fidx].result.dznum - 1; m >= 0;
|
|
m--)
|
|
{
|
|
if (trsp->all_zones[m]
|
|
.has_triggers[0][LIBRPZ_TRIG_IP] ||
|
|
trsp->all_zones[m]
|
|
.has_triggers[1][LIBRPZ_TRIG_IP] ||
|
|
trsp->all_zones[m]
|
|
.has_triggers[0][LIBRPZ_TRIG_NSIP] ||
|
|
trsp->all_zones[m]
|
|
.has_triggers[1][LIBRPZ_TRIG_NSIP] ||
|
|
trsp->all_zones[m]
|
|
.has_triggers[0]
|
|
[LIBRPZ_TRIG_NSDNAME])
|
|
{
|
|
trsp->rstack[0].result.policy =
|
|
LIBRPZ_POLICY_UNDEFINED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (true);
|
|
} else if (nfidx >= 0) {
|
|
strncpy(trsp->domain, dname, sizeof(trsp->domain));
|
|
memmove(&(trsp->rstack[0]), &(trsp->all_nodes[nfidx]),
|
|
sizeof(trsp->rstack[0]));
|
|
trsp->last_zone = trsp->all_nodes[nfidx].result.dznum;
|
|
trsp->rstack[0].hidden_policy = trsp->rstack[0].result.policy;
|
|
trsp->rstack[0].result.hit_id = hit_id;
|
|
trsp->rstack[0].result.policy = LIBRPZ_POLICY_UNDEFINED;
|
|
trsp->rstack[0].result.zpolicy = LIBRPZ_POLICY_UNDEFINED;
|
|
return (true);
|
|
}
|
|
|
|
out:
|
|
if (trsp->rstack[0].result.policy == LIBRPZ_POLICY_UNDEFINED) {
|
|
memset(&(trsp->rstack[0]), 0, sizeof(trsp->rstack[0]));
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
static void
|
|
rpzify_ipv6_str(char *buf) {
|
|
char tmpb[512] = { 0 }, *tptr = NULL;
|
|
|
|
strncpy(tmpb, buf, sizeof(tmpb) - 1);
|
|
memset(buf, 0, strlen(buf));
|
|
tptr = tmpb + strlen(tmpb);
|
|
|
|
strcat(buf, "128.");
|
|
|
|
while (tptr > tmpb) {
|
|
if (*tptr == ':') {
|
|
strcat(buf, tptr + 1);
|
|
strcat(buf, ".");
|
|
}
|
|
|
|
tptr--;
|
|
|
|
if ((*tptr == ':') && (tptr[0] == tptr[1])) {
|
|
strcat(buf, "zz");
|
|
}
|
|
|
|
if (tptr[1] == ':') {
|
|
tptr[1] = 0;
|
|
}
|
|
}
|
|
|
|
strcat(buf, tptr);
|
|
|
|
return;
|
|
}
|
|
|
|
static uint32_t
|
|
get_mask(unsigned char prefix) {
|
|
uint32_t result = 0;
|
|
unsigned char n;
|
|
|
|
if (prefix == 0) {
|
|
return (0);
|
|
} else if (prefix >= 32) {
|
|
return (~(0));
|
|
}
|
|
|
|
for (n = 1; n < prefix; n++) {
|
|
result |= (1 << n);
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
/* XXX: this is broken for handling subnet masks in IPv6. */
|
|
static int
|
|
address_cmp(const char *addrstr, const void *addr, uint family,
|
|
unsigned int *pmask) {
|
|
char abuf[256] = { 0 };
|
|
int ipstr[8] = { 0 };
|
|
unsigned int nmask = 32;
|
|
|
|
if (family == AF_INET6) {
|
|
if (inet_ntop(AF_INET6, addr, abuf, sizeof(abuf)) == 0) {
|
|
return (-1);
|
|
}
|
|
|
|
rpzify_ipv6_str(abuf);
|
|
} else if (family == AF_INET) {
|
|
char newstr[32] = { 0 };
|
|
in_addr_t a1, a2;
|
|
|
|
if (sscanf(addrstr, "%d.%d.%d.%d.%d", &ipstr[0], &ipstr[1],
|
|
&ipstr[2], &ipstr[3], &ipstr[4]) == 5)
|
|
{
|
|
nmask = ipstr[0];
|
|
if (nmask > 32) {
|
|
return (-1);
|
|
}
|
|
} else if (sscanf(addrstr, "%d.%d.%d.%d", &ipstr[1], &ipstr[2],
|
|
&ipstr[3], &ipstr[4]) != 4)
|
|
{
|
|
perror("bad address format");
|
|
return (-1);
|
|
}
|
|
|
|
if (ipstr[1] > 255 || ipstr[2] > 255 || ipstr[3] > 255 ||
|
|
ipstr[4] > 255 || ipstr[1] < 0 || ipstr[2] < 0 ||
|
|
ipstr[3] < 0 || ipstr[4] < 0)
|
|
{
|
|
perror("bad address format");
|
|
return (-1);
|
|
}
|
|
|
|
sprintf(newstr, "%u.%u.%u.%u", ipstr[4], ipstr[3], ipstr[2],
|
|
ipstr[1]);
|
|
|
|
a1 = inet_addr(newstr);
|
|
if (a1 == INADDR_NONE) {
|
|
perror("inet_addr");
|
|
return (-1);
|
|
} else {
|
|
uint32_t m;
|
|
|
|
memmove(&a2, addr, sizeof(uint32_t));
|
|
m = get_mask(nmask);
|
|
|
|
if (pmask != NULL) {
|
|
*pmask = nmask;
|
|
}
|
|
|
|
return (((a1 & m) == (a2 & m)) ? 0 : 1);
|
|
}
|
|
|
|
} else {
|
|
return (-1);
|
|
}
|
|
|
|
if (strcmp(addrstr, abuf) == 0) {
|
|
if (pmask != NULL) {
|
|
*pmask = nmask;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
bool
|
|
trpz_ck_ip(librpz_emsg_t *emsg, const void *addr, uint family,
|
|
librpz_trig_t trig, librpz_result_id_t hit_id, bool recursed,
|
|
librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
size_t n, last_mask = 0;
|
|
ssize_t fidx = -1, nfidx = -1;
|
|
|
|
if (rsp == NULL) {
|
|
trpz_pemsg(emsg, "rsp was NULL!");
|
|
return (false);
|
|
} else if (addr == NULL) {
|
|
trpz_pemsg(emsg, "addr was empty");
|
|
return (false);
|
|
} else if (trig != LIBRPZ_TRIG_IP && trig != LIBRPZ_TRIG_CLIENT_IP &&
|
|
trig != LIBRPZ_TRIG_NSIP)
|
|
{
|
|
trpz_pemsg(emsg, "trigger type not supported for IP");
|
|
return (false);
|
|
}
|
|
|
|
if (trsp->stack_idx == 0) {
|
|
trsp->stack_idx = 1;
|
|
}
|
|
|
|
/* The final match is the most specifically-matching netmask. */
|
|
for (n = 0; n < trsp->num_nodes; n++) {
|
|
unsigned int mask = 0;
|
|
bool amatch = false;
|
|
|
|
if (!trsp->have_rd &&
|
|
!trsp->all_zones[trsp->all_nodes[n].result.dznum]
|
|
.not_recursive_only)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (trsp->all_zones[trsp->all_nodes[n].result.dznum].ip_as_ns &&
|
|
(((trig == LIBRPZ_TRIG_IP) &&
|
|
(trsp->all_nodes[n].result.trig == LIBRPZ_TRIG_NSIP)) ||
|
|
((trig == LIBRPZ_TRIG_NSIP) &&
|
|
(trsp->all_nodes[n].result.trig == LIBRPZ_TRIG_IP))) &&
|
|
!address_cmp(trsp->all_nodes[n].dname, addr, family, &mask))
|
|
{
|
|
amatch = true;
|
|
} else if (trsp->all_nodes[n].match_trig != trig) {
|
|
continue;
|
|
}
|
|
|
|
if (amatch ||
|
|
!address_cmp(trsp->all_nodes[n].dname, addr, family, &mask))
|
|
{
|
|
if ((trsp->all_zones[trsp->all_nodes[n].result.dznum]
|
|
.no_qname_wait_recurse ||
|
|
trsp->all_zones[trsp->all_nodes[n].result.dznum]
|
|
.not_recursive_only ||
|
|
recursed) &&
|
|
has_base_zone(trsp,
|
|
trsp->all_nodes[n].result.dznum) &&
|
|
!trsp->all_zones[trsp->all_nodes[n].result.dznum]
|
|
.forgotten)
|
|
{
|
|
if (mask > last_mask) {
|
|
last_mask = mask;
|
|
fidx = n;
|
|
}
|
|
|
|
} else if ((nfidx < 0) && !recursed &&
|
|
has_base_zone(
|
|
trsp,
|
|
trsp->all_nodes[n].result.dznum) &&
|
|
!trsp->all_zones[trsp->all_nodes[n]
|
|
.result.dznum]
|
|
.forgotten)
|
|
{
|
|
nfidx = n;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!trsp->have_rd && (fidx < 0) && (nfidx < 0)) {
|
|
goto out;
|
|
}
|
|
|
|
if (recursed && (fidx < 0) && trsp->rstack[0].hidden_policy) {
|
|
if (trsp->rstack[0].result.trig <= trig) {
|
|
trsp->rstack[0].result.policy =
|
|
trsp->rstack[0].hidden_policy;
|
|
trsp->rstack[0].result.zpolicy =
|
|
trsp->rstack[0].hidden_policy;
|
|
trsp->rstack[0].hidden_policy = LIBRPZ_POLICY_UNDEFINED;
|
|
return (true);
|
|
}
|
|
}
|
|
|
|
if (fidx >= 0) {
|
|
bool disabled = false;
|
|
|
|
if (!result_supercedes_address(&(trsp->all_nodes[fidx]),
|
|
&(trsp->rstack[0])))
|
|
{
|
|
return (true);
|
|
}
|
|
|
|
memmove(&(trsp->rstack[0]), &(trsp->all_nodes[fidx]),
|
|
sizeof(trsp->rstack[0]));
|
|
trsp->rstack[0].result.hit_id = hit_id;
|
|
trsp->last_zone = trsp->rstack[0].result.dznum;
|
|
|
|
if (trsp->all_zones[trsp->rstack[0].result.dznum].flags) {
|
|
unsigned long flags =
|
|
trsp->all_zones[trsp->rstack[0].result.dznum]
|
|
.flags;
|
|
librpz_policy_t force_policy = 0;
|
|
|
|
if (flags & ZOPT_POLICY_NODATA) {
|
|
force_policy = LIBRPZ_POLICY_NODATA;
|
|
} else if (flags & ZOPT_POLICY_PASSTHRU) {
|
|
force_policy = LIBRPZ_POLICY_PASSTHRU;
|
|
} else if (flags & ZOPT_POLICY_NXDOMAIN) {
|
|
force_policy = LIBRPZ_POLICY_NXDOMAIN;
|
|
} else if (flags & ZOPT_POLICY_DROP) {
|
|
force_policy = LIBRPZ_POLICY_DROP;
|
|
} else if (flags & ZOPT_POLICY_TCP_ONLY) {
|
|
force_policy = LIBRPZ_POLICY_TCP_ONLY;
|
|
} else if (flags & ZOPT_POLICY_DISABLED) {
|
|
disabled = true;
|
|
}
|
|
|
|
if (force_policy) {
|
|
trsp->rstack[0].result.policy = force_policy;
|
|
trsp->rstack[0].result.zpolicy = force_policy;
|
|
trsp->rstack[0].poverride = force_policy;
|
|
}
|
|
}
|
|
|
|
if (disabled) {
|
|
trsp->rstack[0].poverride = LIBRPZ_POLICY_DISABLED;
|
|
}
|
|
|
|
return (true);
|
|
} else if (nfidx >= 0) {
|
|
memmove(&(trsp->rstack[0]), &(trsp->all_nodes[nfidx]),
|
|
sizeof(trsp->rstack[0]));
|
|
trsp->last_zone = trsp->rstack[0].result.dznum;
|
|
trsp->rstack[0].result.hit_id = hit_id;
|
|
trsp->rstack[0].hidden_policy = trsp->rstack[0].result.policy;
|
|
trsp->rstack[0].result.policy = LIBRPZ_POLICY_UNDEFINED;
|
|
trsp->rstack[0].result.zpolicy = LIBRPZ_POLICY_UNDEFINED;
|
|
return (true);
|
|
}
|
|
|
|
out:
|
|
if (trig == LIBRPZ_TRIG_NSIP) {
|
|
bool needs_wait = false;
|
|
|
|
for (n = 0; n < trsp->num_zones; n++) {
|
|
if (trsp->all_zones[n].no_nsip_wait_recurse) {
|
|
needs_wait = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!needs_wait) {
|
|
usleep(100);
|
|
}
|
|
}
|
|
|
|
if (trsp->rstack[0].result.policy == LIBRPZ_POLICY_UNDEFINED) {
|
|
memset(&(trsp->rstack[0]), 0, sizeof(trsp->rstack[0]));
|
|
trsp->rstack[0].result.trig = trig;
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
trpz_soa_serial(librpz_emsg_t *emsg, uint32_t *serialp, const char *domain_nm,
|
|
librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
size_t n, dlen;
|
|
|
|
if (rsp == NULL) {
|
|
trpz_pemsg(emsg, "rsp was NULL");
|
|
return (false);
|
|
} else if (domain_nm == NULL) {
|
|
trpz_pemsg(emsg, "domain_nm was NULL");
|
|
return (false);
|
|
} else if (serialp == NULL) {
|
|
trpz_pemsg(emsg, "serialp was NULL");
|
|
return (false);
|
|
}
|
|
|
|
dlen = strlen(domain_nm);
|
|
|
|
if (dlen > 0U && domain_nm[dlen - 1] == '.') {
|
|
dlen--;
|
|
}
|
|
|
|
for (n = 0; n < trsp->num_zones; n++) {
|
|
if (dlen != strlen(trsp->all_zones[n].name)) {
|
|
continue;
|
|
} else if (!has_base_zone(trsp, n)) {
|
|
continue;
|
|
}
|
|
|
|
if ((strlen(trsp->all_zones[n].name) == dlen) &&
|
|
(!strncmp(trsp->all_zones[n].name, domain_nm, dlen)))
|
|
{
|
|
if (!trsp->all_zones[n].serial) {
|
|
trsp->all_zones[n].serial = time(NULL);
|
|
}
|
|
|
|
*serialp = trsp->all_zones[n].serial;
|
|
return (true);
|
|
}
|
|
}
|
|
|
|
trpz_pemsg(emsg, "zone not found");
|
|
return (false);
|
|
}
|
|
|
|
static bool
|
|
domain_ntop(const u_char *src, char *dst, size_t dstsiz) {
|
|
const unsigned char *sptr = src;
|
|
char *dptr = dst, *dend = dst + dstsiz;
|
|
|
|
if (dst == NULL || dstsiz == 0) {
|
|
return (false);
|
|
}
|
|
|
|
memset(dst, 0, dstsiz);
|
|
|
|
while (*sptr) {
|
|
if (((dptr + *sptr) > dend)) {
|
|
return (false);
|
|
}
|
|
|
|
if (sptr != src) {
|
|
*dptr++ = '.';
|
|
}
|
|
|
|
memmove(dptr, sptr + 1, *sptr);
|
|
dptr += *sptr;
|
|
sptr += *sptr;
|
|
sptr++;
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
static bool
|
|
domain_pton2(const char *src, u_char *dst, size_t dstsiz, size_t *dstlen,
|
|
bool lower) {
|
|
unsigned char *dptr = dst;
|
|
const unsigned char *dend = dst + dstsiz;
|
|
char *tmps = NULL, *tok = NULL, *tptr = NULL;
|
|
|
|
UNUSED(lower);
|
|
|
|
if (src == NULL || dst == NULL || dstsiz == 0) {
|
|
return (false);
|
|
}
|
|
|
|
memset(dst, 0, dstsiz);
|
|
|
|
tmps = strdup(src);
|
|
if (tmps == NULL) {
|
|
perror("strdup");
|
|
return (false);
|
|
}
|
|
|
|
tptr = tmps;
|
|
|
|
SET_IF_NOT_NULL(dstlen, 0);
|
|
|
|
while (tptr && *tptr) {
|
|
tok = strsep(&tptr, ".");
|
|
|
|
if (((dptr + strlen(tok) + 1) > dend)) {
|
|
free(tmps);
|
|
return (false);
|
|
}
|
|
|
|
*dptr++ = strlen(tok);
|
|
memmove(dptr, tok, strlen(tok));
|
|
dptr += strlen(tok);
|
|
|
|
if (dstlen != NULL) {
|
|
(*dstlen) += (1 + strlen(tok));
|
|
}
|
|
}
|
|
|
|
if (dptr >= dend) {
|
|
free(tmps);
|
|
return (false);
|
|
}
|
|
|
|
*dptr = 0;
|
|
|
|
if (dstlen != NULL) {
|
|
(*dstlen)++;
|
|
}
|
|
|
|
free(tmps);
|
|
|
|
return (true);
|
|
}
|
|
|
|
/* XXX: needs IPv6 support. */
|
|
bool
|
|
trpz_rsp_clientip_prefix(librpz_emsg_t *emsg, librpz_prefix_t *prefix,
|
|
librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
unsigned int cbytes[5] = { 0 };
|
|
uint8_t *aptr = NULL;
|
|
|
|
if (rsp == NULL) {
|
|
trpz_pemsg(emsg, "rsp was NULL");
|
|
return (false);
|
|
} else if (prefix == NULL) {
|
|
trpz_pemsg(emsg, "prefix was NULL");
|
|
return (false);
|
|
}
|
|
|
|
memset(prefix, 0, sizeof(*prefix));
|
|
|
|
if (trsp->rstack[0].result.trig != LIBRPZ_TRIG_CLIENT_IP) {
|
|
return (true);
|
|
}
|
|
|
|
if (sscanf(trsp->rstack[0].dname, "%u.%u.%u.%u.%u", &cbytes[0],
|
|
&cbytes[1], &cbytes[2], &cbytes[3], &cbytes[4]) != 5)
|
|
{
|
|
char abuf[64] = { 0 };
|
|
int family = 0;
|
|
|
|
if (sscanf(trsp->rstack[0].dname, "%u.", &cbytes[0]) != 1) {
|
|
return (true);
|
|
}
|
|
|
|
if (get_address_info(trsp->rstack[0].dname, &family, abuf, NULL,
|
|
NULL) < 0)
|
|
{
|
|
return (true);
|
|
} else if (family != AF_INET6) {
|
|
return (true);
|
|
}
|
|
|
|
aptr = (uint8_t *)&(prefix->addr.in6);
|
|
memset(aptr, 0, sizeof(prefix->addr.in6));
|
|
|
|
if (inet_pton(AF_INET6, abuf, aptr) != 1) {
|
|
return (true);
|
|
}
|
|
|
|
prefix->family = AF_INET6;
|
|
prefix->len = cbytes[0];
|
|
|
|
return (true);
|
|
}
|
|
|
|
prefix->family = AF_INET;
|
|
prefix->len = cbytes[0];
|
|
|
|
if (prefix->len <= 24) {
|
|
cbytes[1] = 0;
|
|
}
|
|
|
|
if (prefix->len <= 16) {
|
|
cbytes[2] = 0;
|
|
}
|
|
|
|
if (prefix->len == 86) {
|
|
cbytes[3] = 0;
|
|
}
|
|
|
|
aptr = (uint8_t *)&(prefix->addr.in);
|
|
*aptr++ = cbytes[4];
|
|
*aptr++ = cbytes[3];
|
|
*aptr++ = cbytes[2];
|
|
*aptr++ = cbytes[1];
|
|
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
trpz_have_trig(librpz_trig_t trig, bool ipv6, const librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
size_t ind = ipv6 ? 1 : 0;
|
|
|
|
if (rsp == NULL) {
|
|
return (false);
|
|
}
|
|
|
|
/* No hit, so look in all zones for trigger. */
|
|
if (trsp->stack_idx == 0 || trsp->rstack[0].result.policy == 0) {
|
|
ssize_t max_z = (trsp->last_zone >= 0)
|
|
? trsp->last_zone
|
|
: (ssize_t)trsp->num_zones - 0;
|
|
|
|
for (ssize_t n = 0; n < max_z; n++) {
|
|
if (!trsp->have_rd &&
|
|
!trsp->all_zones[n].not_recursive_only)
|
|
{
|
|
continue;
|
|
} else if (trsp->all_zones[n].has_triggers[ind][trig]) {
|
|
return (true);
|
|
} else if (trsp->all_zones[n].ip_as_ns &&
|
|
(((trig == LIBRPZ_TRIG_IP) &&
|
|
trsp->all_zones[n]
|
|
.has_triggers[ind]
|
|
[LIBRPZ_TRIG_NSIP]) ||
|
|
((trig == LIBRPZ_TRIG_NSIP) &&
|
|
trsp->all_zones[n]
|
|
.has_triggers[ind]
|
|
[LIBRPZ_TRIG_IP])))
|
|
{
|
|
return (true);
|
|
} else if (trsp->all_zones[n].qname_as_ns &&
|
|
(((trig == LIBRPZ_TRIG_QNAME) &&
|
|
trsp->all_zones[n].has_triggers
|
|
[ind][LIBRPZ_TRIG_NSDNAME]) ||
|
|
((trig == LIBRPZ_TRIG_NSDNAME) &&
|
|
trsp->all_zones[n]
|
|
.has_triggers[ind]
|
|
[LIBRPZ_TRIG_QNAME])))
|
|
{
|
|
return (true);
|
|
}
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
/* Special case of first base zone. */
|
|
if (trsp->rstack[0].result.dznum == 0 &&
|
|
(trig > trsp->rstack[0].result.trig))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
/* Otherwise check lower zones (of higher precedence). */
|
|
for (size_t n = 0; n <= (trsp->rstack[0].result.dznum); n++) {
|
|
if (!trsp->have_rd && !trsp->all_zones[n].not_recursive_only) {
|
|
continue;
|
|
} else if (trsp->all_zones[n].has_triggers[ind][trig]) {
|
|
return (true);
|
|
} else if (trsp->all_zones[n].ip_as_ns &&
|
|
(((trig == LIBRPZ_TRIG_IP) &&
|
|
trsp->all_zones[n]
|
|
.has_triggers[ind][LIBRPZ_TRIG_NSIP]) ||
|
|
((trig == LIBRPZ_TRIG_NSIP) &&
|
|
trsp->all_zones[n]
|
|
.has_triggers[ind][LIBRPZ_TRIG_IP])))
|
|
{
|
|
return (true);
|
|
} else if (trsp->all_zones[n].qname_as_ns &&
|
|
(((trig == LIBRPZ_TRIG_QNAME) &&
|
|
trsp->all_zones[n]
|
|
.has_triggers[ind][LIBRPZ_TRIG_NSDNAME]) ||
|
|
((trig == LIBRPZ_TRIG_NSDNAME) &&
|
|
trsp->all_zones[n]
|
|
.has_triggers[ind][LIBRPZ_TRIG_QNAME])))
|
|
{
|
|
return (true);
|
|
}
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
bool
|
|
trpz_rsp_rr(librpz_emsg_t *emsg, uint16_t *typep, uint16_t *classp,
|
|
uint32_t *ttlp, librpz_rr_t **rrp, librpz_result_t *result,
|
|
const uint8_t *qname, size_t qname_size, librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
trpz_result_t *last_result = NULL;
|
|
|
|
if (result == NULL) {
|
|
trpz_pemsg(emsg, "result was NULL");
|
|
return (false);
|
|
} else if (rsp == NULL) {
|
|
trpz_pemsg(emsg, "rsp was NULL");
|
|
return (false);
|
|
}
|
|
|
|
last_result = &(trsp->rstack[0]);
|
|
|
|
if (last_result->rridx < last_result->nrrs) {
|
|
trpz_rr_t *this_rr = &(last_result->rrs[last_result->rridx]);
|
|
|
|
if (classp != NULL) {
|
|
*classp = this_rr->class;
|
|
}
|
|
|
|
if (ttlp != NULL) {
|
|
*ttlp = 3600;
|
|
}
|
|
|
|
SET_IF_NOT_NULL(typep, this_rr->type);
|
|
|
|
if (rrp != NULL) {
|
|
uint8_t *copy_src = NULL, *nrdata = NULL;
|
|
size_t to_copy, needed;
|
|
|
|
copy_src = this_rr->rdata;
|
|
to_copy = this_rr->rdlength;
|
|
|
|
/* If there's CNAME wild card expansion */
|
|
if (qname != NULL && qname_size != 0 &&
|
|
(this_rr->type == ns_t_cname) &&
|
|
(this_rr->rdlength > 2))
|
|
{
|
|
if (this_rr->rdata[0] == 1 &&
|
|
this_rr->rdata[1] == '*')
|
|
{
|
|
char tmpexp[256] = { 0 },
|
|
tmpexp2[256] = { 0 }, tmpexp3[256];
|
|
|
|
wdns_domain_to_str(this_rr->rdata,
|
|
this_rr->rdlength,
|
|
tmpexp);
|
|
wdns_domain_to_str(qname, qname_size,
|
|
tmpexp2);
|
|
|
|
if (tmpexp2[strlen(tmpexp2) - 1] == '.')
|
|
{
|
|
tmpexp2[strlen(tmpexp2) - 1] =
|
|
0;
|
|
}
|
|
|
|
if (strncmp(tmpexp, "*.", 2) == 0) {
|
|
int nrd;
|
|
uint32_t n = snprintf(
|
|
tmpexp3,
|
|
sizeof(tmpexp3),
|
|
"%s.%s", tmpexp2,
|
|
&tmpexp[2]);
|
|
if (n > sizeof(tmpexp3)) {
|
|
trpz_pemsg(
|
|
emsg,
|
|
"%s truncated",
|
|
tmpexp3);
|
|
return (false);
|
|
}
|
|
nrd = wdns_str_to_name(
|
|
tmpexp3, &nrdata, 1);
|
|
if (nrd < 0) {
|
|
trpz_pemsg(
|
|
emsg,
|
|
"Error packing "
|
|
"domain");
|
|
return (false);
|
|
}
|
|
to_copy = nrd;
|
|
copy_src = nrdata;
|
|
}
|
|
}
|
|
}
|
|
|
|
needed = sizeof(**rrp) + to_copy;
|
|
|
|
*rrp = calloc(1, needed);
|
|
if (*rrp == NULL) {
|
|
trpz_pemsg(emsg, "calloc: %s", strerror(errno));
|
|
if (nrdata != NULL) {
|
|
free(nrdata);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
(*rrp)->type = htons(this_rr->type);
|
|
(*rrp)->class = htons(this_rr->class);
|
|
(*rrp)->ttl = htonl(this_rr->ttl);
|
|
(*rrp)->rdlength = htons(to_copy);
|
|
memmove((*rrp)->rdata, copy_src, to_copy);
|
|
if (nrdata != NULL) {
|
|
free(nrdata);
|
|
}
|
|
}
|
|
|
|
result->next_rr = this_rr->rrn;
|
|
trsp->rstack[0].result.next_rr = this_rr->rrn;
|
|
last_result->rridx++;
|
|
} else {
|
|
SET_IF_NOT_NULL(typep, ns_t_invalid);
|
|
|
|
if (rrp != NULL) {
|
|
*rrp = NULL;
|
|
}
|
|
|
|
result->next_rr = 0;
|
|
trsp->rstack[0].result.next_rr = 0;
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
trpz_rsp_forget_zone(librpz_emsg_t *emsg, librpz_cznum_t znum,
|
|
librpz_rsp_t *rsp) {
|
|
trpz_rsp_t *trsp = (trpz_rsp_t *)rsp;
|
|
|
|
if (znum >= trsp->num_zones) {
|
|
trpz_pemsg(emsg, "invalid zone number");
|
|
return (false);
|
|
} else if (trsp->all_zones[znum].forgotten) {
|
|
trpz_pemsg(emsg, "zone already forgotten");
|
|
return (false);
|
|
}
|
|
|
|
trsp->all_zones[znum].forgotten = true;
|
|
|
|
return (true);
|
|
}
|