]> source.dussan.org Git - rspamd.git/commitdiff
Add fixed hex/base32 encode and decode routines
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Fri, 5 Feb 2016 23:05:13 +0000 (23:05 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Fri, 5 Feb 2016 23:05:13 +0000 (23:05 +0000)
src/libutil/str_util.c
src/libutil/str_util.h

index 5a391942869c9b5a5e9bb916d2ffef830d355c04..6a0e4f4e723a8a289fe25752d002198217d0a04e 100644 (file)
@@ -463,49 +463,51 @@ rspamd_str_pool_copy (gconstpointer data, gpointer ud)
  * http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
  */
 
-gchar *
-rspamd_encode_base32 (const guchar *in, gsize inlen)
+gint
+rspamd_encode_base32_buf (const guchar *in, gsize inlen, gchar *out,
+               gsize outlen)
 {
-       gint remain = -1, x;
-       gsize i, r;
-       gsize allocated_len = inlen * 8 / 5 + 2;
-       gchar *out;
        static const char b32[]="ybndrfg8ejkmcpqxot1uwisza345h769";
+       gchar *o, *end;
+       gsize i, r;
+       gint remain = -1, x;
 
-       out = g_malloc (allocated_len);
-       for (i = 0, r = 0; i < inlen; i++) {
+       end = out + outlen;
+       o = out;
+
+       for (i = 0, r = 0; i < inlen && o < end - 1; i++) {
                switch (i % 5) {
                case 0:
                        /* 8 bits of input and 3 to remain */
                        x = in[i];
                        remain = in[i] >> 5;
-                       out[r++] = b32[x & 0x1F];
+                       *o++ = b32[x & 0x1F];
                        break;
                case 1:
                        /* 11 bits of input, 1 to remain */
                        x = remain | in[i] << 3;
-                       out[r++] = b32[x & 0x1F];
-                       out[r++] = b32[x >> 5 & 0x1F];
+                       *o++ = b32[x & 0x1F];
+                       *o++ = b32[x >> 5 & 0x1F];
                        remain = x >> 10;
                        break;
                case 2:
                        /* 9 bits of input, 4 to remain */
                        x = remain | in[i] << 1;
-                       out[r++] = b32[x & 0x1F];
+                       *o++ = b32[x & 0x1F];
                        remain = x >> 5;
                        break;
                case 3:
                        /* 12 bits of input, 2 to remain */
                        x = remain | in[i] << 4;
-                       out[r++] = b32[x & 0x1F];
-                       out[r++] = b32[x >> 5 & 0x1F];
+                       *o++ = b32[x & 0x1F];
+                       *o++ = b32[x >> 5 & 0x1F];
                        remain = x >> 10 & 0x3;
                        break;
                case 4:
                        /* 10 bits of output, nothing to remain */
                        x = remain | in[i] << 2;
-                       out[r++] = b32[x & 0x1F];
-                       out[r++] = b32[x >> 5 & 0x1F];
+                       *o++ = b32[x & 0x1F];
+                       *o++ = b32[x >> 5 & 0x1F];
                        remain = -1;
                        break;
                default:
@@ -514,14 +516,36 @@ rspamd_encode_base32 (const guchar *in, gsize inlen)
                }
 
        }
-       if (remain >= 0) {
-               out[r++] = b32[remain];
+       if (remain >= 0 && o < end) {
+               *o++ = b32[remain];
        }
 
-       out[r] = 0;
-       g_assert (r < allocated_len);
+       if (o <= end) {
+               return (o - out);
+       }
 
-       return out;
+       return -1;
+}
+
+gchar *
+rspamd_encode_base32 (const guchar *in, gsize inlen)
+{
+       gsize allocated_len = inlen * 8 / 5 + 2;
+       gchar *out;
+       gint outlen;
+
+       out = g_malloc (allocated_len);
+       outlen = rspamd_encode_base32_buf (in, inlen, out, allocated_len - 1);
+
+       if (outlen >= 0) {
+               out[outlen] = 0;
+
+               return out;
+       }
+
+       g_free (out);
+
+       return NULL;
 }
 
 static const guchar b32_dec[] = {
@@ -559,48 +583,86 @@ static const guchar b32_dec[] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 };
 
-guchar*
-rspamd_decode_base32 (const gchar *in, gsize inlen, gsize *outlen)
+gint
+rspamd_decode_base32_buf (const gchar *in, gsize inlen,
+               guchar *out, gsize outlen)
 {
-       guchar *res, decoded;
+       guchar *o, *end, decoded;
        guchar c;
        guint acc = 0U;
        guint processed_bits = 0;
-       gsize olen = 0, i, allocated_len = inlen * 5 / 8 + 2;
+       gsize i;
 
-       res = g_malloc (allocated_len);
+       end = out + outlen;
+       o = out;
 
        for (i = 0; i < inlen; i ++) {
                c = (guchar)in[i];
 
                if (processed_bits >= 8) {
                        processed_bits -= 8;
-                       res[olen++] = acc & 0xFF;
+                       *o++ = acc & 0xFF;
                        acc >>= 8;
                }
 
                decoded = b32_dec[c];
-               if (decoded == 0xff) {
-                       g_free (res);
-                       return NULL;
+               if (decoded == 0xff || o >= end) {
+                       return -1;
                }
 
                acc = (decoded << processed_bits) | acc;
                processed_bits += 5;
        }
 
-       if (processed_bits > 0) {
-               res[olen++] = (acc & 0xFF);
+       if (processed_bits > 0 && o < end) {
+               *o++ = (acc & 0xFF);
+       }
+       else if (o > end) {
+               return -1;
+       }
+
+       return (o - out);
+}
+
+guchar*
+rspamd_decode_base32 (const gchar *in, gsize inlen, gsize *outlen)
+{
+       guchar *res;
+
+       gsize olen = 0, i, allocated_len = inlen * 5 / 8 + 2;
+
+       res = g_malloc (allocated_len);
+
+       olen = rspamd_decode_base32_buf (in, inlen, res, allocated_len - 1);
+
+       if (olen >= 0) {
+               res[olen] = '\0';
        }
+       else {
+               g_free (res);
 
-       g_assert (olen <= allocated_len);
+               return NULL;
+       }
 
-       *outlen = olen;
+       if (outlen) {
+               *outlen = olen;
+       }
 
        return res;
 }
 
 
+
+/**
+ * Decode string using base32 encoding
+ * @param in input
+ * @param inlen input length
+ * @param out output buf (may overlap with `in`)
+ * @param outlen output buf len
+ * @return TRUE if in is valid base32 and `outlen` is enough to encode `inlen`
+ */
+
+
 static gchar *
 rspamd_encode_base64_common (const guchar *in, gsize inlen, gint str_len,
                gsize *outlen, gboolean fold)
@@ -1264,54 +1326,74 @@ rspamd_string_find_eoh (GString *input)
        return -1;
 }
 
-gchar *
-rspamd_encode_hex (const guchar *in, gsize inlen)
+gint
+rspamd_encode_hex_buf (const guchar *in, gsize inlen, gchar *out,
+               gsize outlen)
 {
-       gchar *out, *o;
+       gchar *o, *end;
        const guchar *p;
-       gsize outlen = inlen * 2 + 1;
        static const gchar hexdigests[16] = "0123456789abcdef";
 
-       if (in == NULL) {
-               return NULL;
-       }
-
-       out = g_malloc (outlen);
+       end = out + end;
        o = out;
        p = in;
 
-       while (inlen > 0) {
+
+       while (inlen > 0 && o < end - 1) {
                *o++ = hexdigests[((*p >> 4) & 0xF)];
                *o++ = hexdigests[((*p++) & 0xF)];
                inlen --;
        }
 
-       *o = '\0';
+       if (o <= end) {
+               return (o - end);
+       }
 
-       return out;
+       return -1;
 }
 
-
-guchar*
-rspamd_decode_hex (const gchar *in, gsize inlen)
+gchar *
+rspamd_encode_hex (const guchar *in, gsize inlen)
 {
-       guchar *out, *o, ret;
-       const gchar *p;
-       gchar c;
-       gsize outlen = (inlen / 2 + inlen % 2) + 1;
+       gchar *out;
+       gsize outlen = inlen * 2 + 1;
+       gint olen;
 
        if (in == NULL) {
                return NULL;
        }
 
        out = g_malloc (outlen);
+       olen = rspamd_encode_hex_buf (in, inlen, out, outlen - 1);
+
+       if (olen >= 0) {
+               out[olen] = '\0';
+       }
+       else {
+               g_free (out);
+
+               return NULL;
+       }
+
+       return out;
+}
+
+gint
+rspamd_decode_hex_buf (const gchar *in, gsize inlen, gsize inlen,
+               guchar *out, gsize outlen)
+{
+       guchar *o, *end, ret;
+       const gchar *p;
+       gchar c;
+
+       end = out + outlen;
        o = out;
        p = in;
 
        /* We ignore trailing chars if we have not even input */
        inlen = inlen - inlen % 2;
 
-       while (inlen > 0) {
+       while (inlen > 1 && o < end) {
                c = *p++;
 
                if      (c >= '0' && c <= '9') ret = c - '0';
@@ -1330,11 +1412,40 @@ rspamd_decode_hex (const gchar *in, gsize inlen)
                inlen -= 2;
        }
 
-       *o = '\0';
+       if (o <= end) {
+               return (o - end);
+       }
 
-       return out;
+       return -1;
+}
+
+guchar*
+rspamd_decode_hex (const gchar *in, gsize inlen)
+{
+       guchar *out;
+       gsize outlen = (inlen / 2 + inlen % 2) + 1;
+       gint olen;
+
+       if (in == NULL) {
+               return NULL;
+       }
+
+       out = g_malloc (outlen);
+
+       olen = rspamd_decode_hex_buf (in, inlen, out, outlen - 1);
+
+       if (olen >= 0) {
+               out[olen] = '\0';
+
+               return out;
+       }
+
+       g_free (out);
+
+       return NULL;
 }
 
+
 /*
  * GString ucl emitting functions
  */
index 2f16db37d8164232b76cf6917ce457c5898f0428..8e26b37dc770499b6b839168fc5097c09b1ff137 100644 (file)
@@ -131,6 +131,50 @@ gchar * rspamd_encode_hex (const guchar *in, gsize inlen);
  */
 guchar* rspamd_decode_hex (const gchar *in, gsize inlen);
 
+/**
+ * Encode string using base32 encoding
+ * @param in input
+ * @param inlen input length
+ * @param out output buf
+ * @param outlen output buf len
+ * @return encoded len if `outlen` is enough to encode `inlen`
+ */
+gint rspamd_encode_base32_buf (const guchar *in, gsize inlen, gchar *out,
+               gsize outlen);
+
+/**
+ * Decode string using base32 encoding
+ * @param in input
+ * @param inlen input length
+ * @param out output buf (may overlap with `in`)
+ * @param outlen output buf len
+ * @return decoded len if in is valid base32 and `outlen` is enough to encode `inlen`
+ */
+gint rspamd_decode_base32_buf (const gchar *in, gsize inlen,
+               guchar *out, gsize outlen);
+
+/**
+ * Encode string using hex encoding
+ * @param in input
+ * @param inlen input length
+ * @param out output buf
+ * @param outlen output buf len
+ * @return encoded len if `outlen` is enough to encode `inlen`
+ */
+gint rspamd_encode_hex_buf (const guchar *in, gsize inlen, gchar *out,
+               gsize outlen);
+
+/**
+ * Decode string using hex encoding
+ * @param in input
+ * @param inlen input length
+ * @param out output buf (may overlap with `in`)
+ * @param outlen output buf len
+ * @return decoded len if in is valid hex and `outlen` is enough to encode `inlen`
+ */
+gint rspamd_decode_hex_buf (const gchar *in, gsize inlen,
+               guchar *out, gsize outlen);
+
 /**
  * Encode string using base64 encoding
  * @param in input