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