From 7ece395fe83ea03dae3e655c33318bac58aed099 Mon Sep 17 00:00:00 2001
From: Alexander Zubkov <green@qrator.net>
Date: Tue, 27 Jun 2023 01:03:56 +0200
Subject: [PATCH] Add library to process bytestrings, use hex parser from
 library in lexer

---
 conf/cf-lex.l    | 33 ++++++-----------
 lib/Makefile     |  2 +-
 lib/bytestring.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/string.h     |  4 +++
 4 files changed, 109 insertions(+), 23 deletions(-)
 create mode 100644 lib/bytestring.c

diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index 3ebf1a9a..59b88bd5 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -256,37 +256,26 @@ WHITE [ \t]
 }
 
 ({XIGIT}{2}){16,}|{XIGIT}{2}(:{XIGIT}{2}){15,}|hex:({XIGIT}{2}(:?{XIGIT}{2})*)? {
-  char *s, *sb = yytext;
-  size_t len = 0, i;
+  char *sb = yytext;
+  size_t len = 0;
   struct bytestring *bytes;
-  byte *b;
 
   /* skip 'hex:' prefix */
   if (sb[0] == 'h' && sb[1] == 'e' && sb[2] == 'x' && sb[3] == ':')
     sb += 4;
 
-  s = sb;
-  while (*s) {
-    len++;
-    s += 2;
-    if (*s == ':')
-      s++;
-  }
+  errno = 0;
+  len = bstrhextobin(sb, 0);
+  if (errno || len == (size_t)-1)
+    cf_error("Invalid hex string");
+
   bytes = cfg_allocz(sizeof(*bytes) + len);
 
   bytes->length = len;
-  b = &bytes->data[0];
-  s = sb;
-  errno = 0;
-  for (i = 0; i < len; i++) {
-    *b = bstrtobyte16(s);
-    if (errno == ERANGE)
-      cf_error("Invalid hex string");
-    b++;
-    s += 2;
-    if (*s == ':')
-      s++;
-  }
+  bstrhextobin(sb, bytes->data);
+  if (errno)
+    cf_error("Invalid hex string");
+
   cf_lval.bs = bytes;
   return BYTESTRING;
 }
diff --git a/lib/Makefile b/lib/Makefile
index 812f721c..296152ff 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,4 +1,4 @@
-src := bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
+src := bitmap.c bitops.c blake2s.c blake2b.c bytestring.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
 obj := $(src-o-files)
 $(all-daemon)
 
diff --git a/lib/bytestring.c b/lib/bytestring.c
new file mode 100644
index 00000000..ff9ef257
--- /dev/null
+++ b/lib/bytestring.c
@@ -0,0 +1,93 @@
+/*
+ *     BIRD Library -- Work with binary sequences
+ *
+ *     (c) 2023 TODO
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+
+#include "nest/bird.h"
+#include "lib/string.h"
+#include "conf/conf.h"
+
+char
+toxdigit(int b)
+{
+  if (b >= 0 && b <= 9)
+    return ('0' + b);
+  else if (b >= 10 && b < 16)
+    return ('a' + b - 10);
+  else
+    return 0;
+}
+
+int
+bstrhextobin(const char *s, byte *d)
+{
+  size_t len = 0;
+
+  while (*s) {
+    if (!isalnum(s[0])) {
+      s++;
+      continue;
+    }
+
+    if (!isalnum(s[1])) {
+      errno = ERANGE;
+      return -1;
+    }
+
+    if (d) {
+      errno = 0;
+      *d = bstrtobyte16(s);
+      if (errno)
+	return -1;
+      d++;
+    }
+
+    s += 2;
+    len++;
+  }
+
+  return len;
+}
+
+int
+bstrbintohex(const struct bytestring *bs, byte *buf, uint size)
+{
+  if (size < 8)
+    return -1;
+
+  size_t len = bs->length;
+  const byte *b = bs->data;
+
+  byte *end = buf + size - 4;
+
+  memcpy(buf, "hex", 3);
+  buf += 3;
+
+  if (!len) {
+    strcpy(buf, ":");
+    return 0;
+  }
+
+  size_t i;
+  for (i = 0; i < len && buf + 3 <= end; ++i, buf += 3) {
+    byte x = b[i];
+    buf[0] = ':';
+    buf[1] = toxdigit(x >> 4);
+    buf[2] = toxdigit(x & 0xF);
+  }
+
+  if (i < len) {
+    strcpy(buf, "...");
+    return -1;
+  }
+
+  *buf = 0;
+
+  return 0;
+}
diff --git a/lib/string.h b/lib/string.h
index 2829943d..62f03eb6 100644
--- a/lib/string.h
+++ b/lib/string.h
@@ -33,6 +33,10 @@ u64 bstrtoul10(const char *str, char **end);
 u64 bstrtoul16(const char *str, char **end);
 byte bstrtobyte16(const char *str);
 
+struct bytestring;
+int bstrhextobin(const char *s, byte *d);
+int bstrbintohex(const struct bytestring *bs, byte *buf, uint size);
+
 int patmatch(const byte *pat, const byte *str);
 
 static inline char *xbasename(const char *str)
-- 
2.41.0

