hex.c 4.51 KB
Newer Older
Brian Wellington's avatar
Brian Wellington committed
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
Brian Wellington's avatar
Brian Wellington committed
3
 *
4 5 6
 * 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 http://mozilla.org/MPL/2.0/.
7 8 9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
Brian Wellington's avatar
Brian Wellington committed
10 11
 */

12 13

/*! \file */
Brian Wellington's avatar
Brian Wellington committed
14

15 16
#include <config.h>

Brian Wellington's avatar
Brian Wellington committed
17
#include <ctype.h>
18
#include <stdbool.h>
Brian Wellington's avatar
Brian Wellington committed
19

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
#include <isc/buffer.h>
#include <isc/hex.h>
#include <isc/lex.h>
#include <isc/string.h>
#include <isc/util.h>

#define RETERR(x) do { \
	isc_result_t _r = (x); \
	if (_r != ISC_R_SUCCESS) \
		return (_r); \
	} while (0)


/*
 * BEW: These static functions are copied from lib/dns/rdata.c.
 */
static isc_result_t
str_totext(const char *source, isc_buffer_t *target);

static isc_result_t
mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);

static const char hex[] = "0123456789ABCDEF";

isc_result_t
isc_hex_totext(isc_region_t *source, int wordlength,
	       const char *wordbreak, isc_buffer_t *target)
{
	char buf[3];
	unsigned int loops = 0;

	if (wordlength < 2)
		wordlength = 2;

Andreas Gustafsson's avatar
Andreas Gustafsson committed
54
	memset(buf, 0, sizeof(buf));
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
	while (source->length > 0) {
		buf[0] = hex[(source->base[0] >> 4) & 0xf];
		buf[1] = hex[(source->base[0]) & 0xf];
		RETERR(str_totext(buf, target));
		isc_region_consume(source, 1);

		loops++;
		if (source->length != 0 &&
		    (int)((loops + 1) * 2) >= wordlength)
		{
			loops = 0;
			RETERR(str_totext(wordbreak, target));
		}
	}
	return (ISC_R_SUCCESS);
}

72
/*%
73 74 75
 * State of a hex decoding process in progress.
 */
typedef struct {
76 77 78
	int length;		/*%< Desired length of binary data or -1 */
	isc_buffer_t *target;	/*%< Buffer for resulting binary data */
	int digits;		/*%< Number of buffered hex digits */
79 80 81 82 83 84 85 86 87 88 89 90 91
	int val[2];
} hex_decode_ctx_t;

static inline void
hex_decode_init(hex_decode_ctx_t *ctx, int length, isc_buffer_t *target)
{
	ctx->digits = 0;
	ctx->length = length;
	ctx->target = target;
}

static inline isc_result_t
hex_decode_char(hex_decode_ctx_t *ctx, int c) {
92
	const char *s;
93

94
	if ((s = strchr(hex, toupper(c))) == NULL)
95
		return (ISC_R_BADHEX);
96
	ctx->val[ctx->digits++] = (int)(s - hex);
97 98 99 100 101 102
	if (ctx->digits == 2) {
		unsigned char num;

		num = (ctx->val[0] << 4) + (ctx->val[1]);
		RETERR(mem_tobuffer(ctx->target, &num, 1));
		if (ctx->length >= 0) {
103
			if (ctx->length == 0)
104 105
				return (ISC_R_BADHEX);
			else
106
				ctx->length -= 1;
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
		}
		ctx->digits = 0;
	}
	return (ISC_R_SUCCESS);
}

static inline isc_result_t
hex_decode_finish(hex_decode_ctx_t *ctx) {
	if (ctx->length > 0)
		return (ISC_R_UNEXPECTEDEND);
	if (ctx->digits != 0)
		return (ISC_R_BADHEX);
	return (ISC_R_SUCCESS);
}

isc_result_t
isc_hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
124
	unsigned int before, after;
125 126 127
	hex_decode_ctx_t ctx;
	isc_textregion_t *tr;
	isc_token_t token;
128
	bool eol;
129

130 131
	REQUIRE(length >= -2);

132 133
	hex_decode_init(&ctx, length, target);

134
	before = isc_buffer_usedlength(target);
135 136 137
	while (ctx.length != 0) {
		unsigned int i;

138
		if (length > 0) {
139
			eol = false;
140
		} else {
141
			eol = true;
142
		}
143 144
		RETERR(isc_lex_getmastertoken(lexer, &token,
					      isc_tokentype_string, eol));
145
		if (token.type != isc_tokentype_string) {
146
			break;
147
		}
148
		tr = &token.value.as_textregion;
149
		for (i = 0; i < tr->length; i++) {
150
			RETERR(hex_decode_char(&ctx, tr->base[i]));
151
		}
152
	}
153 154
	after = isc_buffer_usedlength(target);
	if (ctx.length < 0) {
155
		isc_lex_ungettoken(lexer, &token);
156
	}
157
	RETERR(hex_decode_finish(&ctx));
158 159 160
	if (length == -2 && before == after) {
		return (ISC_R_UNEXPECTEDEND);
	}
161 162 163 164
	return (ISC_R_SUCCESS);
}

isc_result_t
165
isc_hex_decodestring(const char *cstr, isc_buffer_t *target) {
166 167 168 169 170 171 172 173 174 175 176
	hex_decode_ctx_t ctx;

	hex_decode_init(&ctx, -1, target);
	for (;;) {
		int c = *cstr++;
		if (c == '\0')
			break;
		if (c == ' ' || c == '\t' || c == '\n' || c== '\r')
			continue;
		RETERR(hex_decode_char(&ctx, c));
	}
177
	RETERR(hex_decode_finish(&ctx));
178 179 180 181 182 183 184 185 186 187 188 189 190 191
	return (ISC_R_SUCCESS);
}

static isc_result_t
str_totext(const char *source, isc_buffer_t *target) {
	unsigned int l;
	isc_region_t region;

	isc_buffer_availableregion(target, &region);
	l = strlen(source);

	if (l > region.length)
		return (ISC_R_NOSPACE);

192
	memmove(region.base, source, l);
193 194 195 196 197 198 199 200 201 202 203
	isc_buffer_add(target, l);
	return (ISC_R_SUCCESS);
}

static isc_result_t
mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
	isc_region_t tr;

	isc_buffer_availableregion(target, &tr);
	if (length > tr.length)
		return (ISC_R_NOSPACE);
204
	memmove(tr.base, base, length);
205 206 207
	isc_buffer_add(target, length);
	return (ISC_R_SUCCESS);
}