Hi,

Please look at these patches:

bytestring-hex-prefix.patch - syntax with "hex:" prefix
I allowed mixed colons with no-divider there, so hex:12:345678:90 is
allowed. As there is a distinguishing prefix here, this should not be
a problem.
Empty bytestrings are allowed too: "hex:"

bytestring-hex-function.patch - function-like hex("...") syntax (on
top of the other patch)
It wasn't too complex, but you might have wanted to do it some other
way. I think this should be quite good too, the only problem with it
is inability to mix "hex" symbol with hex("...") bytestrings.
I parse string in two steps. First to know the length of a binary
output. Current hex realization does the same anyway. I made a
separate function for it to reuse it for "classic" bytestrings and for
function-like syntax.
It ignores all non-alphanumeric symbols and requires that each byte
symbols go in pairs without delimiters.
I added a HEX keyword and added it to kw_sym, but this solution does
not allow to mix "hex" symbol and hex("...") bytestring in the same
config.
There probably was a mistake in nest/config.Y with missing "|" here:
"kw_sym: MIN MAX ;"
There is a "TODO" in the heading of strtobin.c, don't know what is
your policy regarding that. The code is still based on the current
BYTESTRING parser.
I also enabled TEXT literals to be multiline. Didn't know it was not
allowed already, so I think it should not break things, but allows to
be more flexible with binary strings.

On Tue, Jun 13, 2023 at 6:37 PM Ondrej Zajicek <santi...@crfreenet.org> wrote:
>
> On Tue, Jun 13, 2023 at 05:34:18PM +0200, Alexander Zubkov wrote:
> > On Tue, Jun 13, 2023, 16:07 Ondrej Zajicek <santi...@crfreenet.org> wrote:
> >
> > > We agreed on keeping existing format for suffiently long hex bytestrings,
> > > using hex: prefix for bytestrings of any length, and adding hex("...") /
> > > base64("...") as ordinary expressions.
> >
> > Hi,
> >
> > So full house. :) I can try to implement it. Do you need a hand?
>
> If you implement hex: prefix, we could easily merge that. The
> hex("...") / base64("...") is not priority and would require some
> nontrivial changes to be done properly.
>
> --
> Elen sila lumenn' omentielvo
>
> Ondrej 'Santiago' Zajicek (email: santi...@crfreenet.org)
> OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net)
> "To err is human -- to blame it on a computer is even more so."
commit a0d088b49510db8f968a99b7e1a5f7f1242e199d
Author: Alexander Zubkov <gr...@qrator.net>
Date:   Tue Jun 13 22:19:28 2023 +0200

    Conf: allow to provide a bytestring of any length with 'hex:' prefix

diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index 9555949d..9025a84d 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -255,12 +255,17 @@ WHITE [ \t]
   return IP4;
 }
 
