xref: /netbsd-src/external/mpl/bind/dist/lib/isc/hex.c (revision 345cf9fb81bd0411c53e25d62cd93bdcaa865312)
1 /*	$NetBSD: hex.c,v 1.8 2022/09/23 12:15:33 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <ctype.h>
19 #include <stdbool.h>
20 
21 #include <isc/buffer.h>
22 #include <isc/hex.h>
23 #include <isc/lex.h>
24 #include <isc/string.h>
25 #include <isc/util.h>
26 
27 #define RETERR(x)                        \
28 	do {                             \
29 		isc_result_t _r = (x);   \
30 		if (_r != ISC_R_SUCCESS) \
31 			return ((_r));   \
32 	} while (0)
33 
34 /*
35  * BEW: These static functions are copied from lib/dns/rdata.c.
36  */
37 static isc_result_t
38 str_totext(const char *source, isc_buffer_t *target);
39 
40 static isc_result_t
41 mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
42 
43 static const char hex[] = "0123456789ABCDEF";
44 
45 isc_result_t
46 isc_hex_totext(isc_region_t *source, int wordlength, const char *wordbreak,
47 	       isc_buffer_t *target) {
48 	char buf[3];
49 	unsigned int loops = 0;
50 
51 	if (wordlength < 2) {
52 		wordlength = 2;
53 	}
54 
55 	memset(buf, 0, sizeof(buf));
56 	while (source->length > 0) {
57 		buf[0] = hex[(source->base[0] >> 4) & 0xf];
58 		buf[1] = hex[(source->base[0]) & 0xf];
59 		RETERR(str_totext(buf, target));
60 		isc_region_consume(source, 1);
61 
62 		loops++;
63 		if (source->length != 0 && (int)((loops + 1) * 2) >= wordlength)
64 		{
65 			loops = 0;
66 			RETERR(str_totext(wordbreak, target));
67 		}
68 	}
69 	return (ISC_R_SUCCESS);
70 }
71 
72 /*%
73  * State of a hex decoding process in progress.
74  */
75 typedef struct {
76 	int length;	      /*%< Desired length of binary data or -1 */
77 	isc_buffer_t *target; /*%< Buffer for resulting binary data */
78 	int digits;	      /*%< Number of buffered hex digits */
79 	int val[2];
80 } hex_decode_ctx_t;
81 
82 static void
83 hex_decode_init(hex_decode_ctx_t *ctx, int length, isc_buffer_t *target) {
84 	ctx->digits = 0;
85 	ctx->length = length;
86 	ctx->target = target;
87 }
88 
89 static isc_result_t
90 hex_decode_char(hex_decode_ctx_t *ctx, int c) {
91 	const char *s;
92 
93 	if ((s = strchr(hex, toupper(c))) == NULL) {
94 		return (ISC_R_BADHEX);
95 	}
96 	ctx->val[ctx->digits++] = (int)(s - hex);
97 	if (ctx->digits == 2) {
98 		unsigned char num;
99 
100 		num = (ctx->val[0] << 4) + (ctx->val[1]);
101 		RETERR(mem_tobuffer(ctx->target, &num, 1));
102 		if (ctx->length >= 0) {
103 			if (ctx->length == 0) {
104 				return (ISC_R_BADHEX);
105 			} else {
106 				ctx->length -= 1;
107 			}
108 		}
109 		ctx->digits = 0;
110 	}
111 	return (ISC_R_SUCCESS);
112 }
113 
114 static isc_result_t
115 hex_decode_finish(hex_decode_ctx_t *ctx) {
116 	if (ctx->length > 0) {
117 		return (ISC_R_UNEXPECTEDEND);
118 	}
119 	if (ctx->digits != 0) {
120 		return (ISC_R_BADHEX);
121 	}
122 	return (ISC_R_SUCCESS);
123 }
124 
125 isc_result_t
126 isc_hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
127 	unsigned int before, after;
128 	hex_decode_ctx_t ctx;
129 	isc_textregion_t *tr;
130 	isc_token_t token;
131 	bool eol;
132 
133 	REQUIRE(length >= -2);
134 
135 	hex_decode_init(&ctx, length, target);
136 
137 	before = isc_buffer_usedlength(target);
138 	while (ctx.length != 0) {
139 		unsigned int i;
140 
141 		if (length > 0) {
142 			eol = false;
143 		} else {
144 			eol = true;
145 		}
146 		RETERR(isc_lex_getmastertoken(lexer, &token,
147 					      isc_tokentype_string, eol));
148 		if (token.type != isc_tokentype_string) {
149 			break;
150 		}
151 		tr = &token.value.as_textregion;
152 		for (i = 0; i < tr->length; i++) {
153 			RETERR(hex_decode_char(&ctx, tr->base[i]));
154 		}
155 	}
156 	after = isc_buffer_usedlength(target);
157 	if (ctx.length < 0) {
158 		isc_lex_ungettoken(lexer, &token);
159 	}
160 	RETERR(hex_decode_finish(&ctx));
161 	if (length == -2 && before == after) {
162 		return (ISC_R_UNEXPECTEDEND);
163 	}
164 	return (ISC_R_SUCCESS);
165 }
166 
167 isc_result_t
168 isc_hex_decodestring(const char *cstr, isc_buffer_t *target) {
169 	hex_decode_ctx_t ctx;
170 
171 	hex_decode_init(&ctx, -1, target);
172 	for (;;) {
173 		int c = *cstr++;
174 		if (c == '\0') {
175 			break;
176 		}
177 		if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
178 			continue;
179 		}
180 		RETERR(hex_decode_char(&ctx, c));
181 	}
182 	RETERR(hex_decode_finish(&ctx));
183 	return (ISC_R_SUCCESS);
184 }
185 
186 static isc_result_t
187 str_totext(const char *source, isc_buffer_t *target) {
188 	unsigned int l;
189 	isc_region_t region;
190 
191 	isc_buffer_availableregion(target, &region);
192 	l = strlen(source);
193 
194 	if (l > region.length) {
195 		return (ISC_R_NOSPACE);
196 	}
197 
198 	memmove(region.base, source, l);
199 	isc_buffer_add(target, l);
200 	return (ISC_R_SUCCESS);
201 }
202 
203 static isc_result_t
204 mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
205 	isc_region_t tr;
206 
207 	isc_buffer_availableregion(target, &tr);
208 	if (length > tr.length) {
209 		return (ISC_R_NOSPACE);
210 	}
211 	memmove(tr.base, base, length);
212 	isc_buffer_add(target, length);
213 	return (ISC_R_SUCCESS);
214 }
215