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, ®ion); 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