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