wasm: net.cidr_contains, net.cidr_intersects, and net.cidr_overlap builtins.

Signed-off-by: Teemu Koponen <koponen@styra.com>
This commit is contained in:
Teemu Koponen
2020-10-24 21:49:56 -07:00
committed by Torin Sandall
parent 52ddaca92b
commit 78d9bf1aed
8 changed files with 465 additions and 66 deletions
File diff suppressed because one or more lines are too long
Binary file not shown.
+68 -65
View File
@@ -58,71 +58,74 @@ const (
)
var builtinsFunctions = map[string]string{
ast.Plus.Name: "opa_arith_plus",
ast.Minus.Name: "opa_arith_minus",
ast.Multiply.Name: "opa_arith_multiply",
ast.Divide.Name: "opa_arith_divide",
ast.Abs.Name: "opa_arith_abs",
ast.Round.Name: "opa_arith_round",
ast.Rem.Name: "opa_arith_rem",
ast.ArrayConcat.Name: "opa_array_concat",
ast.ArraySlice.Name: "opa_array_slice",
ast.SetDiff.Name: "opa_set_diff",
ast.And.Name: "opa_set_intersection",
ast.Or.Name: "opa_set_union",
ast.Intersection.Name: "opa_sets_intersection",
ast.Union.Name: "opa_sets_union",
ast.IsNumber.Name: "opa_types_is_number",
ast.IsString.Name: "opa_types_is_string",
ast.IsBoolean.Name: "opa_types_is_boolean",
ast.IsArray.Name: "opa_types_is_array",
ast.IsSet.Name: "opa_types_is_set",
ast.IsObject.Name: "opa_types_is_object",
ast.IsNull.Name: "opa_types_is_null",
ast.TypeNameBuiltin.Name: "opa_types_name",
ast.BitsOr.Name: "opa_bits_or",
ast.BitsAnd.Name: "opa_bits_and",
ast.BitsNegate.Name: "opa_bits_negate",
ast.BitsXOr.Name: "opa_bits_xor",
ast.BitsShiftLeft.Name: "opa_bits_shiftleft",
ast.BitsShiftRight.Name: "opa_bits_shiftright",
ast.Count.Name: "opa_agg_count",
ast.Sum.Name: "opa_agg_sum",
ast.Product.Name: "opa_agg_product",
ast.Max.Name: "opa_agg_max",
ast.Min.Name: "opa_agg_min",
ast.Sort.Name: "opa_agg_sort",
ast.All.Name: "opa_agg_all",
ast.Any.Name: "opa_agg_any",
ast.Base64IsValid.Name: "opa_base64_is_valid",
ast.Base64Decode.Name: "opa_base64_decode",
ast.Base64Encode.Name: "opa_base64_encode",
ast.Base64UrlEncode.Name: "opa_base64_url_encode",
ast.Base64UrlDecode.Name: "opa_base64_url_decode",
ast.JSONMarshal.Name: "opa_json_marshal",
ast.JSONUnmarshal.Name: "opa_json_unmarshal",
ast.ObjectFilter.Name: "opa_object_filter",
ast.Concat.Name: "opa_strings_concat",
ast.FormatInt.Name: "opa_strings_format_int",
ast.IndexOf.Name: "opa_strings_indexof",
ast.Substring.Name: "opa_strings_substring",
ast.Lower.Name: "opa_strings_lower",
ast.Upper.Name: "opa_strings_upper",
ast.Contains.Name: "opa_strings_contains",
ast.StartsWith.Name: "opa_strings_startswith",
ast.EndsWith.Name: "opa_strings_endswith",
ast.Split.Name: "opa_strings_split",
ast.Replace.Name: "opa_strings_replace",
ast.ReplaceN.Name: "opa_strings_replace_n",
ast.Trim.Name: "opa_strings_trim",
ast.TrimLeft.Name: "opa_strings_trim_left",
ast.TrimPrefix.Name: "opa_strings_trim_prefix",
ast.TrimRight.Name: "opa_strings_trim_right",
ast.TrimSuffix.Name: "opa_strings_trim_suffix",
ast.TrimSpace.Name: "opa_strings_trim_space",
ast.NumbersRange.Name: "opa_numbers_range",
ast.ToNumber.Name: "opa_to_number",
ast.WalkBuiltin.Name: "opa_value_transitive_closure",
ast.Plus.Name: "opa_arith_plus",
ast.Minus.Name: "opa_arith_minus",
ast.Multiply.Name: "opa_arith_multiply",
ast.Divide.Name: "opa_arith_divide",
ast.Abs.Name: "opa_arith_abs",
ast.Round.Name: "opa_arith_round",
ast.Rem.Name: "opa_arith_rem",
ast.ArrayConcat.Name: "opa_array_concat",
ast.ArraySlice.Name: "opa_array_slice",
ast.SetDiff.Name: "opa_set_diff",
ast.And.Name: "opa_set_intersection",
ast.Or.Name: "opa_set_union",
ast.Intersection.Name: "opa_sets_intersection",
ast.Union.Name: "opa_sets_union",
ast.IsNumber.Name: "opa_types_is_number",
ast.IsString.Name: "opa_types_is_string",
ast.IsBoolean.Name: "opa_types_is_boolean",
ast.IsArray.Name: "opa_types_is_array",
ast.IsSet.Name: "opa_types_is_set",
ast.IsObject.Name: "opa_types_is_object",
ast.IsNull.Name: "opa_types_is_null",
ast.TypeNameBuiltin.Name: "opa_types_name",
ast.BitsOr.Name: "opa_bits_or",
ast.BitsAnd.Name: "opa_bits_and",
ast.BitsNegate.Name: "opa_bits_negate",
ast.BitsXOr.Name: "opa_bits_xor",
ast.BitsShiftLeft.Name: "opa_bits_shiftleft",
ast.BitsShiftRight.Name: "opa_bits_shiftright",
ast.Count.Name: "opa_agg_count",
ast.Sum.Name: "opa_agg_sum",
ast.Product.Name: "opa_agg_product",
ast.Max.Name: "opa_agg_max",
ast.Min.Name: "opa_agg_min",
ast.Sort.Name: "opa_agg_sort",
ast.All.Name: "opa_agg_all",
ast.Any.Name: "opa_agg_any",
ast.Base64IsValid.Name: "opa_base64_is_valid",
ast.Base64Decode.Name: "opa_base64_decode",
ast.Base64Encode.Name: "opa_base64_encode",
ast.Base64UrlEncode.Name: "opa_base64_url_encode",
ast.Base64UrlDecode.Name: "opa_base64_url_decode",
ast.NetCIDRContains.Name: "opa_cidr_contains",
ast.NetCIDROverlap.Name: "opa_cidr_contains",
ast.NetCIDRIntersects.Name: "opa_cidr_intersects",
ast.JSONMarshal.Name: "opa_json_marshal",
ast.JSONUnmarshal.Name: "opa_json_unmarshal",
ast.ObjectFilter.Name: "opa_object_filter",
ast.Concat.Name: "opa_strings_concat",
ast.FormatInt.Name: "opa_strings_format_int",
ast.IndexOf.Name: "opa_strings_indexof",
ast.Substring.Name: "opa_strings_substring",
ast.Lower.Name: "opa_strings_lower",
ast.Upper.Name: "opa_strings_upper",
ast.Contains.Name: "opa_strings_contains",
ast.StartsWith.Name: "opa_strings_startswith",
ast.EndsWith.Name: "opa_strings_endswith",
ast.Split.Name: "opa_strings_split",
ast.Replace.Name: "opa_strings_replace",
ast.ReplaceN.Name: "opa_strings_replace_n",
ast.Trim.Name: "opa_strings_trim",
ast.TrimLeft.Name: "opa_strings_trim_left",
ast.TrimPrefix.Name: "opa_strings_trim_prefix",
ast.TrimRight.Name: "opa_strings_trim_right",
ast.TrimSuffix.Name: "opa_strings_trim_suffix",
ast.TrimSpace.Name: "opa_strings_trim_space",
ast.NumbersRange.Name: "opa_numbers_range",
ast.ToNumber.Name: "opa_to_number",
ast.WalkBuiltin.Name: "opa_value_transitive_closure",
}
var builtinDispatchers = [...]string{
+9
View File
@@ -128,6 +128,15 @@ cases:
- note: base64url.encode built-in
query: base64url.encode("Hello",x)
want_result: [{'x': "SGVsbG8="}]
- note: net.cidr_contains
query: net.cidr_contains("172.17.0.0/24", "172.17.0.0/16", x)
want_result: [{'x': false}]
- note: net.cidr_contains
query: net.cidr_overlap("172.17.0.0/16", "172.17.0.1", x)
want_result: [{'x': true}]
- note: net.cidr_intersects
query: net.cidr_intersects("192.168.1.0/25", "192.168.1.64/25", x)
want_result: [{'x': true}]
- note: json.marshal built-in
query: json.marshal("string",x)
want_result: [{'x': '"string"'}]
+337
View File
@@ -0,0 +1,337 @@
#include <stddef.h>
#include <string.h>
#include "str.h"
#include "value.h"
typedef struct {
uint8_t len;
unsigned char ip[16];
unsigned char mask[16];
} ip_net;
typedef unsigned char u_char;
typedef unsigned int u_int;
static int inet_pton4(const char *src, const char *end, u_char *dst);
static int inet_pton6(const char *src, const char *end, u_char *dst);
static bool parse_ip(const char *src, int n, ip_net *dst)
{
for (int i = 0; i < n; i++) {
if (src[i] == '.')
{
dst->len = 4;
memset(dst->mask, 0xff, 4);
return inet_pton4(src, src + n, dst->ip);
}
else if (src[i] == ':')
{
dst->len = 16;
memset(dst->mask, 0xff, 16);
return inet_pton6(src, src + n, dst->ip);
}
}
return FALSE;
}
static bool parse_cidr(const char *src, size_t n, ip_net *dst)
{
const char *slash = NULL;
for (size_t i = 0; i < n; i++)
{
if (src[i] == '/')
{
slash = &src[i];
break;
}
}
if (slash == NULL)
{
return FALSE;
}
const char *addr = src;
const size_t len = slash - src;
if (!parse_ip(addr, len, dst))
{
return FALSE;
}
const char *mask = slash + 1;
long long bits;
if (opa_atoi64(mask, n - len - 1, &bits) == -1 || bits < 0 || bits > dst->len*8)
{
return FALSE;
}
for (int i = 0; i < dst->len; i++)
{
if (bits >= 8) {
dst->mask[i] = 0xff;
bits -= 8;
continue;
}
dst->mask[i] = ~((unsigned char)(0xff >> bits));
bits = 0;
}
for (int i = 0; i < dst->len; i++)
{
dst->ip[i] &= dst->mask[i];
}
return TRUE;
}
// returns true if a contains b.
static bool contains(ip_net *a, ip_net *b)
{
if (a->len != b->len)
{
return FALSE;
}
for (int i = 0; i < a->len; i++)
{
if (a->mask[i] & ~b->mask[i])
{
// If b mask is shorter, a can never contain b. For
// example, 192.168/16 (a) doesn't contain 192.168/15 (b).
// The above logical operation checks if the b mask is
// shorter. For example, consider the masks a and b:
//
// | mask
// -------+----------
// a | 11111000
// b | 11110000
// ~b | 00001111
// a & ~b | 00001000
//
return FALSE;
}
// Since b mask may be longer than a, use the a mask to ignore
// the b bits not relevant for comparison. Note, ips are
// already masked at the construction time with their own
// masks (see L84 above) so they don't have bits beyond their
// mask length.
if (a->ip[i] != (b->ip[i] & a->mask[i]))
{
return FALSE;
}
}
return TRUE;
}
opa_value *opa_cidr_contains(opa_value *a, opa_value *b)
{
if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING)
{
return NULL;
}
ip_net ip_a, ip_b;
opa_string_t *s = opa_cast_string(a);
if (!parse_cidr(s->v, s->len, &ip_a)) {
return NULL;
}
s = opa_cast_string(b);
if (!parse_ip(s->v, s->len, &ip_b) && !parse_cidr(s->v, s->len, &ip_b)) {
return NULL;
}
return opa_boolean(contains(&ip_a, &ip_b) ? TRUE : FALSE);
}
opa_value *opa_cidr_intersects(opa_value *a, opa_value *b)
{
if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING)
{
return NULL;
}
opa_string_t *as = opa_cast_string(a);
opa_string_t *bs = opa_cast_string(b);
ip_net ip_a, ip_b;
if (!parse_cidr(as->v, as->len, &ip_a) || !parse_cidr(bs->v, bs->len, &ip_b)) {
return NULL;
}
return opa_boolean(contains(&ip_a, &ip_b) || contains(&ip_b, &ip_a) ? TRUE : FALSE);
}
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996,1999 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* int
* inet_pton4(src, dst)
* like inet_aton() but without all the hexadecimal and shorthand.
* return:
* 1 if `src' is a valid dotted quad, else 0.
* notice:
* does not touch `dst' unless it's returning 1.
* author:
* Paul Vixie, 1996.
*/
static int
inet_pton4(const char *src, const char *stop, u_char *dst)
{
static const char digits[] = "0123456789";
int saw_digit, octets, ch;
#define NS_INADDRSZ 4
u_char tmp[NS_INADDRSZ], *tp;
saw_digit = 0;
octets = 0;
*(tp = tmp) = 0;
while (src != stop && (ch = *src++) != '\0') {
const char *pch;
if ((pch = strchr(digits, ch)) != NULL) {
u_int new = *tp * 10 + (pch - digits);
if (saw_digit && *tp == 0)
return (0);
if (new > 255)
return (0);
*tp = new;
if (!saw_digit) {
if (++octets > 4)
return (0);
saw_digit = 1;
}
} else if (ch == '.' && saw_digit) {
if (octets == 4)
return (0);
*++tp = 0;
saw_digit = 0;
} else
return (0);
}
if (octets < 4)
return (0);
memcpy(dst, tmp, NS_INADDRSZ);
return (1);
}
/* int
* inet_pton6(src, dst)
* convert presentation level address to network order binary form.
* return:
* 1 if `src' is a valid [RFC1884 2.2] address, else 0.
* notice:
* (1) does not touch `dst' unless it's returning 1.
* (2) :: in a full address is silently ignored.
* credit:
* inspired by Mark Andrews.
* author:
* Paul Vixie, 1996.
*/
static int
inet_pton6(const char *src, const char *stop, u_char *dst)
{
static const char xdigits_l[] = "0123456789abcdef",
xdigits_u[] = "0123456789ABCDEF";
#define NS_IN6ADDRSZ 16
#define NS_INT16SZ 2
u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
const char *xdigits, *curtok, *end;
int ch, seen_xdigits;
u_int val;
memset((tp = tmp), '\0', NS_IN6ADDRSZ);
endp = tp + NS_IN6ADDRSZ;
colonp = NULL;
/* Leading :: requires some special handling. */
if (*src == ':')
if (*++src != ':')
return (0);
curtok = src;
seen_xdigits = 0;
val = 0;
while (src != stop && (ch = *src++) != '\0') {
const char *pch;
if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
pch = strchr((xdigits = xdigits_u), ch);
if (pch != NULL) {
val <<= 4;
val |= (pch - xdigits);
if (++seen_xdigits > 4)
return (0);
continue;
}
if (ch == ':') {
curtok = src;
if (!seen_xdigits) {
if (colonp)
return (0);
colonp = tp;
continue;
} else if (*src == '\0') {
return (0);
}
if (tp + NS_INT16SZ > endp)
return (0);
*tp++ = (u_char) (val >> 8) & 0xff;
*tp++ = (u_char) val & 0xff;
seen_xdigits = 0;
val = 0;
continue;
}
if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
inet_pton4(curtok, stop, tp) > 0) {
tp += NS_INADDRSZ;
seen_xdigits = 0;
break; /*%< '\\0' was seen by inet_pton4(). */
}
return (0);
}
if (seen_xdigits) {
if (tp + NS_INT16SZ > endp)
return (0);
*tp++ = (u_char) (val >> 8) & 0xff;
*tp++ = (u_char) val & 0xff;
}
if (colonp != NULL) {
/*
* Since some memmove()'s erroneously fail to handle
* overlapping regions, we'll do the shift by hand.
*/
const int n = tp - colonp;
int i;
if (tp == endp)
return (0);
for (i = 1; i <= n; i++) {
endp[- i] = colonp[n - i];
colonp[n - i] = 0;
}
tp = endp;
}
if (tp != endp)
return (0);
memcpy(dst, tmp, NS_IN6ADDRSZ);
return (1);
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef OPA_CIDR_H
#define OPA_CIDR_H
#include "value.h"
opa_value *opa_cidr_contains(opa_value *net, opa_value *ip_or_net);
opa_value *opa_cidr_intersects(opa_value *a, opa_value *b);
#endif
+17
View File
@@ -2,6 +2,23 @@
#include "../malloc.h"
void *memchr(const void *src, int c, size_t n)
{
const unsigned char *s = src;
while (n--)
{
if (*s == (unsigned char)c)
{
return (void *)s;
}
s++;
}
return NULL;
}
void *memcpy(void *dest, const void *src, size_t n)
{
unsigned char *d = dest;
+24
View File
@@ -4,6 +4,7 @@
#include "arithmetic.h"
#include "array.h"
#include "bits-builtins.h"
#include "cidr.h"
#include "conversions.h"
#include "encoding.h"
#include "json.h"
@@ -1936,6 +1937,29 @@ void test_to_number(void)
test("to_number/bad value", opa_to_number(opa_array()) == NULL);
}
void test_cidr_contains(void)
{
test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("10.0.0.0/8"), opa_string_terminated("10.1.0.0/24")), opa_boolean(TRUE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("172.17.0.0/24"), opa_string_terminated("172.17.0.0/16")), opa_boolean(FALSE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("10.0.0.0/8"), opa_string_terminated("192.168.1.0/24")), opa_boolean(FALSE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("10.0.0.0/8"), opa_string_terminated("10.1.1.1/32")), opa_boolean(TRUE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("2001:4860:4860::8888/32"), opa_string_terminated("2001:4860:4860:1234::8888/40")), opa_boolean(TRUE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("2001:4860:4860::8888/32"), opa_string_terminated("2001:4860:4860:1234:5678:1234:5678:8888/128")), opa_boolean(TRUE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("2001:4860::/96"), opa_string_terminated("2001:4860::/32")), opa_boolean(FALSE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("2001:4860::/32"), opa_string_terminated("fd1e:5bfe:8af3:9ddc::/64")), opa_boolean(FALSE)) == 0);
test("cidr/contains", opa_cidr_contains(opa_string_terminated("not-a-cidr"), opa_string_terminated("192.168.1.67")) == NULL);
test("cidr/contains", opa_cidr_contains(opa_string_terminated("192.168.1.0/28"), opa_string_terminated("not-a-cidr")) == NULL);
test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("10.0.0.0/8"), opa_string_terminated("10.1.2.3")), opa_boolean(TRUE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("10.0.0.0/8"), opa_string_terminated("192.168.1.1")), opa_boolean(FALSE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_intersects(opa_string_terminated("192.168.1.0/25"), opa_string_terminated("192.168.1.64/25")), opa_boolean(TRUE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_intersects(opa_string_terminated("192.168.1.0/24"), opa_string_terminated("192.168.2.0/24")), opa_boolean(FALSE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_intersects(opa_string_terminated("fd1e:5bfe:8af3:9ddc::/64"), opa_string_terminated("fd1e:5bfe:8af3:9ddc:1111::/72")), opa_boolean(TRUE)) == 0);
test("cidr/contains", opa_value_compare(opa_cidr_intersects(opa_string_terminated("fd1e:5bfe:8af3:9ddc::/64"), opa_string_terminated("2001:4860:4860::8888/32")), opa_boolean(FALSE)) == 0);
test("cidr/contains", opa_cidr_intersects(opa_string_terminated("not-a-cidr"), opa_string_terminated("192.168.1.0/24")) == NULL);
test("cidr/contains", opa_cidr_intersects(opa_string_terminated("192.168.1.0/28"), opa_string_terminated("not-a-cidr")) == NULL);
}
opa_value *__new_value_path(int sz, ...)
{
va_list ap;