]> git.ozlabs.org Git - ccan/blobdiff - ccan/rune/rune.c
rune: new module to implement runes.
[ccan] / ccan / rune / rune.c
diff --git a/ccan/rune/rune.c b/ccan/rune/rune.c
new file mode 100644 (file)
index 0000000..36eaab9
--- /dev/null
@@ -0,0 +1,454 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#include "config.h"
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <ccan/endian/endian.h>
+#include <ccan/tal/str/str.h>
+#include <ccan/rune/rune.h>
+#include <ccan/rune/internal.h>
+
+/* Helper to produce an id field */
+static struct rune_restr *unique_id_restr(const tal_t *ctx,
+                                         const char *unique_id,
+                                         const char *version)
+{
+       const char *id;
+       struct rune_restr *restr;
+
+       assert(!strchr(unique_id, '-'));
+       if (version)
+               id = tal_fmt(NULL, "%s-%s", unique_id, version);
+       else
+               id = tal_strdup(NULL, unique_id);
+
+       restr = rune_restr_new(ctx);
+        /* We use the empty field for this, since it's always present. */
+       rune_restr_add_altern(restr,
+                             take(rune_altern_new(NULL, "", '=', take(id))));
+       return restr;
+}
+
+/* We pad between fields with something identical to the SHA end marker */
+void rune_sha256_endmarker(struct sha256_ctx *shactx)
+{
+       static const unsigned char pad[64] = {0x80};
+       be64 sizedesc;
+
+       sizedesc = cpu_to_be64((uint64_t)shactx->bytes << 3);
+       /* Add '1' bit to terminate, then all 0 bits, up to next block - 8. */
+       sha256_update(shactx, pad, 1 + ((128 - 8 - (shactx->bytes % 64) - 1) % 64));
+       /* Add number of bits of data (big endian) */
+       sha256_update(shactx, &sizedesc, 8);
+}
+
+struct rune *rune_new(const tal_t *ctx, const u8 *secret, size_t secret_len,
+                     const char *version)
+{
+       struct rune *rune = tal(ctx, struct rune);
+        assert(secret_len + 1 + 8 <= 64);
+
+       if (version)
+               rune->version = tal_strdup(rune, version);
+       else
+               rune->version = NULL;
+       rune->unique_id = NULL;
+       sha256_init(&rune->shactx);
+       sha256_update(&rune->shactx, secret, secret_len);
+       rune_sha256_endmarker(&rune->shactx);
+       rune->restrs = tal_arr(rune, struct rune_restr *, 0);
+       return rune;
+}
+
+struct rune *rune_dup(const tal_t *ctx, const struct rune *rune TAKES)
+{
+       struct rune *dup;
+
+       if (taken(rune))
+               return (struct rune *)rune;
+
+       dup = tal_dup(ctx, struct rune, rune);
+       dup->restrs = tal_arr(dup, struct rune_restr *, tal_count(rune->restrs));
+       for (size_t i = 0; i < tal_count(rune->restrs); i++) {
+               dup->restrs[i] = rune_restr_dup(dup->restrs,
+                                               rune->restrs[i]);
+       }
+       return dup;
+}
+
+struct rune *rune_derive_start(const tal_t *ctx,
+                              const struct rune *master,
+                              const char *unique_id TAKES)
+{
+       struct rune *rune = rune_dup(ctx, master);
+
+        /* If they provide a unique_id, it goes first. */
+       if (unique_id) {
+               if (taken(unique_id))
+                       rune->unique_id = unique_id;
+               else
+                       rune->unique_id = tal_strdup(rune, unique_id);
+               
+               rune_add_restr(rune, take(unique_id_restr(NULL,
+                                                         rune->unique_id,
+                                                         rune->version)));
+       } else {
+               assert(!rune->version);
+       }
+       return rune;
+}
+
+struct rune_altern *rune_altern_new(const tal_t *ctx,
+                                   const char *fieldname TAKES,
+                                   enum rune_condition condition,
+                                   const char *value TAKES)
+{
+       struct rune_altern *altern = tal(ctx, struct rune_altern);
+       altern->condition = condition;
+       altern->fieldname = tal_strdup(altern, fieldname);
+       altern->value = tal_strdup(altern, value);
+       return altern;
+}
+
+struct rune_altern *rune_altern_dup(const tal_t *ctx,
+                                   const struct rune_altern *altern TAKES)
+{
+       struct rune_altern *dup;
+
+       if (taken(altern))
+               return (struct rune_altern *)altern;;
+       dup = tal(ctx, struct rune_altern);
+       dup->condition = altern->condition;
+       dup->fieldname = tal_strdup(dup, altern->fieldname);
+       dup->value = tal_strdup(dup, altern->value);
+       return dup;
+}
+
+struct rune_restr *rune_restr_dup(const tal_t *ctx,
+                                 const struct rune_restr *restr TAKES)
+{
+       struct rune_restr *dup;
+       size_t num_altern;
+
+       if (taken(restr))
+               return (struct rune_restr *)restr;
+
+       num_altern = tal_count(restr->alterns);
+       dup = tal(ctx, struct rune_restr);
+       dup->alterns = tal_arr(dup, struct rune_altern *, num_altern);
+       for (size_t i = 0; i < num_altern; i++) {
+               dup->alterns[i] = rune_altern_dup(dup->alterns,
+                                                 restr->alterns[i]);
+       }
+       return dup;
+}
+
+struct rune_restr *rune_restr_new(const tal_t *ctx)
+{
+       struct rune_restr *restr = tal(ctx, struct rune_restr);
+       restr->alterns = tal_arr(restr, struct rune_altern *, 0);
+       return restr;
+}
+
+void rune_restr_add_altern(struct rune_restr *restr,
+                          const struct rune_altern *alt TAKES)
+{
+       size_t num = tal_count(restr->alterns);
+
+       tal_resize(&restr->alterns, num+1);
+       restr->alterns[num] = rune_altern_dup(restr->alterns, alt);
+}
+
+static bool is_unique_id(const struct rune_altern *alt)
+{
+       return streq(alt->fieldname, "");
+}
+       
+/* Return unique_id if valid, and sets *version */
+static const char *extract_unique_id(const tal_t *ctx,
+                                    const struct rune_altern *alt,
+                                    const char **version)
+{
+       size_t len;
+       /* Condition must be '='! */
+       if (alt->condition != '=')
+               return NULL;
+
+       len = strcspn(alt->value, "-");
+       if (alt->value[len])
+               *version = tal_strdup(ctx, alt->value + len + 1);
+       else
+               *version = NULL;
+       return tal_strndup(ctx, alt->value, len);
+}
+
+bool rune_add_restr(struct rune *rune,
+                   const struct rune_restr *restr TAKES)
+{
+       size_t num = tal_count(rune->restrs);
+
+       /* An empty fieldname is additional correctness checks */
+       for (size_t i = 0; i < tal_count(restr->alterns); i++) {
+               if (!is_unique_id(restr->alterns[i]))
+                       continue;
+
+               /* Must be the only alternative */
+               if (tal_count(restr->alterns) != 1)
+                       goto fail;
+               /* Must be the first restriction */
+               if (num != 0)
+                       goto fail;
+
+               rune->unique_id = extract_unique_id(rune,
+                                                   restr->alterns[i],
+                                                   &rune->version);
+               if (!rune->unique_id)
+                       goto fail;
+       }
+
+       tal_resize(&rune->restrs, num+1);
+       rune->restrs[num] = rune_restr_dup(rune->restrs, restr);
+
+       rune_sha256_add_restr(&rune->shactx, rune->restrs[num]);
+       return true;
+
+fail:
+       if (taken(restr))
+               tal_free(restr);
+       return false;
+}
+
+static const char *rune_restr_test(const tal_t *ctx,
+                                  const struct rune *rune,
+                                  const struct rune_restr *restr,
+                                  const char *(*check)(const tal_t *ctx,
+                                                       const struct rune *rune,
+                                                       const struct rune_altern *alt,
+                                                       void *arg),
+                                  void *arg)
+{
+       size_t num = tal_count(restr->alterns);
+       const char **errs = tal_arr(NULL, const char *, num);
+       char *err;
+
+       /* Only one alternative has to pass! */
+       for (size_t i = 0; i < num; i++) {
+               errs[i] = check(errs, rune, restr->alterns[i], arg);
+               if (!errs[i]) {
+                       tal_free(errs);
+                       return NULL;
+               }
+       }
+
+       err = tal_fmt(ctx, "%s", errs[0]);
+       for (size_t i = 1; i < num; i++)
+               tal_append_fmt(&err, " AND %s", errs[i]);
+       tal_free(errs);
+       return err;
+}
+
+static const char *cond_test(const tal_t *ctx,
+                            const struct rune_altern *alt,
+                            const char *complaint,
+                            bool cond)
+{
+       if (cond)
+               return NULL;
+
+       return tal_fmt(ctx, "%s %s %s", alt->fieldname, complaint, alt->value);
+}
+
+static const char *integer_compare_valid(const tal_t *ctx,
+                                        const s64 *fieldval_int,
+                                        const struct rune_altern *alt,
+                                        s64 *runeval_int)
+{
+       long l;
+       char *p;
+
+       if (!fieldval_int)
+               return tal_fmt(ctx, "%s is not an integer field",
+                              alt->fieldname);
+
+       errno = 0;
+       l = strtol(alt->value, &p, 10);
+       if (p == alt->value
+           || *p
+           || ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE))
+               return tal_fmt(ctx, "%s is not a valid integer", alt->value);
+
+       *runeval_int = l;
+       return NULL;
+}
+
+const char *rune_alt_single(const tal_t *ctx,
+                           const struct rune_altern *alt,
+                           const char *fieldval_str,
+                           const s64 *fieldval_int)
+{
+       char strfield[STR_MAX_CHARS(s64) + 1];
+       s64 runeval_int;
+       const char *err;
+       
+       /* Caller can't set both! */
+       if (fieldval_int) {
+               assert(!fieldval_str);
+               sprintf(strfield, "%"PRIi64, *fieldval_int);
+               fieldval_str = strfield;
+       }
+
+       switch (alt->condition) {
+       case RUNE_COND_IF_MISSING:
+               if (!fieldval_str)
+                       return NULL;
+               return tal_fmt(ctx, "%s is present", alt->fieldname);
+       case RUNE_COND_EQUAL:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is not equal to",
+                                streq(fieldval_str, alt->value));
+       case RUNE_COND_NOT_EQUAL:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is equal to",
+                                !streq(fieldval_str, alt->value));
+       case RUNE_COND_BEGINS:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "does not start with",
+                                strstarts(fieldval_str, alt->value));
+       case RUNE_COND_ENDS:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "does not end with",
+                                strends(fieldval_str, alt->value));
+       case RUNE_COND_CONTAINS:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "does not contain",
+                                strstr(fieldval_str, alt->value));
+       case RUNE_COND_INT_LESS:
+               err = integer_compare_valid(ctx, fieldval_int,
+                                           alt, &runeval_int);
+               if (err)
+                       return err;
+               return cond_test(ctx, alt, "is greater or equal to",
+                                *fieldval_int < runeval_int);
+       case RUNE_COND_INT_GREATER:
+               err = integer_compare_valid(ctx, fieldval_int,
+                                           alt, &runeval_int);
+               if (err)
+                       return err;
+               return cond_test(ctx, alt, "is less or equal to",
+                                *fieldval_int > runeval_int);
+       case RUNE_COND_LEXO_BEFORE:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is equal to or ordered after",
+                                strcmp(fieldval_str, alt->value) < 0);
+       case RUNE_COND_LEXO_AFTER:
+               if (!fieldval_str)
+                       return tal_fmt(ctx, "%s not present", alt->fieldname);
+               return cond_test(ctx, alt, "is equal to or ordered before",
+                                strcmp(fieldval_str, alt->value) > 0);
+       case RUNE_COND_COMMENT:
+               return NULL;
+       }
+       /* We should never create any other values! */
+       abort();
+}
+
+const char *rune_meets_criteria_(const tal_t *ctx,
+                                const struct rune *rune,
+                                const char *(*check)(const tal_t *ctx,
+                                                     const struct rune *rune,
+                                                     const struct rune_altern *alt,
+                                                     void *arg),
+                                void *arg)
+{
+       for (size_t i = 0; i < tal_count(rune->restrs); i++) {
+               const char *err;
+
+               /* Don't "check" unique id */
+               if (i == 0 && is_unique_id(rune->restrs[i]->alterns[0]))
+                       continue;
+               
+               err = rune_restr_test(ctx, rune, rune->restrs[i], check, arg);
+               if (err)
+                       return err;
+       }
+       return NULL;
+}
+
+const char *rune_test_(const tal_t *ctx,
+                      const struct rune *master,
+                      const struct rune *rune,
+                      const char *(*check)(const tal_t *ctx,
+                                           const struct rune *rune,
+                                           const struct rune_altern *alt,
+                                           void *arg),
+                      void *arg)
+{
+       const char *err;
+
+       err = rune_is_derived(master, rune);
+       if (err)
+               return err;
+       return rune_meets_criteria_(ctx, rune, check, arg);
+}
+
+bool rune_altern_eq(const struct rune_altern *alt1,
+                   const struct rune_altern *alt2)
+{
+       return alt1->condition == alt2->condition
+               && streq(alt1->fieldname, alt2->fieldname)
+               && streq(alt1->value, alt2->value);
+}
+
+bool rune_restr_eq(const struct rune_restr *rest1,
+                  const struct rune_restr *rest2)
+{
+       if (tal_count(rest1->alterns) != tal_count(rest2->alterns))
+               return false;
+
+       for (size_t i = 0; i < tal_count(rest1->alterns); i++)
+               if (!rune_altern_eq(rest1->alterns[i], rest2->alterns[i]))
+                       return false;
+       return true;
+}
+
+/* Equal, as in both NULL, or both non-NULL and matching */
+bool runestr_eq(const char *a, const char *b)
+{
+       if (a) {
+               if (!b)
+                       return false;
+               return streq(a, b);
+       } else
+               return b == NULL;
+}
+
+bool rune_eq(const struct rune *rune1, const struct rune *rune2)
+{
+       if (!runestr_eq(rune1->unique_id, rune2->unique_id))
+               return false;
+       if (!runestr_eq(rune1->version, rune2->version))
+               return false;
+
+       if (memcmp(rune1->shactx.s, rune2->shactx.s, sizeof(rune1->shactx.s)))
+               return false;
+       if (rune1->shactx.bytes != rune2->shactx.bytes)
+               return false;
+       if (memcmp(rune1->shactx.buf.u8, rune2->shactx.buf.u8,
+                  rune1->shactx.bytes % 64))
+               return false;
+
+       if (tal_count(rune1->restrs) != tal_count(rune2->restrs))
+               return false;
+
+       for (size_t i = 0; i < tal_count(rune1->restrs); i++)
+               if (!rune_restr_eq(rune1->restrs[i], rune2->restrs[i]))
+                       return false;
+       return true;
+}