223 lines
5 KiB
C
223 lines
5 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.
|
|
*/
|
|
|
|
#include <isc/ascii.h>
|
|
#include <isc/buffer.h>
|
|
#include <isc/result.h>
|
|
#include <isc/types.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/compress.h>
|
|
#include <dns/types.h>
|
|
|
|
/*
|
|
*/
|
|
|
|
#include "old.h"
|
|
|
|
/*
|
|
* code copied from lib/dns/name.c as of commit
|
|
* 6967973568fe80b03e1729259f8907ce8792be34
|
|
*/
|
|
|
|
typedef enum { fw_start = 0, fw_ordinary, fw_newcurrent } fw_state;
|
|
|
|
#define VALID_NAME(n) ISC_MAGIC_VALID(n, DNS_NAME_MAGIC)
|
|
|
|
#define INIT_OFFSETS(name, var, default_offsets) \
|
|
if ((name)->offsets != NULL) \
|
|
var = (name)->offsets; \
|
|
else \
|
|
var = (default_offsets);
|
|
|
|
#define MAKE_EMPTY(name) \
|
|
do { \
|
|
name->ndata = NULL; \
|
|
name->length = 0; \
|
|
name->labels = 0; \
|
|
name->attributes.absolute = false; \
|
|
} while (0)
|
|
|
|
#define BINDABLE(name) (!name->attributes.readonly && !name->attributes.dynamic)
|
|
|
|
isc_result_t
|
|
old_name_fromwire(dns_name_t *name, isc_buffer_t *source, dns_decompress_t dctx,
|
|
unsigned int options, isc_buffer_t *target) {
|
|
unsigned char *cdata, *ndata;
|
|
unsigned int cused; /* Bytes of compressed name data used */
|
|
unsigned int nused, labels, n, nmax;
|
|
unsigned int current, new_current, biggest_pointer;
|
|
bool done;
|
|
fw_state state = fw_start;
|
|
unsigned int c;
|
|
unsigned char *offsets;
|
|
dns_offsets_t odata;
|
|
bool downcase;
|
|
bool seen_pointer;
|
|
|
|
/*
|
|
* Copy the possibly-compressed name at source into target,
|
|
* decompressing it. Loop prevention is performed by checking
|
|
* the new pointer against biggest_pointer.
|
|
*/
|
|
|
|
REQUIRE(VALID_NAME(name));
|
|
REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) ||
|
|
(target == NULL && ISC_BUFFER_VALID(name->buffer)));
|
|
|
|
downcase = ((options & DNS_NAME_DOWNCASE) != 0);
|
|
|
|
if (target == NULL && name->buffer != NULL) {
|
|
target = name->buffer;
|
|
isc_buffer_clear(target);
|
|
}
|
|
|
|
REQUIRE(BINDABLE(name));
|
|
|
|
INIT_OFFSETS(name, offsets, odata);
|
|
|
|
/*
|
|
* Make 'name' empty in case of failure.
|
|
*/
|
|
MAKE_EMPTY(name);
|
|
|
|
/*
|
|
* Initialize things to make the compiler happy; they're not required.
|
|
*/
|
|
n = 0;
|
|
new_current = 0;
|
|
|
|
/*
|
|
* Set up.
|
|
*/
|
|
labels = 0;
|
|
done = false;
|
|
|
|
ndata = isc_buffer_used(target);
|
|
nused = 0;
|
|
seen_pointer = false;
|
|
|
|
/*
|
|
* Find the maximum number of uncompressed target name
|
|
* bytes we are willing to generate. This is the smaller
|
|
* of the available target buffer length and the
|
|
* maximum legal domain name length (255).
|
|
*/
|
|
nmax = isc_buffer_availablelength(target);
|
|
if (nmax > DNS_NAME_MAXWIRE) {
|
|
nmax = DNS_NAME_MAXWIRE;
|
|
}
|
|
|
|
cdata = isc_buffer_current(source);
|
|
cused = 0;
|
|
|
|
current = source->current;
|
|
biggest_pointer = current;
|
|
|
|
/*
|
|
* Note: The following code is not optimized for speed, but
|
|
* rather for correctness. Speed will be addressed in the future.
|
|
*/
|
|
|
|
while (current < source->active && !done) {
|
|
c = *cdata++;
|
|
current++;
|
|
if (!seen_pointer) {
|
|
cused++;
|
|
}
|
|
|
|
switch (state) {
|
|
case fw_start:
|
|
if (c < 64) {
|
|
offsets[labels] = nused;
|
|
labels++;
|
|
if (nused + c + 1 > nmax) {
|
|
goto full;
|
|
}
|
|
nused += c + 1;
|
|
*ndata++ = c;
|
|
if (c == 0) {
|
|
done = true;
|
|
}
|
|
n = c;
|
|
state = fw_ordinary;
|
|
} else if (c >= 192) {
|
|
/*
|
|
* 14-bit compression pointer
|
|
*/
|
|
if (!dns_decompress_getpermitted(dctx)) {
|
|
return DNS_R_DISALLOWED;
|
|
}
|
|
new_current = c & 0x3F;
|
|
state = fw_newcurrent;
|
|
} else {
|
|
return DNS_R_BADLABELTYPE;
|
|
}
|
|
break;
|
|
case fw_ordinary:
|
|
if (downcase) {
|
|
c = isc_ascii_tolower(c);
|
|
}
|
|
*ndata++ = c;
|
|
n--;
|
|
if (n == 0) {
|
|
state = fw_start;
|
|
}
|
|
break;
|
|
case fw_newcurrent:
|
|
new_current *= 256;
|
|
new_current += c;
|
|
if (new_current >= biggest_pointer) {
|
|
return DNS_R_BADPOINTER;
|
|
}
|
|
biggest_pointer = new_current;
|
|
current = new_current;
|
|
cdata = (unsigned char *)source->base + current;
|
|
seen_pointer = true;
|
|
state = fw_start;
|
|
break;
|
|
default:
|
|
FATAL_ERROR("Unknown state %d", state);
|
|
/* Does not return. */
|
|
}
|
|
}
|
|
|
|
if (!done) {
|
|
return ISC_R_UNEXPECTEDEND;
|
|
}
|
|
|
|
name->ndata = (unsigned char *)target->base + target->used;
|
|
name->labels = labels;
|
|
name->length = nused;
|
|
name->attributes.absolute = true;
|
|
|
|
isc_buffer_forward(source, cused);
|
|
isc_buffer_add(target, name->length);
|
|
|
|
return ISC_R_SUCCESS;
|
|
|
|
full:
|
|
if (nmax == DNS_NAME_MAXWIRE) {
|
|
/*
|
|
* The name did not fit even though we had a buffer
|
|
* big enough to fit a maximum-length name.
|
|
*/
|
|
return DNS_R_NAMETOOLONG;
|
|
} else {
|
|
/*
|
|
* The name might fit if only the caller could give us a
|
|
* big enough buffer.
|
|
*/
|
|
return ISC_R_NOSPACE;
|
|
}
|
|
}
|