]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
lib: unify conversions to/from hex
authorDaniel Stenberg <daniel@haxx.se>
Mon, 31 Mar 2025 21:12:09 +0000 (23:12 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 2 Apr 2025 12:17:56 +0000 (14:17 +0200)
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

lib/escape.c
lib/escape.h
lib/http_aws_sigv4.c
lib/inet_pton.c
lib/strparse.c
lib/strparse.h
lib/urlapi.c
lib/vtls/keylog.c

index 0afc96fc01d5d9a8a2fe624cf9d78b73d0bcdae1..31673fff55052d292919049c149f96d48ce5a8f4 100644 (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];
+}
index 690e4178795a93940138bf02d09ba71de05ff049..1f2bac8fac2a40d060bbb545fa48378b5a90f286 100644 (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 */
index 854cc8f4efad1f03b95cf271f625aa3e05895429..d73ac43d75383e6d55d3a47f2a8287a4c8b8065c 100644 (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;
       }
index 5a871a3099490a377a0aa62bc2335fbfe1a7e825..46b29ad9a0b1bb40678ffb97f2fcdfb9484e04d9 100644 (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;
index 1d53ba6757c58b80b5bb479201884c031a832503..ebe352f15bba57f57e6638853cef5e194bd62022 100644 (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;
index ebad485259fcc287e47b07405a03b41dd8ee9354..6168bf9ca336aabfd1e3188faaa3c7ad41487d82 100644 (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)
index cdd64f9a897208536f1ce1f05536d8952397b362..bbf7947c2a9af73cdfc495652e459c0e2dbe514d 100644 (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);
index 1e76845b33153346690e679c5a85b045d55541b0..e4a8040e5a995a47fcf8b91e92d356739840e558 100644 (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;
 }