From: Daniel Stenberg Date: Mon, 31 Mar 2025 21:12:09 +0000 (+0200) Subject: lib: unify conversions to/from hex X-Git-Tag: curl-8_14_0~402 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0c6e63a1be26a5ba4c953bcacd29c1db412e7e95;p=thirdparty%2Fcurl.git lib: unify conversions to/from hex Curl_hexbyte - output a byte as a two-digit ASCII hex number Curl_hexval - convert an ASCII hex digit to its binary value ... instead of duplicating similar code and hexdigit strings in numerous places. Closes #16888 --- diff --git a/lib/escape.c b/lib/escape.c index 0afc96fc01..31673fff55 100644 --- a/lib/escape.c +++ b/lib/escape.c @@ -35,6 +35,8 @@ struct Curl_easy; #include "warnless.h" #include "escape.h" #include "strdup.h" +#include "strparse.h" + /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -82,10 +84,8 @@ char *curl_easy_escape(CURL *data, const char *string, } else { /* encode it */ - const char hex[] = "0123456789ABCDEF"; - char out[3]={'%'}; - out[1] = hex[in >> 4]; - out[2] = hex[in & 0xf]; + unsigned char out[3]={'%'}; + Curl_hexbyte(&out[1], in, FALSE); if(Curl_dyn_addn(&d, out, 3)) return NULL; } @@ -94,16 +94,6 @@ char *curl_easy_escape(CURL *data, const char *string, return Curl_dyn_ptr(&d); } -static const unsigned char hextable[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ - 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ - 0, 10, 11, 12, 13, 14, 15 /* 0x60 - 0x66 */ -}; - -/* the input is a single hex digit */ -#define onehex2dec(x) hextable[x - '0'] - /* * Curl_urldecode() URL decodes the given string. * @@ -144,8 +134,8 @@ CURLcode Curl_urldecode(const char *string, size_t length, if(('%' == in) && (alloc > 2) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { /* this is two hexadecimal digits following a '%' */ - in = (unsigned char)(onehex2dec(string[1]) << 4) | onehex2dec(string[2]); - + in = (unsigned char)((Curl_hexval(string[1]) << 4) | + Curl_hexval(string[2])); string += 3; alloc -= 3; } @@ -219,13 +209,12 @@ void curl_free(void *p) void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ unsigned char *out, size_t olen) /* output buffer size */ { - const char *hex = "0123456789abcdef"; DEBUGASSERT(src && len && (olen >= 3)); if(src && len && (olen >= 3)) { while(len-- && (olen >= 3)) { - *out++ = (unsigned char)hex[(*src & 0xF0) >> 4]; - *out++ = (unsigned char)hex[*src & 0x0F]; + Curl_hexbyte(out, *src, TRUE); ++src; + out += 2; olen -= 2; } *out = 0; @@ -233,3 +222,19 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ else if(olen) *out = 0; } + +/* Curl_hexbyte + * + * Output a single unsigned char as a two-digit hex number, lowercase or + * uppercase + */ +void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */ + unsigned char val, + bool lowercase) +{ + const unsigned char uhex[] = "0123456789ABCDEF"; + const unsigned char lhex[] = "0123456789abcdef"; + const unsigned char *t = lowercase ? lhex : uhex; + dest[0] = t[val >> 4]; + dest[1] = t[val & 0x0F]; +} diff --git a/lib/escape.h b/lib/escape.h index 690e417879..1f2bac8fac 100644 --- a/lib/escape.h +++ b/lib/escape.h @@ -41,4 +41,8 @@ CURLcode Curl_urldecode(const char *string, size_t length, void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ unsigned char *out, size_t olen); /* output buffer size */ +void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */ + unsigned char val, + bool lowercase); + #endif /* HEADER_CURL_ESCAPE_H */ diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 854cc8f4ef..d73ac43d75 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -537,8 +537,7 @@ static CURLcode canon_string(const char *q, size_t len, result = Curl_dyn_addn(dq, "%25", 3); break; default: { - const char hex[] = "0123456789ABCDEF"; - char out[3]={'%'}; + unsigned char out[3]={'%'}; if(!found_equals) { /* if found_equals is NULL assuming, been in path */ @@ -557,8 +556,7 @@ static CURLcode canon_string(const char *q, size_t len, } } /* URL encode */ - out[1] = hex[((unsigned char)*q) >> 4]; - out[2] = hex[*q & 0xf]; + Curl_hexbyte(&out[1], *q, FALSE); result = Curl_dyn_addn(dq, out, 3); break; } diff --git a/lib/inet_pton.c b/lib/inet_pton.c index 5a871a3099..46b29ad9a0 100644 --- a/lib/inet_pton.c +++ b/lib/inet_pton.c @@ -19,6 +19,8 @@ */ #include "curl_setup.h" +#include "curl_ctype.h" +#include "strparse.h" #ifndef HAVE_INET_PTON @@ -99,7 +101,6 @@ curlx_inet_pton(int af, const char *src, void *dst) static int inet_pton4(const char *src, unsigned char *dst) { - static const char digits[] = "0123456789"; int saw_digit, octets, ch; unsigned char tmp[INADDRSZ], *tp; @@ -108,12 +109,8 @@ inet_pton4(const char *src, unsigned char *dst) tp = tmp; *tp = 0; while((ch = *src++) != '\0') { - const char *pch; - - pch = strchr(digits, ch); - if(pch) { - unsigned int val = (unsigned int)(*tp * 10) + - (unsigned int)(pch - digits); + if(ISDIGIT(ch)) { + unsigned int val = (*tp * 10) + (ch - '0'); if(saw_digit && *tp == 0) return 0; @@ -157,8 +154,6 @@ inet_pton4(const char *src, unsigned char *dst) static int inet_pton6(const char *src, unsigned char *dst) { - static const char xdigits_l[] = "0123456789abcdef", - xdigits_u[] = "0123456789ABCDEF"; unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp; const char *curtok; int ch, saw_xdigit; @@ -175,15 +170,9 @@ inet_pton6(const char *src, unsigned char *dst) saw_xdigit = 0; val = 0; while((ch = *src++) != '\0') { - const char *xdigits; - const char *pch; - - pch = strchr((xdigits = xdigits_l), ch); - if(!pch) - pch = strchr((xdigits = xdigits_u), ch); - if(pch) { + if(ISXDIGIT(ch)) { val <<= 4; - val |= (pch - xdigits); + val |= Curl_hexval(ch); if(++saw_xdigit > 4) return 0; continue; diff --git a/lib/strparse.c b/lib/strparse.c index 1d53ba6757..ebe352f15b 100644 --- a/lib/strparse.c +++ b/lib/strparse.c @@ -139,25 +139,23 @@ int Curl_str_singlespace(const char **linep) /* given an ASCII character and max ascii, return TRUE if valid */ #define valid_digit(x,m) \ - (((x) >= '0') && ((x) <= m) && hexasciitable[(x)-'0']) + (((x) >= '0') && ((x) <= m) && Curl_hexasciitable[(x)-'0']) + +/* We use 16 for the zero index (and the necessary bitwise AND in the loop) + to be able to have a non-zero value there to make valid_digit() able to + use the info */ +const unsigned char Curl_hexasciitable[] = { + 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* 0x30: 0 - 9 */ + 0, 0, 0, 0, 0, 0, 0, + 10, 11, 12, 13, 14, 15, /* 0x41: A - F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 11, 12, 13, 14, 15 /* 0x61: a - f */ +}; /* no support for 0x prefix nor leading spaces */ static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max, int base) /* 8, 10 or 16, nothing else */ { - /* We use 16 for the zero index (and the necessary bitwise AND in the loop) - to be able to have a non-zero value there to make valid_digit() able to - use the info */ - static const unsigned char hexasciitable[] = { - 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* 0x30: 0 - 9 */ - 0, 0, 0, 0, 0, 0, 0, - 10, 11, 12, 13, 14, 15, /* 0x41: A - F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 10, 11, 12, 13, 14, 15 /* 0x61: a - f */ - }; - curl_off_t num = 0; const char *p; int m = (base == 10) ? '9' : /* the largest digit possible */ @@ -172,7 +170,7 @@ static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max, if(max < base) { /* special-case low max scenario because check needs to be different */ do { - int n = hexasciitable[*p++ - '0'] & 0x0f; + int n = Curl_hexval(*p++); num = num * base + n; if(num > max) return STRE_OVERFLOW; @@ -180,7 +178,7 @@ static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max, } else { do { - int n = hexasciitable[*p++ - '0'] & 0x0f; + int n = Curl_hexval(*p++); if(num > ((max - n) / base)) return STRE_OVERFLOW; num = num * base + n; diff --git a/lib/strparse.h b/lib/strparse.h index ebad485259..6168bf9ca3 100644 --- a/lib/strparse.h +++ b/lib/strparse.h @@ -102,6 +102,13 @@ int Curl_str_cspn(const char **linep, struct Curl_str *out, const char *cspn); void Curl_str_trimblanks(struct Curl_str *out); void Curl_str_passblanks(const char **linep); +/* given a hexadecimal letter, return the binary value. '0' returns 0, 'a' + returns 10. THIS ONLY WORKS ON VALID HEXADECIMAL LETTER INPUT. Verify + before calling this! +*/ +extern const unsigned char Curl_hexasciitable[]; +#define Curl_hexval(x) (unsigned char)(Curl_hexasciitable[(x) - '0'] & 0x0f) + #define curlx_str_number(x,y,z) Curl_str_number(x,y,z) #define curlx_str_octal(x,y,z) Curl_str_octal(x,y,z) #define curlx_str_single(x,y) Curl_str_single(x,y) diff --git a/lib/urlapi.c b/lib/urlapi.c index cdd64f9a89..bbf7947c2a 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -129,7 +129,6 @@ static const char *find_host_sep(const char *url) #define cc2cu(x) ((x) == CURLE_TOO_LARGE ? CURLUE_TOO_LARGE : \ CURLUE_OUT_OF_MEMORY) -static const char hexdigits[] = "0123456789abcdef"; /* urlencode_str() writes data into an output dynbuf and URL-encodes the * spaces in the source URL accordingly. * @@ -164,9 +163,8 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, result = Curl_dyn_addn(o, "+", 1); } else if((*iptr < ' ') || (*iptr >= 0x7f)) { - char out[3]={'%'}; - out[1] = hexdigits[*iptr >> 4]; - out[2] = hexdigits[*iptr & 0xf]; + unsigned char out[3]={'%'}; + Curl_hexbyte(&out[1], *iptr, TRUE); result = Curl_dyn_addn(o, out, 3); } else { @@ -1824,9 +1822,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return cc2cu(result); } else { - char out[3]={'%'}; - out[1] = hexdigits[*i >> 4]; - out[2] = hexdigits[*i & 0xf]; + unsigned char out[3]={'%'}; + Curl_hexbyte(&out[1], *i, TRUE); result = Curl_dyn_addn(&enc, out, 3); if(result) return cc2cu(result); diff --git a/lib/vtls/keylog.c b/lib/vtls/keylog.c index 1e76845b33..e4a8040e5a 100644 --- a/lib/vtls/keylog.c +++ b/lib/vtls/keylog.c @@ -32,6 +32,7 @@ #include "keylog.h" #include +#include "escape.h" /* The last #include files should be: */ #include "curl_memory.h" @@ -114,10 +115,9 @@ Curl_tls_keylog_write(const char *label, const unsigned char client_random[CLIENT_RANDOM_SIZE], const unsigned char *secret, size_t secretlen) { - const char *hex = "0123456789ABCDEF"; size_t pos, i; - char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 + - 2 * SECRET_MAXLEN + 1 + 1]; + unsigned char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 + + 2 * SECRET_MAXLEN + 1 + 1]; if(!keylog_file_fp) { return FALSE; @@ -134,22 +134,22 @@ Curl_tls_keylog_write(const char *label, /* Client Random */ for(i = 0; i < CLIENT_RANDOM_SIZE; i++) { - line[pos++] = hex[client_random[i] >> 4]; - line[pos++] = hex[client_random[i] & 0xF]; + Curl_hexbyte(&line[pos], client_random[i], FALSE); + pos += 2; } line[pos++] = ' '; /* Secret */ for(i = 0; i < secretlen; i++) { - line[pos++] = hex[secret[i] >> 4]; - line[pos++] = hex[secret[i] & 0xF]; + Curl_hexbyte(&line[pos], secret[i], FALSE); + pos += 2; } line[pos++] = '\n'; line[pos] = '\0'; /* Using fputs here instead of fprintf since libcurl's fprintf replacement may not be thread-safe. */ - fputs(line, keylog_file_fp); + fputs((char *)line, keylog_file_fp); return TRUE; }