krb5 commit [json]: Add internal JSON encoding and decoding support

Greg Hudson ghudson at MIT.EDU
Tue Aug 14 13:08:07 EDT 2012


https://github.com/krb5/krb5/commit/bca83b4d0a3c511daf9fe0f3615fe3352acbcdd8
commit bca83b4d0a3c511daf9fe0f3615fe3352acbcdd8
Author: Greg Hudson <ghudson at mit.edu>
Date:   Thu Aug 9 18:05:50 2012 -0400

    Add internal JSON encoding and decoding support

 src/include/k5-json.h        |  154 +++++++
 src/include/k5-platform.h    |    6 +
 src/util/support/Makefile.in |   15 +-
 src/util/support/json.c      |  955 ++++++++++++++++++++++++++++++++++++++++++
 src/util/support/t_json.c    |  288 +++++++++++++
 5 files changed, 1416 insertions(+), 2 deletions(-)

diff --git a/src/include/k5-json.h b/src/include/k5-json.h
new file mode 100644
index 0000000..d4f523f
--- /dev/null
+++ b/src/include/k5-json.h
@@ -0,0 +1,154 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/k5-json.h - JSON declarations */
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef K5_JSON_H
+#define K5_JSON_H 1
+
+typedef void *k5_json_value;
+typedef unsigned int k5_json_tid;
+
+void *k5_json_retain(k5_json_value val);
+void k5_json_release(k5_json_value val);
+
+k5_json_tid k5_json_get_tid(k5_json_value val);
+
+/*
+ * Null
+ */
+
+typedef struct k5_json_null_st *k5_json_null;
+
+k5_json_null k5_json_null_create(void);
+k5_json_tid k5_json_null_tid(void);
+
+/*
+ * Boolean
+ */
+
+typedef struct k5_json_bool_st *k5_json_bool;
+
+k5_json_bool k5_json_bool_create(int truth);
+int k5_json_bool_val(k5_json_bool bval);
+k5_json_tid k5_json_bool_tid(void);
+
+/*
+ * Array
+ */
+
+typedef struct k5_json_array_st *k5_json_array;
+
+k5_json_array k5_json_array_create(void);
+k5_json_tid k5_json_array_tid(void);
+int k5_json_array_append(k5_json_array array, k5_json_value val);
+size_t k5_json_array_length(k5_json_array array);
+k5_json_value k5_json_array_get(k5_json_array array, size_t idx);
+void k5_json_array_set(k5_json_array array, size_t idx, k5_json_value val);
+
+/*
+ * Object
+ */
+
+typedef struct k5_json_object_st *k5_json_object;
+typedef void (*k5_json_object_iterator_fn)(void *arg, const char *key,
+                                           k5_json_value val);
+
+k5_json_object k5_json_object_create(void);
+k5_json_tid k5_json_object_tid(void);
+int k5_json_object_set(k5_json_object obj, const char *key, k5_json_value val);
+void k5_json_object_iterate(k5_json_object obj,
+                            k5_json_object_iterator_fn func, void *arg);
+k5_json_value k5_json_object_get(k5_json_object obj, const char *key);
+
+/*
+ * String
+ */
+
+typedef struct k5_json_string_st *k5_json_string;
+
+k5_json_string k5_json_string_create(const char *string);
+k5_json_string k5_json_string_create_len(const void *data, size_t len);
+k5_json_tid k5_json_string_tid(void);
+const char *k5_json_string_utf8(k5_json_string string);
+
+/*
+ * Number
+ */
+
+typedef struct k5_json_number_st *k5_json_number;
+
+k5_json_number k5_json_number_create(long long number);
+k5_json_tid k5_json_number_tid(void);
+long long k5_json_number_value(k5_json_number number);
+
+/*
+ * JSON encoding and decoding
+ */
+
+char *k5_json_encode(k5_json_value val);
+void k5_json_free_encoding(char *str);
+k5_json_value k5_json_decode(const char *str);
+
+#endif /* K5_JSON_H */
diff --git a/src/include/k5-platform.h b/src/include/k5-platform.h
index c927291..e4c3392 100644
--- a/src/include/k5-platform.h
+++ b/src/include/k5-platform.h
@@ -421,12 +421,18 @@ typedef struct { int error; unsigned char did_run; } k5_init_t;
 # endif
 # define INT64_TYPE int64_t
 # define UINT64_TYPE uint64_t
+# define INT64_FMT PRId64
+# define UINT64_FMT PRIu64
 #elif defined(_WIN32)
 # define INT64_TYPE signed __int64
 # define UINT64_TYPE unsigned __int64
+# define INT64_FMT "I64d"
+# define UINT64_FMT "I64u"
 #else /* not Windows, and neither stdint.h nor inttypes.h */
 # define INT64_TYPE signed long long
 # define UINT64_TYPE unsigned long long
+# define INT64_FMT "lld"
+# define UINT64_FMT "llu"
 #endif
 
 #ifndef SIZE_MAX
diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in
index 6ac9175..e990986 100644
--- a/src/util/support/Makefile.in
+++ b/src/util/support/Makefile.in
@@ -74,6 +74,7 @@ STLIBOBJS= \
 	utf8_conv.o \
 	zap.o \
 	path.o \