-{XIGIT}{2}((:{XIGIT}{2}){15,}|({XIGIT}{2}){15,}) {
-  char *s = yytext;
+({XIGIT}{2}){16,}|{XIGIT}{2}(:{XIGIT}{2}){15,}|hex:({XIGIT}{2}(:?{XIGIT}{2})*)? {
+  char *s, *sb = yytext;
   size_t len = 0, i;
   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;
@@ -271,7 +276,7 @@ WHITE [ \t]
 
   bytes->length = len;
   b = &bytes->data[0];
-  s = yytext;
+  s = sb;
   errno = 0;
   for (i = 0; i < len; i++) {
     *b = bstrtobyte16(s);
commit eead1fd2f7193bed2bbece8aeebfa04571030f6e
Author: Alexander Zubkov <gr...@qrator.net>
Date:   Wed Jun 14 00:08:23 2023 +0200

    Conf: new bytestring syntax hex("..."), allow multiline strings

diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index 9025a84d..dd607380 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 *str = yytext;
+  size_t len;
   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++;
-  }
+  if (str[0] == 'h' && str[1] == 'e' && str[2] == 'x' && str[3] == ':')
+    str += 4;
+
+  errno = 0;
+  len = bstrhextobin(str, 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(str, bytes->data);
+  if (errno)
+    cf_error("Invalid hex string");
+
   cf_lval.bs = bytes;
   return BYTESTRING;
 }
@@ -361,7 +350,6 @@ else: {
   quoted_buffer_init();
 }
 
-<QUOTED>\n	cf_error("Unterminated string");
 <QUOTED><<EOF>> cf_error("Unterminated string");
 <QUOTED>["]	{
   BEGIN(INITIAL);
@@ -370,7 +358,7 @@ else: {
   return TEXT;
 }
 
-<QUOTED>.	BUFFER_PUSH(quoted_buffer) = yytext[0];
+<QUOTED>(.|\n)	BUFFER_PUSH(quoted_buffer) = yytext[0];
 
 <INITIAL,COMMENT><<EOF>>	{ if (check_eof()) return END; }
 
diff --git a/conf/confbase.Y b/conf/confbase.Y
index 3e8f5807..4ee628de 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -10,6 +10,7 @@ CF_HDR
 
 #define PARSER 1
 
+#include <errno.h>
 #include "nest/bird.h"
 #include "conf/conf.h"
 #include "lib/resource.h"
@@ -107,6 +108,7 @@ CF_DECLS
 %token <s> CF_SYM_KNOWN CF_SYM_UNDEFINED
 %token <t> TEXT
 %token <bs> BYTESTRING
+%type <bs> bytestring
 %type <iface> ipa_scope
 
 %type <i> expr bool pxlen4
@@ -131,9 +133,12 @@ CF_DECLS
 %start config
 
 CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM)
+CF_KEYWORDS(HEX)
 
 CF_GRAMMAR
 
+kw_sym: HEX ;
+
 /* Basic config file structure */
 
 config: conf_entries END { return 0; }
@@ -395,6 +400,27 @@ opttext:
  | /* empty */ { $$ = NULL; }
  ;
 
+bytestring:
+   BYTESTRING
+ | HEX '(' TEXT ')' {
+     size_t len;
+     struct bytestring *bytes;
+
+     errno = 0;
+     len = bstrhextobin($3, 0);
+     if (errno || len == (size_t)-1)
+       cf_error("Invalid hex string");
+
+     bytes = cfg_allocz(sizeof(*bytes) + len);
+     $$ = bytes;
+
+     bytes->length = len;
+     bstrhextobin($3, bytes->data);
+     if (errno)
+       cf_error("Invalid hex string");
+   }
+ ;
+
 
 CF_CODE
 
diff --git a/lib/Makefile b/lib/Makefile
index 812f721c..f83a34eb 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 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 strtobin.c strtoul.c tbf.c timer.c xmalloc.c
 obj := $(src-o-files)
 $(all-daemon)
 
diff --git a/lib/string.h b/lib/string.h
index 2829943d..51c407c1 100644
--- a/lib/string.h
+++ b/lib/string.h
@@ -33,6 +33,8 @@ u64 bstrtoul10(const char *str, char **end);
 u64 bstrtoul16(const char *str, char **end);
 byte bstrtobyte16(const char *str);
 
+int bstrhextobin(const char *s, byte *d);
+
 int patmatch(const byte *pat, const byte *str);
 
 static inline char *xbasename(const char *str)
diff --git a/lib/strtobin.c b/lib/strtobin.c
new file mode 100644
index 00000000..7a4a04a6
--- /dev/null
+++ b/lib/strtobin.c
@@ -0,0 +1,52 @@
+/*
+ *	BIRD Library -- Parse binary sequences
+ *
+ *	(c) 2023 TODO
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "nest/bird.h"
+#include "lib/string.h"
+
+#include <errno.h>
+
+static int
+is_alnum(char c)
+{
+  if (c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
+    return 1;
+  else
+    return 0;
+}
+
+int
+bstrhextobin(const char *s, byte *d)
+{
+  size_t len = 0;
+
+  while (*s) {
+    if (!is_alnum(s[0])) {
+      s++;
+      continue;
+    }
+
+    if (!is_alnum(s[1])) {
+      errno = ERANGE;
+      return -1;
+    }
+
+    if (d) {
+      errno = 0;
+      *d = bstrtobyte16(s);
+      if (errno)
+        return -1;
+      d++;
+    }
+
+    s += 2;
+    len++;
+  }
+
+  return len;
+}
diff --git a/nest/config.Y b/nest/config.Y
index c83c715b..6721dd6f 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -126,6 +126,7 @@ CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
 CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
 CF_KEYWORDS(CHECK, LINK)
 CF_KEYWORDS(SORTED, TRIE, MIN, MAX, SETTLE, TIME, GC, THRESHOLD, PERIOD)
+CF_KEYWORDS(HEX)
 
 /* For r_args_channel */
 CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
@@ -154,7 +155,7 @@ CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
 
 CF_GRAMMAR
 
-kw_sym: MIN MAX ;
+kw_sym: MIN | MAX ;
 
 /* Setting of router ID */
 
@@ -548,7 +549,7 @@ pass_key: PASSWORD | KEY;
 
 password_item_begin:
     pass_key text { init_password_list(); init_password($2, strlen($2), password_id++); }
-  | pass_key BYTESTRING { init_password_list(); init_password($2->data, $2->length, password_id++); }
+  | pass_key bytestring { init_password_list(); init_password($2->data, $2->length, password_id++); }
 ;
 
 password_item_params:
diff --git a/proto/radv/config.Y b/proto/radv/config.Y
index 5c213d50..8f5fb2d0 100644
--- a/proto/radv/config.Y
+++ b/proto/radv/config.Y
@@ -71,7 +71,7 @@ radv_proto_item:
  | PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); }
  | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_CFG->rdnss_list, &radv_dns_list); }
  | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_CFG->dnssl_list, &radv_dns_list); }
- | OTHER TYPE expr BYTESTRING { radv_add_to_custom_list(&RADV_CFG->custom_list, $3, $4); }
+ | OTHER TYPE expr bytestring { radv_add_to_custom_list(&RADV_CFG->custom_list, $3, $4); }
  | TRIGGER net_ip6 { RADV_CFG->trigger = $2; }
  | PROPAGATE ROUTES bool { RADV_CFG->propagate_routes = $3; }
  ;
@@ -136,7 +136,7 @@ radv_iface_item:
  | PREFIX radv_prefix { add_tail(&RADV_IFACE->pref_list, NODE this_radv_prefix); }
  | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_IFACE->rdnss_list, &radv_dns_list); }
  | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_IFACE->dnssl_list, &radv_dns_list); }
- | OTHER TYPE expr BYTESTRING { radv_add_to_custom_list(&RADV_IFACE->custom_list, $3, $4); }
+ | OTHER TYPE expr bytestring { radv_add_to_custom_list(&RADV_IFACE->custom_list, $3, $4); }
  | RDNSS LOCAL bool { RADV_IFACE->rdnss_local = $3; }
  | DNSSL LOCAL bool { RADV_IFACE->dnssl_local = $3; }
  | OTHER LOCAL bool { RADV_IFACE->custom_local = $3; }

Reply via email to