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
This commit is contained in:
Daniel Stenberg 2025-03-31 23:12:09 +02:00
parent 1d84d683bb
commit 0c6e63a1be
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
8 changed files with 69 additions and 71 deletions

View file

@ -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];
}

View file

@ -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 */

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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);

View file

@ -32,6 +32,7 @@
#include "keylog.h"
#include <curl/curl.h>
#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;
}