+	json.o \
 	$(GETTIMEOFDAY_ST_OBJ) \
 	$(IPC_ST_OBJ) \
 	$(STRLCPY_ST_OBJ) \
@@ -93,6 +94,7 @@ LIBOBJS= \
 	$(OUTPRE)utf8_conv.$(OBJEXT) \
 	$(OUTPRE)zap.$(OBJEXT) \
 	$(OUTPRE)path.$(OBJEXT) \
+	$(OUTPRE)json.$(OBJEXT) \
 	$(GETTIMEOFDAY_OBJ) \
 	$(IPC_OBJ) \
 	$(STRLCPY_OBJ) \
@@ -122,8 +124,11 @@ SRCS=\
 	$(srcdir)/mkstemp.c \
 	$(srcdir)/t_k5buf.c \
 	$(srcdir)/t_unal.c \
+	$(srcdir)/t_path.c \
+	$(srcdir)/t_json.c \
 	$(srcdir)/zap.c \
-	$(srcdir)/path.c
+	$(srcdir)/path.c \
+	$(srcdir)/json.c
 
 SHLIB_EXPDEPS =
 # Add -lm if dumping thread stats, for sqrt.
@@ -186,15 +191,21 @@ t_path_win.o: $(srcdir)/t_path.c
 path_win.o: $(srcdir)/path.c
 	$(CC) $(ALL_CFLAGS) -DWINDOWS_PATHS -c $(srcdir)/path.c -o $@
 
+T_JSON_OBJS= t_json.o json.o k5buf.o $(PRINTF_ST_OBJ)
+
+t_json: $(T_JSON_OBJS)
+	$(CC_LINK) -o $@ $(T_JSON_OBJS)
+
 t_unal: t_unal.o
 	$(CC_LINK) -o t_unal t_unal.o
 
-TEST_PROGS= t_k5buf t_path t_path_win t_unal
+TEST_PROGS= t_k5buf t_path t_path_win t_json t_unal
 
 check-unix:: $(TEST_PROGS)
 	./t_k5buf
 	./t_path
 	./t_path_win
+	./t_json
 	./t_unal
 
 clean::
