mirror of
https://github.com/isc-projects/bind9.git
synced 2026-04-23 23:28:18 -04:00
spatch --sp-file cocci/set_if_not_null.spatch --use-gitgrep --dir "." --include-headers --in-place
1488 lines
31 KiB
C
1488 lines
31 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.
|
|
*/
|
|
|
|
#define _GNU_SOURCE 1
|
|
#include <arpa/inet.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <isc/atomic.h>
|
|
#include <isc/util.h>
|
|
|
|
#include "test-data.h"
|
|
|
|
const rpz_soa_t g_soa_record = { "a.rpz-ns.dns-nod.net",
|
|
"nod-admin.fsi.io",
|
|
12345,
|
|
3600,
|
|
1200,
|
|
604800,
|
|
60 };
|
|
|
|
int
|
|
wdns_str_to_name(const char *str, uint8_t **pbuf, bool downcase);
|
|
|
|
static char *
|
|
str_printf(const char *fmt, ...) {
|
|
va_list ap;
|
|
char tbuf[8192], *result = NULL;
|
|
|
|
va_start(ap, fmt);
|
|
vsnprintf(tbuf, sizeof(tbuf) - 1, fmt, ap);
|
|
tbuf[sizeof(tbuf) - 1] = 0;
|
|
va_end(ap);
|
|
|
|
result = strdup(tbuf);
|
|
if (result == NULL) {
|
|
perror("strdup");
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* Given a config-ready RPZ IP address, determine its family and normal
|
|
* canonical representation.
|
|
*/
|
|
int
|
|
get_address_info(const char *astr, int *pfamily, char *pbuf,
|
|
const char *optname, char **errp) {
|
|
char tmpc[512] = { 0 };
|
|
char *tok = NULL, *tptr = tmpc, *last_tok = NULL;
|
|
size_t lcount = 0, bcount = 0;
|
|
unsigned int prefix = 0, values[16] = { 0 }, hex_values[16] = { 0 };
|
|
bool is_ipv6 = false;
|
|
|
|
if (astr == NULL || pfamily == NULL || pbuf == NULL) {
|
|
return (-1);
|
|
}
|
|
|
|
strncpy(tmpc, astr, sizeof(tmpc) - 1);
|
|
|
|
while ((tok = strsep(&tptr, "."))) {
|
|
char *eptr = NULL;
|
|
unsigned long val;
|
|
|
|
lcount++;
|
|
last_tok = tok;
|
|
|
|
errno = 0;
|
|
val = strtoul(tok, &eptr, 10);
|
|
|
|
if (errno != 0 || *eptr != '\0') {
|
|
bool bad = false;
|
|
|
|
bcount++;
|
|
errno = 0;
|
|
eptr = NULL;
|
|
val = strtoul(tok, &eptr, 16);
|
|
|
|
if (errno || *eptr != '\0') {
|
|
if (strcmp(tok, "zz") == 0) {
|
|
val = ~0;
|
|
is_ipv6 = true;
|
|
} else {
|
|
bad = true;
|
|
}
|
|
}
|
|
|
|
if (!bad && (lcount > 1)) {
|
|
hex_values[lcount - 2] = val;
|
|
}
|
|
} else {
|
|
if (val > 255) {
|
|
bcount++;
|
|
}
|
|
|
|
if (lcount == 1) {
|
|
prefix = val;
|
|
} else if (lcount > 1) {
|
|
unsigned int hexval;
|
|
values[lcount - 2] = val;
|
|
|
|
/*
|
|
* All integer strings are valid hex
|
|
* strings, but decimal values are longer,
|
|
* so we have to check for overflow when
|
|
* reading as hex.
|
|
*/
|
|
errno = 0;
|
|
hexval = strtoul(tok, &eptr, 16);
|
|
if (errno != 0) {
|
|
return (-1);
|
|
}
|
|
hex_values[lcount - 2] = hexval;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (last_tok && (strncmp(last_tok, "rpz-", 4) == 0)) {
|
|
lcount--;
|
|
bcount--;
|
|
}
|
|
|
|
/* Not acceptable for either address family. */
|
|
if (lcount > 9) {
|
|
return (-1);
|
|
}
|
|
|
|
*pfamily = (!is_ipv6 && (lcount == 5)) ? AF_INET : AF_INET6;
|
|
|
|
/*
|
|
* For AF_INET we expect exactly 4 "good" (0<->255) octets and the
|
|
* subnet mask.
|
|
*/
|
|
if (*pfamily == AF_INET) {
|
|
if (prefix > 32) {
|
|
if (errp != NULL) {
|
|
*errp = str_printf(
|
|
"invalid rpz IP address \"%s\"; "
|
|
"invalid prefix length of %u",
|
|
(optname ? optname : astr), prefix);
|
|
}
|
|
|
|
return (-1);
|
|
} else if (bcount > 0) {
|
|
return (-1);
|
|
}
|
|
|
|
sprintf(pbuf, "%u.%u.%u.%u", values[3], values[2], values[1],
|
|
values[0]);
|
|
} else {
|
|
size_t n;
|
|
|
|
if (prefix > 128) {
|
|
if (errp != NULL) {
|
|
*errp = str_printf(
|
|
"invalid rpz IP address \"%s\"; "
|
|
"invalid prefix length of %u",
|
|
(optname ? optname : astr), prefix);
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
*pbuf = 0;
|
|
|
|
/*
|
|
* Walk the values backward. Account for :: and discard
|
|
* chunks > 2 octets.
|
|
*/
|
|
for (n = lcount - 1; n > 0; n--) {
|
|
if (hex_values[n - 1] == ~0U) {
|
|
strcat(pbuf, ":");
|
|
} else {
|
|
if (hex_values[n - 1] > 0xffff) {
|
|
return (-1);
|
|
} else if (n > 1) {
|
|
sprintf(&pbuf[strlen(pbuf)],
|
|
"%x:", hex_values[n - 1]);
|
|
} else {
|
|
sprintf(&pbuf[strlen(pbuf)], "%x",
|
|
hex_values[n - 1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
rpz_soa_t *
|
|
parse_serial(unsigned char *rdata, size_t rdlen) {
|
|
rpz_soa_t *result = NULL;
|
|
char dname[WDNS_PRESLEN_NAME];
|
|
size_t mlen, rlen;
|
|
uint32_t *uptr = NULL;
|
|
|
|
result = calloc(1, sizeof(*result));
|
|
if (result == NULL) {
|
|
perror("calloc");
|
|
return (NULL);
|
|
}
|
|
|
|
mlen = wdns_domain_to_str(rdata, rdlen, dname);
|
|
result->mname = strdup(dname);
|
|
rlen = wdns_domain_to_str(rdata + mlen, rdlen - mlen, dname);
|
|
result->rname = strdup(dname);
|
|
uptr = (uint32_t *)(rdata + mlen + rlen);
|
|
result->serial = ntohl(*uptr);
|
|
uptr++;
|
|
result->refresh = ntohl(*uptr);
|
|
uptr++;
|
|
result->retry = ntohl(*uptr);
|
|
uptr++;
|
|
result->expire = ntohl(*uptr);
|
|
uptr++;
|
|
result->minimum = ntohl(*uptr);
|
|
|
|
return (result);
|
|
}
|
|
|
|
size_t
|
|
wdns_domain_to_str(const uint8_t *src, size_t src_len, char *dst) {
|
|
size_t bytes_read = 0;
|
|
size_t bytes_remaining = src_len;
|
|
uint8_t oclen;
|
|
|
|
if (src == NULL) {
|
|
return (0);
|
|
}
|
|
|
|
oclen = *src;
|
|
while (bytes_remaining > 0 && oclen != 0) {
|
|
src++;
|
|
bytes_remaining--;
|
|
|
|
bytes_read += oclen + 1 /* length octet */;
|
|
|
|
while (oclen-- && bytes_remaining > 0) {
|
|
uint8_t c = *src++;
|
|
bytes_remaining--;
|
|
|
|
if (c == '.' || c == '\\') {
|
|
*dst++ = '\\';
|
|
*dst++ = c;
|
|
} else if (c >= '!' && c <= '~') {
|
|
*dst++ = c;
|
|
} else {
|
|
snprintf(dst, 5, "\\%.3d", c);
|
|
dst += 4;
|
|
}
|
|
}
|
|
*dst++ = '.';
|
|
oclen = *src;
|
|
}
|
|
if (bytes_read == 0) {
|
|
*dst++ = '.';
|
|
}
|
|
bytes_read++;
|
|
|
|
*dst = '\0';
|
|
return ((bytes_read));
|
|
}
|
|
|
|
/* Add parsed update specification to maintained list of nodes. */
|
|
static trpz_result_t *
|
|
apply_update_to_set(trpz_result_t **results, size_t *pnresults,
|
|
trpz_zone_t **pzones, const char *node, size_t zidx,
|
|
uint32_t ttl, librpz_trig_t trigger, librpz_policy_t policy,
|
|
int *modified, unsigned long flags, char **errp) {
|
|
size_t n;
|
|
int family = 0;
|
|
|
|
UNUSED(flags);
|
|
|
|
*modified = 0;
|
|
|
|
switch (trigger) {
|
|
case LIBRPZ_TRIG_QNAME:
|
|
case LIBRPZ_TRIG_NSDNAME:
|
|
(*pzones)[zidx].has_triggers[0][trigger] = 1;
|
|
break;
|
|
case LIBRPZ_TRIG_CLIENT_IP:
|
|
case LIBRPZ_TRIG_IP:
|
|
case LIBRPZ_TRIG_NSIP: {
|
|
char abuf[128];
|
|
|
|
if (get_address_info(node, &family, abuf, NULL, errp) < 0) {
|
|
fprintf(stderr,
|
|
"Error in determining IP address type: %s\n",
|
|
node);
|
|
return (NULL);
|
|
} else if (family == AF_INET) {
|
|
(*pzones)[zidx].has_triggers[0][trigger] = 1;
|
|
} else {
|
|
(*pzones)[zidx].has_triggers[1][trigger] = 1;
|
|
}
|
|
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
for (n = 0; n < *pnresults; n++) {
|
|
trpz_result_t *rptr = &((*results)[n]);
|
|
|
|
if (rptr->result.cznum != zidx) {
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(rptr->dname, node)) {
|
|
if (rptr->result.trig == trigger &&
|
|
rptr->result.policy == policy && rptr->ttl == ttl)
|
|
{
|
|
return (rptr);
|
|
}
|
|
|
|
rptr->result.trig = trigger;
|
|
rptr->result.policy = policy;
|
|
rptr->result.zpolicy = policy;
|
|
rptr->ttl = ttl;
|
|
*modified = 1;
|
|
return (rptr);
|
|
}
|
|
}
|
|
|
|
/* No match. Instead, append. */
|
|
(*pnresults)++;
|
|
|
|
*results = realloc(*results, (*pnresults * sizeof(**results)));
|
|
if (*results == NULL) {
|
|
perror("realloc");
|
|
return (NULL);
|
|
}
|
|
|
|
memset(&((*results)[*pnresults - 1]), 0, sizeof(**results));
|
|
(*results)[*pnresults - 1].dname = strdup(node);
|
|
(*results)[*pnresults - 1].ttl = ttl;
|
|
(*results)[*pnresults - 1].result.trig = trigger;
|
|
(*results)[*pnresults - 1].result.policy = policy;
|
|
(*results)[*pnresults - 1].result.zpolicy = policy;
|
|
(*results)[*pnresults - 1].result.cznum = zidx;
|
|
(*results)[*pnresults - 1].result.dznum = zidx;
|
|
(*results)[*pnresults - 1].result.log = 1;
|
|
(*results)[*pnresults - 1].poverride = policy;
|
|
(*results)[*pnresults - 1].match_trig = trigger;
|
|
|
|
if (family == AF_INET6) {
|
|
(*results)[*pnresults - 1].flags |= NODE_FLAG_IPV6_ADDRESS;
|
|
}
|
|
|
|
*modified = 1;
|
|
return ((&((*results)[*pnresults - 1])));
|
|
}
|
|
|
|
/*
|
|
* Add a parsed RR value that is maintained in conjunction with record policy
|
|
* items.
|
|
*/
|
|
static int
|
|
add_other_rr(trpz_result_t *node, const char *rrtype, const char *val,
|
|
uint32_t ttl, int *modified) {
|
|
trpz_rr_t nrec = { 0 };
|
|
size_t n;
|
|
static atomic_uint_fast32_t rrn = 1;
|
|
|
|
*modified = 0;
|
|
|
|
nrec.class = ns_c_in;
|
|
nrec.ttl = ttl;
|
|
nrec.rrn = atomic_fetch_add_relaxed(&rrn, 1);
|
|
|
|
if (!strcasecmp(rrtype, "A")) {
|
|
uint32_t addr;
|
|
|
|
if (inet_pton(AF_INET, val, &addr) != 1) {
|
|
fprintf(stderr,
|
|
"Error determining policy record IPv4 address: "
|
|
"%s\n",
|
|
val);
|
|
return (-1);
|
|
}
|
|
|
|
nrec.type = ns_t_a;
|
|
nrec.rdlength = sizeof(uint32_t);
|
|
|
|
nrec.rdata = malloc(nrec.rdlength);
|
|
if (nrec.rdata == NULL) {
|
|
perror("malloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
memmove(nrec.rdata, &addr, nrec.rdlength);
|
|
} else if (!strcasecmp(rrtype, "AAAA")) {
|
|
char addr[16] = { 0 };
|
|
|
|
if (inet_pton(AF_INET6, val, addr) != 1) {
|
|
fprintf(stderr,
|
|
"Error determining policy record IPv6 address: "
|
|
"%s\n",
|
|
val);
|
|
return (-1);
|
|
}
|
|
|
|
nrec.type = ns_t_aaaa;
|
|
nrec.rdlength = sizeof(addr);
|
|
|
|
nrec.rdata = malloc(nrec.rdlength);
|
|
if (nrec.rdata == NULL) {
|
|
perror("malloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
memmove(nrec.rdata, addr, nrec.rdlength);
|
|
} else if (!strcasecmp(rrtype, "TXT")) {
|
|
nrec.type = ns_t_txt;
|
|
nrec.rdlength = 1 + strlen(val);
|
|
|
|
nrec.rdata = calloc(nrec.rdlength, 1);
|
|
if (nrec.rdata == NULL) {
|
|
perror("calloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
nrec.rdata[0] = nrec.rdlength - 1;
|
|
memmove(&(nrec.rdata[1]), val, nrec.rdlength - 1);
|
|
} else if (!strcasecmp(rrtype, "CNAME")) {
|
|
int ret;
|
|
|
|
nrec.type = ns_t_cname;
|
|
ret = wdns_str_to_name(val, &(nrec.rdata), 1);
|
|
|
|
if (ret <= 0) {
|
|
fprintf(stderr,
|
|
"Error processing CNAME policy record data "
|
|
"(%d)!\n",
|
|
ret);
|
|
return (-1);
|
|
}
|
|
|
|
nrec.rdlength = ret;
|
|
} else if (!strcasecmp(rrtype, "DNAME")) {
|
|
int ret;
|
|
|
|
nrec.type = ns_t_dname;
|
|
ret = wdns_str_to_name(val, &(nrec.rdata), 1);
|
|
|
|
if (ret <= 0) {
|
|
fprintf(stderr,
|
|
"Error processing DNAME policy record data "
|
|
"(%d)!\n",
|
|
ret);
|
|
return (-1);
|
|
}
|
|
|
|
nrec.rdlength = ret;
|
|
} else {
|
|
fprintf(stderr,
|
|
"Error: unsupported policy record type: \"%s\"\n",
|
|
rrtype);
|
|
return (-1);
|
|
}
|
|
|
|
for (n = 0; n < node->nrrs; n++) {
|
|
trpz_rr_t *rptr = &(node->rrs[n]);
|
|
|
|
/* Same thing. Don't replace. */
|
|
if (rptr->type == nrec.type && rptr->class == nrec.class &&
|
|
rptr->ttl == nrec.ttl && rptr->rdlength == nrec.rdlength &&
|
|
!memcmp(rptr->rdata, nrec.rdata, nrec.rdlength))
|
|
{
|
|
free(nrec.rdata);
|
|
return (n + 1);
|
|
}
|
|
}
|
|
|
|
node->nrrs++;
|
|
|
|
node->rrs = realloc(node->rrs, (node->nrrs * sizeof(*(node->rrs))));
|
|
if (node->rrs == NULL) {
|
|
perror("realloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
memset(&(node->rrs[node->nrrs - 1]), 0, sizeof(node->rrs[0]));
|
|
node->rrs[node->nrrs - 1] = nrec;
|
|
*modified = 1;
|
|
|
|
return (node->nrrs);
|
|
}
|
|
|
|
void
|
|
reverse_labels(const char *str, char *pbuf) {
|
|
const char *sptr = str, *end = NULL;
|
|
|
|
if (sptr == NULL || *sptr == 0) {
|
|
return;
|
|
}
|
|
|
|
sptr += (strlen(sptr) - 1);
|
|
end = sptr + 1;
|
|
*pbuf = 0;
|
|
|
|
if (*sptr == '.') {
|
|
sptr--;
|
|
end--;
|
|
}
|
|
|
|
while (sptr >= str) {
|
|
if ((*sptr != '.') && (sptr != str)) {
|
|
sptr--;
|
|
continue;
|
|
}
|
|
|
|
if (sptr == str) {
|
|
strncat(pbuf, sptr, (end - sptr));
|
|
break;
|
|
}
|
|
|
|
strncat(pbuf, sptr + 1, (end - (sptr + 1)));
|
|
strcat(pbuf, ".");
|
|
end = sptr--;
|
|
}
|
|
|
|
if (pbuf[strlen(pbuf) - 1] == '.') {
|
|
pbuf[strlen(pbuf) - 1] = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Parse trailing zone options as specified in a cstr line */
|
|
unsigned long
|
|
parse_zone_options(const char *str) {
|
|
char tmpstr[8192] = { 0 };
|
|
char *tok = NULL, *sptr = NULL;
|
|
unsigned long result = 0;
|
|
|
|
if (str == NULL || *str == 0) {
|
|
return (0);
|
|
}
|
|
|
|
strncpy(tmpstr, str, sizeof(tmpstr) - 1);
|
|
|
|
tok = strtok_r(tmpstr, " ", &sptr);
|
|
|
|
while (tok) {
|
|
if (!strcasecmp(tok, "policy")) {
|
|
tok = strtok_r(NULL, " ", &sptr);
|
|
if (tok == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (!strcasecmp(tok, "passthru")) {
|
|
result |= ZOPT_POLICY_PASSTHRU;
|
|
} else if (!strcasecmp(tok, "drop")) {
|
|
result |= ZOPT_POLICY_DROP;
|
|
} else if (!strcasecmp(tok, "tcp-only")) {
|
|
result |= ZOPT_POLICY_TCP_ONLY;
|
|
} else if (!strcasecmp(tok, "nxdomain")) {
|
|
result |= ZOPT_POLICY_NXDOMAIN;
|
|
} else if (!strcasecmp(tok, "nodata")) {
|
|
result |= ZOPT_POLICY_NODATA;
|
|
} else if (!strcasecmp(tok, "given")) {
|
|
result |= ZOPT_POLICY_GIVEN;
|
|
} else if (!strcasecmp(tok, "disabled")) {
|
|
result |= ZOPT_POLICY_DISABLED;
|
|
} else if (!strcasecmp(tok, "no-op")) {
|
|
;
|
|
}
|
|
} else {
|
|
if (!strcasecmp(tok, "max-policy-ttl")) {
|
|
tok = strtok_r(NULL, " ", &sptr);
|
|
if (tok == NULL) {
|
|
break;
|
|
}
|
|
} else if (!strcasecmp(tok, "recursive-only")) {
|
|
tok = strtok_r(NULL, " ", &sptr);
|
|
if (tok == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (!strcasecmp(tok, "yes")) {
|
|
result |= ZOPT_RECURSIVE_ONLY;
|
|
} else if (!strcasecmp(tok, "no")) {
|
|
result |= ZOPT_NOT_RECURSIVE_ONLY;
|
|
}
|
|
|
|
} else {
|
|
if (!strcasecmp(tok, "qname-as-ns")) {
|
|
tok = strtok_r(NULL, " ", &sptr);
|
|
if (tok == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (!strcasecmp(tok, "yes")) {
|
|
result |= ZOPT_QNAME_AS_NS;
|
|
}
|
|
|
|
} else if (!strcasecmp(tok, "ip-as-ns")) {
|
|
tok = strtok_r(NULL, " ", &sptr);
|
|
if (tok == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (!strcasecmp(tok, "yes")) {
|
|
result |= ZOPT_IP_AS_NS;
|
|
}
|
|
|
|
} else if (!strcasecmp(tok,
|
|
"qname-wait-recurse"))
|
|
{
|
|
tok = strtok_r(NULL, " ", &sptr);
|
|
if (tok == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (!strcasecmp(tok, "no")) {
|
|
result |=
|
|
ZOPT_NO_QNAME_WAIT_RECURSE;
|
|
}
|
|
|
|
} else if (!strcasecmp(tok,
|
|
"nsip-wait-recurse"))
|
|
{
|
|
tok = strtok_r(NULL, " ", &sptr);
|
|
if (tok == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (!strcasecmp(tok, "no")) {
|
|
result |=
|
|
ZOPT_NO_NSIP_WAIT_RECURSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tok = strtok_r(NULL, " ", &sptr);
|
|
}
|
|
|
|
/* LIBRPZ_POLICY_CNAME, */
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* Parse an update string and attempt to add any relevant data to the node
|
|
* and policy RR tables.
|
|
*/
|
|
int
|
|
apply_update(const char *updstr, trpz_result_t **presults, size_t *pnresults,
|
|
trpz_zone_t **pzones, size_t *pnzones, int is_static,
|
|
unsigned long flags, char **errp) {
|
|
trpz_result_t *res = NULL;
|
|
char cmdbuf[64] = { 0 }, nodebuf[256] = { 0 }, rrbuf[32] = { 0 },
|
|
databuf[256] = { 0 };
|
|
char *nend = NULL;
|
|
librpz_policy_t policy = LIBRPZ_POLICY_UNDEFINED;
|
|
librpz_trig_t trig = LIBRPZ_TRIG_QNAME;
|
|
unsigned int ttl;
|
|
ssize_t n, zidx = -1;
|
|
size_t ndlen = 0, last_matchlen = 0;
|
|
int nfield, zupd = 0;
|
|
|
|
nfield = sscanf(updstr, "%63s %255s %u %31s %255s", cmdbuf, nodebuf,
|
|
&ttl, rrbuf, databuf);
|
|
if (nfield < 1) {
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Special case for handling zone additions; here the 'ttl' field
|
|
* becomes a serial.
|
|
*/
|
|
if (!strcasecmp(cmdbuf, "zone")) {
|
|
trpz_zone_t *zptr = NULL;
|
|
bool qname_as_ns = false, ip_as_ns = false,
|
|
not_recursive_only = false, do_inc = false;
|
|
bool no_qname_as_ns = false, no_ip_as_ns = false,
|
|
recursive_only = false, no_nsip_wait_recurse = false;
|
|
|
|
if (nfield < 3) {
|
|
return (-1);
|
|
}
|
|
|
|
if (!strcasecmp(rrbuf, "qname_as_ns")) {
|
|
qname_as_ns = true;
|
|
} else if (!strcasecmp(rrbuf, "ip_as_ns")) {
|
|
ip_as_ns = true;
|
|
} else if (!strcasecmp(rrbuf, "not_recursive_only")) {
|
|
not_recursive_only = true;
|
|
} else if (!strcasecmp(rrbuf, "inc")) {
|
|
do_inc = true;
|
|
} else if (!strcasecmp(rrbuf, "no_qname_as_ns")) {
|
|
no_qname_as_ns = true;
|
|
} else if (!strcasecmp(rrbuf, "no_ip_as_ns")) {
|
|
no_ip_as_ns = true;
|
|
} else if (!strcasecmp(rrbuf, "recursive_only")) {
|
|
recursive_only = true;
|
|
} else if (!strcasecmp(rrbuf, "no_nsip_wait_recurse")) {
|
|
no_nsip_wait_recurse = true;
|
|
}
|
|
|
|
if (flags & ZOPT_RECURSIVE_ONLY) {
|
|
recursive_only = true;
|
|
not_recursive_only = false;
|
|
} else if (flags & ZOPT_NOT_RECURSIVE_ONLY) {
|
|
recursive_only = false;
|
|
not_recursive_only = true;
|
|
}
|
|
|
|
if (flags & ZOPT_NO_NSIP_WAIT_RECURSE) {
|
|
no_nsip_wait_recurse = true;
|
|
}
|
|
|
|
for (n = 0; (size_t)n < *pnzones; n++) {
|
|
if (!strcmp((*pzones)[n].name, nodebuf)) {
|
|
/*
|
|
* Force override of serial. But only if
|
|
* serial is non-zero.
|
|
*/
|
|
if (ttl) {
|
|
if (do_inc) {
|
|
(*pzones)[n].serial += ttl;
|
|
} else {
|
|
(*pzones)[n].serial = ttl;
|
|
}
|
|
}
|
|
|
|
(*pzones)[n].has_update = 0;
|
|
|
|
if (qname_as_ns) {
|
|
(*pzones)[n].qname_as_ns = true;
|
|
}
|
|
|
|
if (ip_as_ns) {
|
|
(*pzones)[n].ip_as_ns = true;
|
|
}
|
|
|
|
if (no_qname_as_ns) {
|
|
(*pzones)[n].qname_as_ns = false;
|
|
}
|
|
|
|
if (no_ip_as_ns) {
|
|
(*pzones)[n].ip_as_ns = false;
|
|
}
|
|
|
|
if (not_recursive_only) {
|
|
(*pzones)[n].not_recursive_only = true;
|
|
}
|
|
|
|
if (recursive_only) {
|
|
(*pzones)[n].not_recursive_only = false;
|
|
}
|
|
|
|
if (flags & ZOPT_NO_QNAME_WAIT_RECURSE) {
|
|
(*pzones)[n].no_qname_wait_recurse =
|
|
true;
|
|
}
|
|
|
|
if (no_nsip_wait_recurse) {
|
|
(*pzones)[n].no_nsip_wait_recurse =
|
|
true;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
(*pnzones)++;
|
|
|
|
*pzones = realloc(*pzones, (*pnzones * sizeof(**pzones)));
|
|
if (*pzones == NULL) {
|
|
perror("realloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
zptr = &(*pzones)[*pnzones - 1];
|
|
*zptr = (trpz_zone_t){
|
|
.serial = ttl,
|
|
.qname_as_ns = qname_as_ns,
|
|
.ip_as_ns = ip_as_ns,
|
|
.flags = flags,
|
|
.not_recursive_only = not_recursive_only,
|
|
};
|
|
|
|
if (qname_as_ns) {
|
|
(*pzones)[n].qname_as_ns = true;
|
|
}
|
|
|
|
if (ip_as_ns) {
|
|
(*pzones)[n].ip_as_ns = true;
|
|
}
|
|
|
|
if (flags & ZOPT_NO_QNAME_WAIT_RECURSE) {
|
|
(*pzones)[n].no_qname_wait_recurse = true;
|
|
}
|
|
|
|
if (no_nsip_wait_recurse) {
|
|
(*pzones)[n].no_nsip_wait_recurse = true;
|
|
}
|
|
|
|
strncpy(zptr->name, nodebuf, LIBRPZ_MAXDOMAIN + 1);
|
|
if (zptr->name[strlen(zptr->name) - 1] == '.') {
|
|
zptr->name[strlen(zptr->name) - 1] = 0;
|
|
}
|
|
|
|
return (0);
|
|
} else if (nfield != 5) {
|
|
return (-1);
|
|
}
|
|
|
|
if (strcasecmp(cmdbuf, "add")) {
|
|
fprintf(stderr, "Warning: only update add action is currently "
|
|
"supported!\n");
|
|
return (-1);
|
|
}
|
|
|
|
if (!strcasecmp(rrbuf, "A")) {
|
|
policy = LIBRPZ_POLICY_RECORD;
|
|
} else if (!strcasecmp(rrbuf, "CNAME")) {
|
|
if (!strcmp(databuf, ".")) {
|
|
policy = LIBRPZ_POLICY_NXDOMAIN;
|
|
} else if (!strcmp(databuf, "*.")) {
|
|
policy = LIBRPZ_POLICY_NODATA;
|
|
} else if (!strcasecmp(databuf, "rpz-passthru.")) {
|
|
policy = LIBRPZ_POLICY_PASSTHRU;
|
|
} else if (!strcasecmp(databuf, "rpz-drop.")) {
|
|
policy = LIBRPZ_POLICY_DROP;
|
|
} else if (!strcasecmp(databuf, "rpz-tcp-only.")) {
|
|
policy = LIBRPZ_POLICY_TCP_ONLY;
|
|
} else {
|
|
policy = LIBRPZ_POLICY_RECORD;
|
|
}
|
|
|
|
} else if (!strcasecmp(rrbuf, "TXT")) {
|
|
char *ftext = NULL;
|
|
|
|
ftext = strstr(updstr, databuf);
|
|
if (ftext == NULL) {
|
|
fprintf(stderr, "Error parsing TXT record: \"%s\"\n",
|
|
updstr);
|
|
return (-1);
|
|
}
|
|
|
|
if (*ftext == '"') {
|
|
*ftext++ = 0;
|
|
|
|
if (ftext[strlen(ftext) - 1] == '"') {
|
|
ftext[strlen(ftext) - 1] = 0;
|
|
}
|
|
}
|
|
|
|
strncpy(databuf, ftext, sizeof(databuf));
|
|
databuf[sizeof(databuf) - 1] = 0;
|
|
policy = LIBRPZ_POLICY_RECORD;
|
|
} else if (!strcasecmp(rrbuf, "DNAME")) {
|
|
policy = LIBRPZ_POLICY_RECORD;
|
|
} else if (!strcasecmp(rrbuf, "AAAA")) {
|
|
policy = LIBRPZ_POLICY_RECORD;
|
|
} else {
|
|
fprintf(stderr,
|
|
"Warning: target \"%s\" is not currently supported!\n",
|
|
rrbuf);
|
|
return (-1);
|
|
}
|
|
|
|
if (policy == LIBRPZ_POLICY_UNDEFINED) {
|
|
fprintf(stderr, "Error: could not determine appropriate policy "
|
|
"for update!\n");
|
|
return (-1);
|
|
}
|
|
|
|
for (n = 0; (size_t)n < *pnzones; n++) {
|
|
const char *zptr = nodebuf;
|
|
size_t cmplen;
|
|
|
|
zptr += strlen(zptr) - 1;
|
|
|
|
if (*zptr == '.') {
|
|
zptr--;
|
|
}
|
|
|
|
cmplen = strlen((*pzones)[n].name);
|
|
|
|
if ((*pzones)[n].name[cmplen - 1] == '.') {
|
|
cmplen--;
|
|
}
|
|
|
|
zptr -= (cmplen - 1);
|
|
|
|
if ((zptr <= nodebuf) || (*(zptr - 1) != '.')) {
|
|
continue;
|
|
}
|
|
|
|
if (!strncmp((*pzones)[n].name, zptr, cmplen)) {
|
|
/*
|
|
* We don't break immediately after a match because
|
|
* there might be a better one yet.
|
|
*/
|
|
if (cmplen > last_matchlen) {
|
|
ndlen = strlen(nodebuf) - cmplen;
|
|
|
|
if (nodebuf[strlen(nodebuf) - 1] == '.') {
|
|
ndlen--;
|
|
}
|
|
|
|
/*
|
|
* Account for the period between the node name
|
|
* and zone name.
|
|
*/
|
|
ndlen--;
|
|
zidx = n;
|
|
last_matchlen = cmplen;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (memmem(nodebuf, ndlen, ".rpz-", 5)) {
|
|
char *tptr = nodebuf + ndlen - 1;
|
|
size_t slen;
|
|
|
|
while (strncmp(tptr, ".rpz-", 5)) {
|
|
tptr--;
|
|
}
|
|
|
|
slen = nodebuf + ndlen - tptr;
|
|
nend = tptr;
|
|
|
|
if (slen == 7 && !memcmp(tptr, ".rpz-ip", 7)) {
|
|
trig = LIBRPZ_TRIG_IP;
|
|
} else if (slen == 9 && !memcmp(tptr, ".rpz-nsip", 9)) {
|
|
trig = LIBRPZ_TRIG_NSIP;
|
|
} else if (slen == 14 && !memcmp(tptr, ".rpz-client-ip", 14)) {
|
|
trig = LIBRPZ_TRIG_CLIENT_IP;
|
|
} else if (slen == 12 && !memcmp(tptr, ".rpz-nsdname", 12)) {
|
|
trig = LIBRPZ_TRIG_NSDNAME;
|
|
} else {
|
|
fprintf(stderr, "Warning: unknown suffix \"%s\"\n",
|
|
tptr);
|
|
nend = NULL;
|
|
}
|
|
|
|
/* We saved the trigger value, so shave that part off. */
|
|
*tptr = 0;
|
|
}
|
|
|
|
if (zidx == -1) {
|
|
return (0);
|
|
}
|
|
|
|
nodebuf[ndlen] = 0;
|
|
|
|
/*
|
|
* The original, deprecated PASSTHRU encoding of a CNAME pointing
|
|
* to the trigger QNAME might still be in use in local, private
|
|
* policy zones, and so it is still recognized by RPZ subscriber
|
|
* implementations as of 2016.
|
|
*/
|
|
if ((policy == LIBRPZ_POLICY_RECORD) && !strcasecmp(rrbuf, "cname")) {
|
|
char tmpname[512] = { 0 };
|
|
|
|
strncpy(tmpname, databuf, sizeof(tmpname) - 1);
|
|
|
|
if ((nodebuf[strlen(nodebuf) - 1] == '.') &&
|
|
(tmpname[strlen(tmpname) - 1] != '.'))
|
|
{
|
|
size_t tlen = strlen(tmpname);
|
|
|
|
tmpname[tlen] = '.';
|
|
tmpname[tlen + 1] = 0;
|
|
} else if ((nodebuf[strlen(nodebuf) - 1] != '.') &&
|
|
(tmpname[strlen(tmpname) - 1] == '.'))
|
|
{
|
|
tmpname[strlen(tmpname) - 1] = 0;
|
|
}
|
|
|
|
/* A special case of PASSTHRU (with trailing characters) */
|
|
if (nend != NULL &&
|
|
(strlen(databuf) == (size_t)(nend - nodebuf)) &&
|
|
!strncmp(databuf, nodebuf, (nend - nodebuf)))
|
|
{
|
|
policy = LIBRPZ_POLICY_PASSTHRU;
|
|
}
|
|
|
|
if (!strcmp(nodebuf, tmpname)) {
|
|
policy = LIBRPZ_POLICY_PASSTHRU;
|
|
}
|
|
}
|
|
|
|
res = apply_update_to_set(presults, pnresults, pzones, nodebuf, zidx,
|
|
ttl, trig, policy, &zupd, flags, errp);
|
|
|
|
if (res) {
|
|
if (zupd && !is_static) {
|
|
(*pzones)[zidx].has_update = 1;
|
|
} else if (is_static) {
|
|
res->flags |= NODE_FLAG_STATIC_DATA;
|
|
}
|
|
|
|
if (policy == LIBRPZ_POLICY_RECORD) {
|
|
/*
|
|
* Policy/RR change does not seem to prompt zone
|
|
* serial increment. (has_update)
|
|
*/
|
|
if (add_other_rr(res, rrbuf, databuf, ttl, &zupd) < 0) {
|
|
fprintf(stderr,
|
|
"Error: could not add policy record %s "
|
|
"/ %s\n",
|
|
rrbuf, databuf);
|
|
return (-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* XXX: memory leak. Also, does not properly preserve "static" node entries,
|
|
* as envisioned.
|
|
*/
|
|
static void
|
|
free_nodes(trpz_result_t **presults, size_t *pnresults) {
|
|
size_t n, tot;
|
|
|
|
if (presults == NULL || *presults == NULL) {
|
|
if (pnresults != NULL) {
|
|
*pnresults = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
tot = *pnresults;
|
|
|
|
for (n = tot; n > 0; n--) {
|
|
trpz_result_t *res = &((*presults)[n - 1]);
|
|
size_t m;
|
|
|
|
if (res->canonical != NULL) {
|
|
free(res->canonical);
|
|
}
|
|
|
|
if (res->dname != NULL) {
|
|
free(res->dname);
|
|
}
|
|
|
|
for (m = 0; m < res->nrrs; m++) {
|
|
if (res->rrs[m].rdata != NULL) {
|
|
free(res->rrs[m].rdata);
|
|
}
|
|
}
|
|
|
|
if (res->rrs != NULL) {
|
|
free(res->rrs);
|
|
}
|
|
}
|
|
|
|
free(*presults);
|
|
*presults = NULL;
|
|
*pnresults = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Perform only sanity checking on a data file's contents.
|
|
*
|
|
* Note that this function only really exists to facilitate the logging of error
|
|
* messages that may be expected to occur upon encounter with certain invalid
|
|
* node data in unit tests.
|
|
*
|
|
* fname is the pathname of the data file to be checked.
|
|
* errp is a pointer to an error string that may be set if this function fails.
|
|
* It is the responsibility of the caller to free this pointer if it is returned
|
|
* non-NULL.
|
|
*
|
|
* This function returns 0 on success, or -1 on failure, possibly setting *errp
|
|
* on failure.
|
|
*/
|
|
int
|
|
sanity_check_data_file(const char *fname, char **errp) {
|
|
FILE *f = NULL;
|
|
int result = -1;
|
|
|
|
SET_IF_NOT_NULL(errp, NULL);
|
|
|
|
f = fopen(fname, "r");
|
|
if (f == NULL) {
|
|
fprintf(stderr, "couldn't sanity check %s\n", fname);
|
|
perror("fopen");
|
|
return (-1);
|
|
}
|
|
|
|
while (!feof(f)) {
|
|
char line[1024] = { 0 }, cmdbuf[64] = { 0 },
|
|
nodebuf[256] = { 0 }, rrbuf[32] = { 0 },
|
|
databuf[256] = { 0 };
|
|
char *lptr = line;
|
|
int nfield;
|
|
unsigned int ttl;
|
|
|
|
if (!fgets(line, sizeof(line) - 1, f)) {
|
|
break;
|
|
}
|
|
|
|
if (!line[0]) {
|
|
continue;
|
|
}
|
|
|
|
if (line[strlen(line) - 1] == '\n') {
|
|
line[strlen(line) - 1] = 0;
|
|
}
|
|
|
|
if (!line[0] || line[0] == ';') {
|
|
continue;
|
|
}
|
|
|
|
while (*lptr && !isspace(*lptr)) {
|
|
lptr++;
|
|
}
|
|
|
|
*lptr++ = 0;
|
|
|
|
if (!strcasecmp(line, "server") || !strcasecmp(line, "send") ||
|
|
!strcasecmp(line, "wipe") ||
|
|
!strcasecmp(line, "rollback") ||
|
|
!strcasecmp(line, "restart"))
|
|
{
|
|
continue;
|
|
} else if (strcasecmp(line, "static") &&
|
|
strcasecmp(line, "update"))
|
|
{
|
|
if (errp != NULL) {
|
|
*errp = str_printf("Found unknown instruction "
|
|
"directive: \"%s\"\n",
|
|
line);
|
|
}
|
|
|
|
goto out;
|
|
}
|
|
|
|
while (isspace(*lptr)) {
|
|
lptr++;
|
|
}
|
|
|
|
nfield = sscanf(lptr, "%63s %255s %u %31s %255s", cmdbuf,
|
|
nodebuf, &ttl, rrbuf, databuf);
|
|
|
|
/* We don't care about checking zones - only node entries. */
|
|
if (nfield != 5) {
|
|
continue;
|
|
}
|
|
|
|
if (strcasecmp(cmdbuf, "add")) {
|
|
continue;
|
|
}
|
|
|
|
if (strcasecmp(rrbuf, "A") && strcasecmp(rrbuf, "CNAME") &&
|
|
strcasecmp(rrbuf, "TXT") && strcasecmp(rrbuf, "DNAME") &&
|
|
strcasecmp(rrbuf, "AAAA"))
|
|
{
|
|
if (errp != NULL) {
|
|
*errp = str_printf("Target \"%s\" is not "
|
|
"currently supported!\n",
|
|
rrbuf);
|
|
}
|
|
|
|
goto out;
|
|
}
|
|
|
|
if (strstr(nodebuf, ".rpz-")) {
|
|
char abuf[64], tmpname[512];
|
|
char *tptr = nodebuf;
|
|
size_t slen;
|
|
int family;
|
|
|
|
while (strncmp(tptr, ".rpz-", 5)) {
|
|
tptr++;
|
|
}
|
|
|
|
slen = nodebuf + strlen(nodebuf) - tptr;
|
|
|
|
if (!(slen >= 8 && !memcmp(tptr, ".rpz-ip.", 8)) &&
|
|
!(slen >= 10 && !memcmp(tptr, ".rpz-nsip.", 10)) &&
|
|
!(slen >= 15 &&
|
|
!memcmp(tptr, ".rpz-client-ip.", 15)) &&
|
|
!(slen >= 13 && !memcmp(tptr, ".rpz-nsdname.", 13)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
strncpy(tmpname, nodebuf, sizeof(tmpname) - 1);
|
|
tmpname[sizeof(tmpname) - 1] = 0;
|
|
|
|
*tptr = 0;
|
|
|
|
if (get_address_info(nodebuf, &family, abuf, tmpname,
|
|
errp) < 0)
|
|
{
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
result = 0;
|
|
|
|
out:
|
|
fclose(f);
|
|
|
|
return (result);
|
|
}
|
|
|
|
/* Load a database of nodes from a given filename. */
|
|
int
|
|
load_all_updates(const char *fname, trpz_result_t **presults, size_t *pnresults,
|
|
trpz_zone_t **pzones, size_t *pnzones, char **errp) {
|
|
FILE *f = NULL;
|
|
|
|
f = fopen(fname, "r");
|
|
if (f == NULL) {
|
|
fprintf(stderr, "couldn't load updates from %s\n", fname);
|
|
perror("fopen");
|
|
return (-1);
|
|
}
|
|
|
|
while (!feof(f)) {
|
|
char line[1024] = { 0 };
|
|
char *lptr = line;
|
|
int is_static = 0;
|
|
|
|
if (!fgets(line, sizeof(line) - 1, f)) {
|
|
break;
|
|
}
|
|
|
|
if (!line[0]) {
|
|
continue;
|
|
}
|
|
|
|
if (line[strlen(line) - 1] == '\n') {
|
|
line[strlen(line) - 1] = 0;
|
|
}
|
|
|
|
if (!line[0]) {
|
|
strcpy(line, "send");
|
|
}
|
|
|
|
if (!line[0] || line[0] == ';') {
|
|
continue;
|
|
}
|
|
|
|
while (*lptr && !isspace(*lptr)) {
|
|
lptr++;
|
|
}
|
|
|
|
*lptr++ = 0;
|
|
|
|
if (!strcasecmp(line, "server")) {
|
|
continue;
|
|
} else if (!strcasecmp(line, "send")) {
|
|
size_t n;
|
|
|
|
for (n = 0; n < *pnzones; n++) {
|
|
if ((*pzones)[n].has_update) {
|
|
(*pzones)[n].serial += 1;
|
|
(*pzones)[n].rollback += 1;
|
|
(*pzones)[n].has_update = 0;
|
|
}
|
|
}
|
|
|
|
continue;
|
|
} else if (!strcasecmp(line, "wipe") ||
|
|
!strcasecmp(line, "rollback"))
|
|
{
|
|
size_t n;
|
|
int rollback;
|
|
|
|
rollback = strcasecmp(line, "rollback") == 0;
|
|
|
|
free_nodes(presults, pnresults);
|
|
|
|
/* Now push forward the serial by # rollback */
|
|
for (n = 0; n < *pnzones; n++) {
|
|
if (rollback) {
|
|
(*pzones)[n].serial +=
|
|
(*pzones)[n].rollback;
|
|
(*pzones)[n].rollback = 0;
|
|
}
|
|
|
|
memset((*pzones)[n].has_triggers, 0,
|
|
sizeof((*pzones)[n].has_triggers));
|
|
}
|
|
|
|
continue;
|
|
} else if (!strcasecmp(line, "static")) {
|
|
is_static = 1;
|
|
} else if (!strcasecmp(line, "restart")) {
|
|
size_t n;
|
|
|
|
for (n = 0; n < *pnzones; n++) {
|
|
(*pzones)[n].serial = 1;
|
|
(*pzones)[n].rollback = 0;
|
|
}
|
|
|
|
continue;
|
|
} else if (strcasecmp(line, "update")) {
|
|
fprintf(stderr,
|
|
"Warning: skipping unknown instruction "
|
|
"directive: \"%s\"\n",
|
|
line);
|
|
continue;
|
|
}
|
|
|
|
/* Everything here is an update */
|
|
while (isspace(*lptr)) {
|
|
lptr++;
|
|
}
|
|
|
|
if (apply_update(lptr, presults, pnresults, pzones, pnzones,
|
|
is_static, 0, errp) == -1)
|
|
{
|
|
fprintf(stderr,
|
|
"Error: could not apply update \"%s\"\n", lptr);
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
return (0);
|
|
}
|
|
|
|
#define WDNS_MAXLEN_NAME 255
|
|
int
|
|
wdns_str_to_name(const char *str, uint8_t **pbuf, bool downcase) {
|
|
const char *p = NULL;
|
|
size_t label_len;
|
|
ssize_t slen;
|
|
uint8_t c, *oclen = NULL, *data = NULL;
|
|
int res = -1;
|
|
|
|
assert(pbuf != NULL);
|
|
|
|
p = str;
|
|
slen = strlen(str);
|
|
|
|
if (slen == 1 && *p == '.') {
|
|
*pbuf = malloc(1);
|
|
if (*pbuf == NULL) {
|
|
perror("malloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
*pbuf[0] = 0;
|
|
return (1);
|
|
}
|
|
|
|
res = 0;
|
|
*pbuf = malloc(WDNS_MAXLEN_NAME);
|
|
if (*pbuf == NULL) {
|
|
perror("malloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
data = *pbuf;
|
|
label_len = 0;
|
|
oclen = data++;
|
|
res++;
|
|
|
|
for (;;) {
|
|
c = *p++;
|
|
label_len++;
|
|
|
|
/* Will the wire name become too long? */
|
|
if (res >= WDNS_MAXLEN_NAME) {
|
|
goto out;
|
|
}
|
|
|
|
if (slen == 0) {
|
|
/* end of input */
|
|
*oclen = --label_len;
|
|
*data++ = '\0';
|
|
res++;
|
|
break;
|
|
}
|
|
|
|
if (c >= 'A' && c <= 'Z') {
|
|
/* an upper case letter; downcase it */
|
|
if (downcase) {
|
|
c |= 0x20;
|
|
}
|
|
*data++ = c;
|
|
res++;
|
|
} else if (c == '\\' && !isdigit(*p)) {
|
|
/* an escaped character */
|
|
if (slen <= 0) {
|
|
goto out;
|
|
}
|
|
*data++ = *p;
|
|
res++;
|
|
p++;
|
|
slen--;
|
|
} else if (c == '\\' && slen >= 3) {
|
|
/* an escaped octet */
|
|
char d[4];
|
|
char *endptr = NULL;
|
|
long int val;
|
|
|
|
d[0] = *p++;
|
|
d[1] = *p++;
|
|
d[2] = *p++;
|
|
d[3] = '\0';
|
|
slen -= 3;
|
|
if (!isdigit(d[0]) || !isdigit(d[1]) || !isdigit(d[2]))
|
|
{
|
|
goto out;
|
|
}
|
|
val = strtol(d, &endptr, 10);
|
|
if (endptr != NULL && *endptr == '\0' && val >= 0 &&
|
|
val <= 255)
|
|
{
|
|
uint8_t uval;
|
|
|
|
uval = (uint8_t)val;
|
|
*data++ = uval;
|
|
res++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
} else if (c == '\\') {
|
|
/* should not occur */
|
|
goto out;
|
|
} else if (c == '.') {
|
|
/* end of label */
|
|
*oclen = --label_len;
|
|
if (label_len == 0) {
|
|
goto out;
|
|
}
|
|
oclen = data++;
|
|
if (slen > 1) {
|
|
res++;
|
|
}
|
|
label_len = 0;
|
|
} else if (c != '\0') {
|
|
*data++ = c;
|
|
res++;
|
|
}
|
|
|
|
slen--;
|
|
}
|
|
|
|
return (res);
|
|
|
|
out:
|
|
free(*pbuf);
|
|
*pbuf = NULL;
|
|
return (-1);
|
|
}
|