diff --git a/src/util/support/json.c b/src/util/support/json.c
new file mode 100644
index 0000000..75a5b5a
--- /dev/null
+++ b/src/util/support/json.c
@@ -0,0 +1,955 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/json.c - JSON parser and unparser */
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file implements a minimal dynamic type system for JSON values and a
+ * JSON encoder and decoder.  It is loosely based on the heimbase code from
+ * Heimdal.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <k5-json.h>
+#include <k5-buf.h>
+
+#define MAX_DECODE_DEPTH 64
+
+typedef void (*type_dealloc_fn)(void *val);
+
+typedef struct json_type_st {
+    k5_json_tid tid;
+    const char *name;
+    type_dealloc_fn dealloc;
+} *json_type;
+
+enum {
+    TID_NUMBER = 0,
+    TID_NULL = 1,
+    TID_BOOL = 2,
+    TID_MEMORY = 128,
+    TID_ARRAY = 129,
+    TID_OBJECT = 130,
+    TID_STRING = 131,
+};
+
+struct value_base {
+    json_type isa;
+    unsigned int ref_cnt;
+};
+
+#define PTR2BASE(ptr) (((struct value_base *)ptr) - 1)
+#define BASE2PTR(ptr) ((void *)(((struct value_base *)ptr) + 1))
+
+/* Increase reference count on a k5_json_value. */
+void *
+k5_json_retain(k5_json_value val)
+{
+    struct value_base *p;
+
+    if (val == NULL)
+        return val;
+    p = PTR2BASE(val);
+    assert(p->ref_cnt != 0);
+    p->ref_cnt++;
+    return val;
+}
+
+/* Release a k5_json_value; free if the reference count reaches zero. */
+void
+k5_json_release(k5_json_value val)
+{
+    struct value_base *p;
+
+    if (val == NULL)
+        return;
+    p = PTR2BASE(val);
+    assert(p->ref_cnt != 0);
+    p->ref_cnt--;
+    if (p->ref_cnt == 0) {
+        if (p->isa->dealloc != NULL)
+            p->isa->dealloc(val);
+        free(p);
+    }
+}
+
+/* Get the type description of a k5_json_value. */
+static json_type
+get_isa(k5_json_value val)
+{
+    struct value_base *p = PTR2BASE(val);
+
+    return p->isa;
+}
+
+/* Get the type ID of a k5_json_value. */
+k5_json_tid
+k5_json_get_tid(k5_json_value val)
+{
+    json_type isa = get_isa(val);
+
+    return isa->tid;
+}
+
+static k5_json_value
+alloc_value(json_type type, size_t size)
+{
+    struct value_base *p = calloc(1, size + sizeof(*p));
+
+    if (p == NULL)
+        return NULL;
+    p->isa = type;
+    p->ref_cnt = 1;
+
+    return BASE2PTR(p);
+}
+
+/*** Null type ***/
+
+static struct json_type_st null_type = { TID_NULL, "null", NULL };
+
+k5_json_null
+k5_json_null_create(void)
+{
+    return alloc_value(&null_type, 0);
+}
+
+/* Get the type ID of the null type. */
+k5_json_tid
+k5_json_null_tid(void)
+{
+    return TID_NULL;
+}
+
+/*** Boolean type ***/
+
+static struct json_type_st bool_type = { TID_BOOL, "bool", NULL };
+
+k5_json_bool
+k5_json_bool_create(int truth)
+{
+    k5_json_bool b;
+
+    b = alloc_value(&bool_type, 1);
+    *(unsigned char *)b = !!truth;
+    return b;
+}
+
+int
+k5_json_bool_val(k5_json_bool bval)
+{
+    return *(unsigned char *)bval;
+}
+
+/* Get the type ID of the boolean type. */
+k5_json_tid
+k5_json_bool_tid(void)
+{
+    return TID_BOOL;
+}
+
+/*** Array type ***/
+
+struct k5_json_array_st {
+    k5_json_value *values;
+    size_t len;
+    size_t allocated;
+};
+
+static void
+array_dealloc(void *ptr)
+{
+    k5_json_array array = ptr;
+    size_t i;
+
+    for (i = 0; i < array->len; i++)
+        k5_json_release(array->values[i]);
+    free(array->values);
+}
+
+static struct json_type_st array_type = { TID_ARRAY, "array", array_dealloc };
+
+/* Allocate an array object. */
+k5_json_array
+k5_json_array_create(void)
+{
+    return alloc_value(&array_type, sizeof(struct k5_json_array_st));
+}
+
+/* Get the type ID of the array type. */
+k5_json_tid
+k5_json_array_tid(void)
+{
+    return TID_ARRAY;
+}
+
+/* Append a value to an array. */
+int
+k5_json_array_append(k5_json_array array, k5_json_value val)
+{
+    k5_json_value *ptr;
+    size_t new_alloc;
+
+    if (array->len >= array->allocated) {
+        /* Increase the number of slots by 50% (16 slots minimum). */
+        new_alloc = array->len + 1 + (array->len >> 1);
+        if (new_alloc < 16)
+            new_alloc = 16;
+        ptr = realloc(array->values, new_alloc * sizeof(*array->values));
+        if (ptr == NULL)
+            return ENOMEM;
+        array->values = ptr;
+        array->allocated = new_alloc;
+    }
+    array->values[array->len++] = k5_json_retain(val);
+    return 0;
+}
+
+/* Get the length of array. */
+size_t
+k5_json_array_length(k5_json_array array)
+{
+    return array->len;
+}
+
+/* Get an alias to the value of element at array index.  Do not release this or
+ * use it beyond the lifetime of array without retaining it first. */
+k5_json_value
+k5_json_array_get(k5_json_array array, size_t idx)
+{
+    if (idx >= array->len)
+        abort();
+    return array->values[idx];
+}
+
+/* Set a value at an existing array index.  Retains val (i.e. does not take
+ * ownership of the caller's reference count on val). */
+void
+k5_json_array_set(k5_json_array array, size_t idx, k5_json_value val)
+{
+    if (idx >= array->len)
+        abort();
+    k5_json_release(array->values[idx]);
+    array->values[idx] = k5_json_retain(val);
+}
+
+/*** Object type (string:value mapping) ***/
+
+struct entry {
+    char *key;
+    k5_json_value value;
+};
+
+struct k5_json_object_st {
+    struct entry *entries;
+    size_t len;
+    size_t allocated;
+};
+
+static void
+object_dealloc(void *ptr)
+{
+    k5_json_object obj = ptr;
+    size_t i;
+
+    for (i = 0; i < obj->len; i++) {
+        free(obj->entries[i].key);
+        k5_json_release(obj->entries[i].value);
+    }
+    free(obj->entries);
+}
+
+static struct json_type_st object_type = {
+    TID_OBJECT, "object", object_dealloc
+};
+
+/* Create an object sized for approximately size entries. */
+k5_json_object
+k5_json_object_create(void)
+{
+    return alloc_value(&object_type, sizeof(struct k5_json_object_st));
+}
+
+/* Get the type ID of the object type. */
+k5_json_tid
+k5_json_object_tid(void)
+{
+    return TID_OBJECT;
+}
+
+/* Internal search function for objects. */
+static struct entry *
+object_search(k5_json_object obj, const char *key)
+{
+    size_t i;
+
+    for (i = 0; i < obj->len; i++) {
+        if (strcmp(key, obj->entries[i].key) == 0)
+            return &obj->entries[i];
+    }
+    return NULL;
+}
+
+/* Return an alias to the value of key in an object.  Do not release this or
+ * use it beyond the lifetime of obj without retaining it first. */
+k5_json_value
+k5_json_object_get(k5_json_object obj, const char *key)
+{
+    struct entry *ent;
+
+    ent = object_search(obj, key);
+    if (ent == NULL)
+        return NULL;
+    return ent->value;
+}
+
+/* Add a mapping from key to val in obj. */
+int
+k5_json_object_set(k5_json_object obj, const char *key, k5_json_value val)
+{
+    struct entry *ent, *ptr;
+    size_t new_alloc;
+
+    ent = object_search(obj, key);
+    if (ent) {
+        k5_json_release(ent->value);
+        ent->value = k5_json_retain(val);
+        return 0;
+    }
+
+    if (obj->len >= obj->allocated) {
+        /* Increase the number of slots by 50% (16 slots minimum). */
+        new_alloc = obj->len + 1 + (obj->len >> 1);
+        if (new_alloc < 16)
+            new_alloc = 16;
+        ptr = realloc(obj->entries, new_alloc * sizeof(*obj->entries));
+        if (ptr == NULL)
+            return ENOMEM;
+        obj->entries = ptr;
+        obj->allocated = new_alloc;
+    }
+    obj->entries[obj->len].key = strdup(key);
+    if (obj->entries[obj->len].key == NULL)
+        return ENOMEM;
+    obj->entries[obj->len].value = k5_json_retain(val);
+    obj->len++;
+    return 0;
+}
+
+/* Invoke func for each element of obj. */
+void
+k5_json_object_iterate(k5_json_object obj, k5_json_object_iterator_fn func,
+                       void *arg)
+{
+    size_t i;
+
+    for (i = 0; i < obj->len; i++)
+        func(arg, obj->entries[i].key, obj->entries[i].value);
+}
+
+/*** String type ***/
+
+static struct json_type_st string_type = { TID_STRING, "string", NULL };
+
+k5_json_string
+k5_json_string_create(const char *string)
+{
+    return k5_json_string_create_len(string, strlen(string));
+}
+
+/* Create a string object. */
+k5_json_string
+k5_json_string_create_len(const void *data, size_t len)
+{
+    char *s;
+
+    s = alloc_value(&string_type, len + 1);
+    if (s == NULL)
+        return NULL;
+    memcpy(s, data, len);
+    s[len] = '\0';
+    return (k5_json_string)s;
+}
+
+/* Get the type ID of the string type. */
+k5_json_tid
+k5_json_string_tid(void)
+{
+    return TID_STRING;
+}
+
+/* Get the C string value of string. */
+const char *
+k5_json_string_utf8(k5_json_string string)
+{
+    return (const char *)string;
+}
+
+/*** Number type ***/
+
+static struct json_type_st number_type = { TID_NUMBER, "number", NULL };
+
+/* Create a number object. */
+k5_json_number
+k5_json_number_create(long long number)
+{
+    k5_json_number n;
+
+    n = alloc_value(&number_type, sizeof(long long));
+    if (n)
+        *((long long *)n) = number;
+    return n;
+}
+
+/* Return the type ID of the number type. */
+k5_json_tid
+k5_json_number_tid(void)
+{
+    return TID_NUMBER;
+}
+
+/* Get the integer value of a number. */
+long long
+k5_json_number_value(k5_json_number number)
+{
+    return *(long long *)number;
+}
+
+/*** JSON encoding ***/
+
+static const char quotemap_json[] = "\"\\/bfnrt";
+static const char quotemap_c[] = "\"\\/\b\f\n\r\t";
+static const char needs_quote[] = "\\\"\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"
+    "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37";
+
+struct encode_ctx {
+    struct k5buf buf;
+    int ret;
+    int first;
+};
+
+static int encode_value(struct encode_ctx *j, k5_json_value val);
+
+static void
+encode_string(struct encode_ctx *j, const char *str)
+{
+    size_t n;
+    const char *p;
+
+    krb5int_buf_add(&j->buf, "\"");
+    while (*str != '\0') {
+        n = strcspn(str, needs_quote);
+        krb5int_buf_add_len(&j->buf, str, n);
+        str += n;
+        if (*str == '\0')
+            break;
+        krb5int_buf_add(&j->buf, "\\");
+        p = strchr(quotemap_c, *str);
+        if (p != NULL)
+            krb5int_buf_add_len(&j->buf, quotemap_json + (p - quotemap_c), 1);
+        else
+            krb5int_buf_add_fmt(&j->buf, "u00%02X", (unsigned int)*str);
+        str++;
+    }
+    krb5int_buf_add(&j->buf, "\"");
+}
+
+static void
+encode_dict_entry(void *ctx, const char *key, k5_json_value value)
+{
+    struct encode_ctx *j = ctx;
+
+    if (j->ret)
+        return;
+    if (j->first)
+        j->first = 0;
+    else
+        krb5int_buf_add(&j->buf, ",");
+    encode_string(j, key);
+    krb5int_buf_add(&j->buf, ":");
+    j->ret = encode_value(j, value);
+    if (j->ret)
+        return;
+}
+
+static int
+encode_value(struct encode_ctx *j, k5_json_value val)
+{
+    k5_json_tid type;
+    int first = 0, ret;
+    size_t i, len;
+
+    if (val == NULL)
+        return EINVAL;
+
+    type = k5_json_get_tid(val);
+    switch (type) {
+    case TID_ARRAY:
+        krb5int_buf_add(&j->buf, "[");
+        len = k5_json_array_length(val);
+        for (i = 0; i < len; i++) {
+            if (i != 0)
+                krb5int_buf_add(&j->buf, ",");
+            ret = encode_value(j, k5_json_array_get(val, i));
+            if (ret)
+                return ret;
+        }
+        krb5int_buf_add(&j->buf, "]");
+        break;
+
+    case TID_OBJECT:
+        krb5int_buf_add(&j->buf, "{");
+        first = j->first;
+        j->first = 1;
+        k5_json_object_iterate(val, encode_dict_entry, j);
+        krb5int_buf_add(&j->buf, "}");
+        j->first = first;
+        break;
+
+    case TID_STRING:
+        encode_string(j, k5_json_string_utf8(val));
+        break;
+
+    case TID_NUMBER:
+        krb5int_buf_add_fmt(&j->buf, "%lld", k5_json_number_value(val));
+        break;
+
+    case TID_NULL:
+        krb5int_buf_add(&j->buf, "null");
+        break;
+
+    case TID_BOOL:
+        krb5int_buf_add(&j->buf, k5_json_bool_val(val) ? "true" : "false");
+        break;
+
+    default:
+        return 1;
+    }
+    return 0;
+}
+
+char *
+k5_json_encode(k5_json_value val)
+{
+    struct encode_ctx j;
+
+    j.ret = 0;
+    j.first = 1;
+    krb5int_buf_init_dynamic(&j.buf);
+    if (encode_value(&j, val)) {
+        krb5int_free_buf(&j.buf);
+        return NULL;
+    }
+    return krb5int_buf_data(&j.buf);
+}
+
+void
+k5_json_free_encoding(char *str)
+{
+    free(str);
+}
+
+/*** JSON decoding ***/
+
+struct decode_ctx {
+    const unsigned char *p;
+    size_t depth;
+};
+
+static k5_json_value
+parse_value(struct decode_ctx *ctx);
+
+/* Consume whitespace.  Return 0 if there is anything left to parse after the
+ * whitespace, -1 if not. */
+static int
+white_spaces(struct decode_ctx *ctx)
+{
+    unsigned char c;
+
+    for (; *ctx->p != '\0'; ctx->p++) {
+        c = *ctx->p;
+        if (c != ' ' && c != '\t' && c != '\r' && c != '\n')
+            return 0;
+    }
+    return -1;
+}
+
+/* Return true if c is a decimal digit. */
+static inline int
+is_digit(unsigned char c)
+{
+    return ('0' <= c && c <= '9');
+}
+
+/* Return true if c is a hexadecimal digit. */
+static inline int
+is_hex_digit(unsigned char c)
+{
+    return is_digit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
+}
+
+/* Return the numeric value of a hex digit; aborts if c is not a hex digit. */
+static inline unsigned int
+hexval(unsigned char c)
+{
+    if (is_digit(c))
+        return c - '0';
+    else if ('a' <= c && c <= 'f')
+        return c - 'a' + 10;
+    else if ('A' <= c && c <= 'F')
+        return c - 'A' + 10;
+    abort();
+}
+
+/* Parse a JSON number (which must be an integer in the signed 64-bit range; we
+ * do not allow floating-point numbers). */
+static k5_json_number
+parse_number(struct decode_ctx *ctx)
+{
+    const unsigned long long umax = ~0ULL, smax = (1ULL << 63) - 1;
+    unsigned long long number = 0;
+    int neg = 1;
+
+    if (*ctx->p == '-') {
+        neg = -1;
+        ctx->p++;
+    }
+
+    if (!is_digit(*ctx->p))
+        return NULL;
+
+    /* Read the number into an unsigned 64-bit container, ensuring that we
+     * don't overflow it. */
+    while (is_digit(*ctx->p)) {
+        if (number + 1 > umax / 10)
+            return NULL;
+        number = (number * 10) + (*ctx->p - '0');
+        ctx->p++;
+    }
+
+    /* Make sure the unsigned 64-bit value fits in the signed 64-bit range. */
+    if (number > smax + 1 || (number == smax && neg == 1))
+        return NULL;
+
+    return k5_json_number_create(number * neg);
+}
+
+/* Parse a JSON string (which must not quote Unicode code points above 256). */
+static char *
+parse_string(struct decode_ctx *ctx)
+{
+    const unsigned char *p, *start, *end = NULL;
+    const char *q;
+    char *buf, *pos;
+    unsigned int code;
+
+    /* Find the start and end of the string. */
+    if (*ctx->p != '"')
+        return NULL;
+    start = ++ctx->p;
+    for (; *ctx->p != '\0'; ctx->p++) {
+        if (*ctx->p == '\\') {
+            ctx->p++;
+            if (*ctx->p == '\0')
+                return NULL;
+        } else if (*ctx->p == '"') {
+            end = ctx->p++;
+            break;
+        }
+    }
+    if (end == NULL)
+        return NULL;
+
+    pos = buf = malloc(end - start + 1);
+    if (buf == NULL)
+        return NULL;
+    for (p = start; p < end;) {
+        if (*p == '\\') {
+            p++;
+            if (*p == 'u' && is_hex_digit(p[1]) && is_hex_digit(p[2]) &&
+                is_hex_digit(p[3]) && is_hex_digit(p[4])) {
+                code = (hexval(p[1]) << 12) | (hexval(p[2]) << 8) |
+                    (hexval(p[3]) << 4) | hexval(p[4]);
+                if (code <= 0xff) {
+                    *pos++ = code;
+                } else {
+                    /* Code points above 0xff don't need to be quoted, so we
+                     * don't implement translating those into UTF-8. */
+                    free(buf);
+                    return NULL;
+                }
+                p += 5;
+            } else {
+                q = strchr(quotemap_json, *p);
+                if (q != NULL) {
+                    *pos++ = quotemap_c[q - quotemap_json];
+                } else {
+                    free(buf);
+                    return NULL;
+                }
+                p++;
+            }
+        } else {
+            *pos++ = *p++;
+        }
+    }
+    *pos = '\0';
+    return buf;
+}
+
+/*
+ * Parse an object association and the following comma.  Return 1 if an
+ * association was parsed, 0 if the end of the object was reached, and -1 on
+ * error.
+ */
+static int
+parse_pair(k5_json_object obj, struct decode_ctx *ctx)
+{
+    char *key = NULL;
+    k5_json_value value;
+
+    if (white_spaces(ctx))
+        goto err;
+
+    /* Check for the end of the object. */
+    if (*ctx->p == '}') {
+        ctx->p++;
+        return 0;
+    }
+
+    /* Parse the key and value. */
+    key = parse_string(ctx);
+    if (key == NULL)
+        goto err;
+    if (white_spaces(ctx))
+        goto err;
+    if (*ctx->p != ':')
+        goto err;
+    ctx->p++;
+    if (white_spaces(ctx))
+        goto err;
+    value = parse_value(ctx);
+    if (value == NULL) {
+        free(key);
+        return -1;
+    }
+
+    /* Add the key and value to the object. */
+    k5_json_object_set(obj, key, value);
+    free(key);
+    key = NULL;
+    k5_json_release(value);
+
+    /* Consume the following comma if this isn't the last item. */
+    if (white_spaces(ctx))
+        goto err;
+    if (*ctx->p == ',')
+        ctx->p++;
+    else if (*ctx->p != '}')
+        goto err;
+
+    return 1;
+
+err:
+    free(key);
+    return -1;
+}
+
+/* Parse a JSON object. */
+static k5_json_object
+parse_object(struct decode_ctx *ctx)
+{
+    k5_json_object obj;
+    int ret;
+
+    obj = k5_json_object_create();
+    if (obj == NULL)
+        return NULL;
+
+    ctx->p++;
+    while ((ret = parse_pair(obj, ctx)) > 0)
+        ;
+    if (ret < 0) {
+        k5_json_release(obj);
+        return NULL;
+    }
+    return obj;
+}
+
+/* Parse a JSON array item and the following comma.  Return 1 if an item was
+ * parsed, 0 if the end of the array was reached, and -1 on error. */
+static int
+parse_item(k5_json_array array, struct decode_ctx *ctx)
+{
+    k5_json_value value;
+
+    if (white_spaces(ctx))
+        return -1;
+
+    if (*ctx->p == ']') {
+        ctx->p++;
+        return 0;
+    }
+
+    value = parse_value(ctx);
+    if (value == NULL)
+        return -1;
+
+    k5_json_array_append(array, value);
+    k5_json_release(value);
+
+    if (white_spaces(ctx))
+        return -1;
+
+    if (*ctx->p == ',')
+        ctx->p++;
+    else if (*ctx->p != ']')
+        return -1;
+    return 1;
+}
+
+/* Parse a JSON array. */
+static k5_json_array
+parse_array(struct decode_ctx *ctx)
+{
+    k5_json_array array = k5_json_array_create();
+    int ret;
+
+    assert(*ctx->p == '[');
+    ctx->p += 1;
+
+    while ((ret = parse_item(array, ctx)) > 0)
+        ;
+    if (ret < 0) {
+        k5_json_release(array);
+        return NULL;
+    }
+    return array;
+}
+
+/* Parse a JSON value of any type. */
+static k5_json_value
+parse_value(struct decode_ctx *ctx)
+{
+    k5_json_value v;
+    char *str;
+
+    if (white_spaces(ctx))
+        return NULL;
+
+    if (*ctx->p == '"') {
+        str = parse_string(ctx);
+        if (str == NULL)
+            return NULL;
+        v = k5_json_string_create(str);
+        free(str);
+        return v;
+    } else if (*ctx->p == '{') {
+        if (ctx->depth-- == 1)
+            return NULL;
+        v = parse_object(ctx);
+        ctx->depth++;
+        return v;
+    } else if (*ctx->p == '[') {
+        if (ctx->depth-- == 1)
+            return NULL;
+        v = parse_array(ctx);
+        ctx->depth++;
+        return v;
+    } else if (is_digit(*ctx->p) || *ctx->p == '-') {
+        return parse_number(ctx);
+    }
+
+    if (strncmp((char *)ctx->p, "null", 4) == 0) {
+        ctx->p += 4;
+        return k5_json_null_create();
+    } else if (strncmp((char *)ctx->p, "true", 4) == 0) {
+        ctx->p += 4;
+        return k5_json_bool_create(1);
+    } else if (strncmp((char *)ctx->p, "false", 5) == 0) {
+        ctx->p += 5;
+        return k5_json_bool_create(0);
+    }
+
+    return NULL;
+}
+
+k5_json_value
+k5_json_decode(const char *string)
+{
+    struct decode_ctx ctx;
+    k5_json_value v;
+
+    ctx.p = (unsigned char *)string;
+    ctx.depth = MAX_DECODE_DEPTH;
+    v = parse_value(&ctx);
+    if (white_spaces(&ctx) == 0) {
+        k5_json_release(v);
+        return NULL;
+    }
+    return v;
+}
diff --git a/src/util/support/t_json.c b/src/util/support/t_json.c
new file mode 100644
index 0000000..a2323a9
--- /dev/null
+++ b/src/util/support/t_json.c
@@ -0,0 +1,288 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/t_json.c - JSON test program */
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <k5-json.h>
+
+static void
+err(const char *str)
+{
+    fprintf(stderr, "%s\n", str);
+    exit(1);
+}
+
+static void
+check(int pred, const char *str)
+{
+    if (!pred)
+        err(str);
+}
+
+static void
+test_array()
+{
+    k5_json_string v1 = k5_json_string_create("abc");
+    k5_json_number v2 = k5_json_number_create(2);
+    k5_json_null v3 = k5_json_null_create();
+    k5_json_array a = k5_json_array_create();
+    k5_json_value v;
+
+    k5_json_array_append(a, v1);
+    k5_json_array_append(a, v2);
+    k5_json_array_append(a, v3);
+
+    check(k5_json_array_length(a) == 3, "array length");
+    v = k5_json_array_get(a, 2);
+    check(k5_json_get_tid(v) == k5_json_null_tid(), "array[2] tid");
+    v = k5_json_array_get(a, 1);
+    check(k5_json_get_tid(v) == k5_json_number_tid(), "array[1] tid");
+    check(k5_json_number_value(v) == 2, "array[1] value");
+    v = k5_json_array_get(a, 0);
+    check(k5_json_get_tid(v) == k5_json_string_tid(), "array[0] tid");
+    check(strcmp(k5_json_string_utf8(v), "abc") == 0, "array[0] value");
+
+    k5_json_release(v1);
+    k5_json_release(v2);
+    k5_json_release(v3);
+    k5_json_release(a);
+}
+
+static void
+test_object(void)
+{
+    k5_json_object object;
+    k5_json_number n, v1 = k5_json_number_create(1);
+    k5_json_string s, v2 = k5_json_string_create("hejsan");
+
+    object = k5_json_object_create();
+    k5_json_object_set(object, "key1", v1);
+    k5_json_object_set(object, "key2", v2);
+
+    n = k5_json_object_get(object, "key1");
+    if (k5_json_number_value(n) != 1)
+        err("Retrieving key1 from object failed");
+
+    s = k5_json_object_get(object, "key2");
+    if (strcmp(k5_json_string_utf8(s), "hejsan") != 0)
+        err("Retrieving key2 from object failed");
+
+    k5_json_release(v1);
+    k5_json_release(v2);
+    k5_json_release(object);
+}
+
+static void
+test_string(void)
+{
+    k5_json_string s1, s2;
+
+    s1 = k5_json_string_create("hejsan");
+    s2 = k5_json_string_create("hejsan");
+
+    if (strcmp(k5_json_string_utf8(s1), k5_json_string_utf8(s2)) != 0)
+        err("Identical strings are not identical");
+
+    k5_json_release(s1);
+    k5_json_release(s2);
+}
+
+static void
+test_json(void)
+{
+    static char *tests[] = {
+        "{\"k1\":\"s1\",\"k2\":\"s2\"}",
+        "{\"k1\":[\"s1\",\"s2\",\"s3\"],\"k2\":\"s3\"}",
+        "{\"k1\":{\"k2\":\"s1\",\"k3\":\"s2\",\"k4\":\"s3\"},\"k5\":\"s4\"}",
+        "[\"v1\",\"v2\",[\"v3\",\"v4\",[\"v 5\",\" v 7 \"]],-123456789,"
+        "null,true,false,123456789,\"\"]",
+        "-1",
+        "\"\\\\abc\\\"\\nde\\b\\r/\\ff\\tghi\\u0001\\u001F\"",
+        "9223372036854775807",
+        "-9223372036854775808",
+        NULL
+    };
+    char **tptr, *s, *enc, *p, orig;
+    int i;
+    k5_json_value v, v2;
+
+    v = k5_json_decode("\"string\"");
+    check(v != NULL, "string1");
+    check(k5_json_get_tid(v) == k5_json_string_tid(), "string1 tid");
+    check(strcmp(k5_json_string_utf8(v), "string") == 0, "string1 utf8");
+    k5_json_release(v);
+
+    v = k5_json_decode("\t \"foo\\\"bar\" ");
+    check(v != NULL, "string2");
+    check(k5_json_get_tid(v) == k5_json_string_tid(), "string2 tid");
+    check(strcmp(k5_json_string_utf8(v), "foo\"bar") == 0, "string2 utf8");
+    k5_json_release(v);
+
+    v = k5_json_decode(" { \"key\" : \"value\" }");
+    check(v != NULL, "object1");
+    check(k5_json_get_tid(v) == k5_json_object_tid(), "object1 tid");
+    v2 = k5_json_object_get(v, "key");
+    check(v2 != NULL, "object[key]");
+    check(k5_json_get_tid(v2) == k5_json_string_tid(), "object1[key] tid");
+    check(strcmp(k5_json_string_utf8(v2), "value") == 0, "object1[key] utf8");
+    k5_json_release(v);
+
+    v = k5_json_decode("{ \"k1\" : { \"k2\" : \"s2\", \"k3\" : \"s3\" }, "
+                       "\"k4\" : \"s4\" }");
+    check(v != NULL, "object2");
+    v2 = k5_json_object_get(v, "k1");
+    check(v2 != NULL, "object2[k1]");
+    check(k5_json_get_tid(v2) == k5_json_object_tid(), "object2[k1] tid");
+    v2 = k5_json_object_get(v2, "k3");
+    check(v2 != NULL, "object2[k1][k3]");
+    check(k5_json_get_tid(v2) == k5_json_string_tid(), "object2[k1][k3] tid");
+    check(strcmp(k5_json_string_utf8(v2), "s3") == 0, "object2[k1][k3] utf8");
+    k5_json_release(v);
+
+    v = k5_json_decode("{ \"k1\" : 1 }");
+    check(v != NULL, "object3");
+    check(k5_json_get_tid(v) == k5_json_object_tid(), "object3 id");
+    v2 = k5_json_object_get(v, "k1");
+    check(k5_json_get_tid(v2) == k5_json_number_tid(), "object3[k1] tid");
+    check(k5_json_number_value(v2) == 1, "object3[k1] value");
+    k5_json_release(v);
+
+    v = k5_json_decode("-10");
+    check(v != NULL, "number1");
+    check(k5_json_get_tid(v) == k5_json_number_tid(), "number1 tid");
+    check(k5_json_number_value(v) == -10, "number1 value");
+    k5_json_release(v);
+
+    v = k5_json_decode("99");
+    check(v != NULL, "number2");
+    check(k5_json_get_tid(v) == k5_json_number_tid(), "number2 tid");
+    check(k5_json_number_value(v) == 99, "number2 value");
+    k5_json_release(v);
+
+    v = k5_json_decode(" [ 1 ]");
+    check(v != NULL, "array1");
+    check(k5_json_get_tid(v) == k5_json_array_tid(), "array1 tid");
+    check(k5_json_array_length(v) == 1, "array1 len");
+    v2 = k5_json_array_get(v, 0);
+    check(v2 != NULL, "array1[0]");
+    check(k5_json_get_tid(v2) == k5_json_number_tid(), "array1[0] tid");
+    check(k5_json_number_value(v2) == 1, "array1[0] value");
+    k5_json_release(v);
+
+    v = k5_json_decode(" [ -1 ]");
+    check(v != NULL, "array2");
+    check(k5_json_get_tid(v) == k5_json_array_tid(), "array2 tid");
+    check(k5_json_array_length(v) == 1, "array2 len");
+    v2 = k5_json_array_get(v, 0);
+    check(v2 != NULL, "array2[0]");
+    check(k5_json_get_tid(v2) == k5_json_number_tid(), "array2[0] tid");
+    check(k5_json_number_value(v2) == -1, "array2[0] value");
+    k5_json_release(v);
+
+    v = k5_json_decode("18446744073709551616");
+    check(v == NULL, "unsigned 64-bit overflow");
+    v = k5_json_decode("9223372036854775808");
+    check(v == NULL, "signed 64-bit positive overflow");
+    v = k5_json_decode("-9223372036854775809");
+    check(v == NULL, "signed 64-bit negative overflow");
+
+    for (tptr = tests; *tptr != NULL; tptr++) {
+        s = strdup(*tptr);
+        v = k5_json_decode(s);
+        if (v == NULL)
+            err(s);
+        enc = k5_json_encode(v);
+        if (enc == NULL || strcmp(enc, s) != 0)
+            err(s);
+        k5_json_free_encoding(enc);
+        k5_json_release(v);
+
+        /* Fuzz bytes.  Parsing may succeed or fail; we're just looking for
+         * memory access bugs. */
+        for (p = s; *p != '\0'; p++) {
+            orig = *p;
+            for (i = 0; i <= 255; i++) {
+                *p = i;
+                k5_json_release(k5_json_decode(s));
+            }
+            *p = orig;
+        }
+        free(s);
+    }
+}
+
+int
+main(int argc, char **argv)
+{
+    test_array();
+    test_object();
+    test_string();
+    test_json();
+    return 0;
+}


More information about the cvs-krb5 mailing list