summaryrefslogtreecommitdiff
path: root/src/util/support
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/support')
-rw-r--r--src/util/support/Makefile.in241
-rw-r--r--src/util/support/base64.c145
-rw-r--r--src/util/support/bcmp.c44
-rw-r--r--src/util/support/cache-addrinfo.h131
-rw-r--r--src/util/support/deps92
-rw-r--r--src/util/support/errors.c123
-rw-r--r--src/util/support/fake-addrinfo.c1361
-rw-r--r--src/util/support/fnmatch.c207
-rw-r--r--src/util/support/getopt.c149
-rw-r--r--src/util/support/getopt_long.c232
-rw-r--r--src/util/support/gettimeofday.c102
-rw-r--r--src/util/support/gmt_mktime.c168
-rw-r--r--src/util/support/init-addrinfo.c68
-rw-r--r--src/util/support/ipc_stream.c468
-rw-r--r--src/util/support/json.c1092
-rw-r--r--src/util/support/k5buf.c243
-rw-r--r--src/util/support/libkrb5support-fixed.exports88
-rw-r--r--src/util/support/mkstemp.c138
-rw-r--r--src/util/support/path.c161
-rw-r--r--src/util/support/plugins.c787
-rw-r--r--src/util/support/printf.c100
-rw-r--r--src/util/support/strerror_r.c96
-rw-r--r--src/util/support/strlcpy.c88
-rw-r--r--src/util/support/supp-int.h38
-rw-r--r--src/util/support/t_base64.c110
-rw-r--r--src/util/support/t_json.c329
-rw-r--r--src/util/support/t_k5buf.c260
-rw-r--r--src/util/support/t_path.c191
-rw-r--r--src/util/support/t_unal.c46
-rw-r--r--src/util/support/t_utf8.c209
-rw-r--r--src/util/support/threads.c614
-rw-r--r--src/util/support/utf8.c533
-rw-r--r--src/util/support/utf8_conv.c457
-rw-r--r--src/util/support/zap.c41
34 files changed, 9152 insertions, 0 deletions
diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in
new file mode 100644
index 000000000000..6239e41761ee
--- /dev/null
+++ b/src/util/support/Makefile.in
@@ -0,0 +1,241 @@
+mydir=util$(S)support
+BUILDTOP=$(REL)..$(S)..
+RELDIR=../util/support
+
+##DOS##BUILDTOP = ..\..
+##DOS##LIBNAME=$(OUTPRE)k5sprt32.lib
+##DOS##WIN64LIBNAME=$(OUTPRE)k5sprt64.lib
+##DOS##XTRA=
+##DOS##OBJFILE=$(OUTPRE)k5sprt32.lst
+##DOS##WIN64OBJFILE=$(OUTPRE)k5sprt64.lst
+
+SED = sed
+
+LIBBASE=krb5support
+LIBMAJOR=@SUPPORTLIB_MAJOR@
+LIBMINOR=1
+
+LIBINITFUNC=krb5int_thread_support_init
+LIBFINIFUNC=krb5int_thread_support_fini
+
+GETTIMEOFDAY_ST_OBJ= @GETTIMEOFDAY_ST_OBJ@
+GETTIMEOFDAY_OBJ= @GETTIMEOFDAY_OBJ@
+##DOS##GETTIMEOFDAY_ST_OBJ= gettimeofday.o
+##DOS##GETTIMEOFDAY_OBJ= $(OUTPRE)gettimeofday.$(OBJEXT)
+
+MKSTEMP_ST_OBJ= @MKSTEMP_ST_OBJ@
+MKSTEMP_OBJ= @MKSTEMP_OBJ@
+##DOS##MKSTEMP_ST_OBJ= mkstemp.o
+##DOS##MKSTEMP_OBJ= $(OUTPRE)mkstemp.$(OBJEXT)
+
+STRLCPY_ST_OBJ=@STRLCPY_ST_OBJ@
+STRLCPY_OBJ=@STRLCPY_OBJ@
+##DOS##STRLCPY_ST_OBJ= strlcpy.o
+##DOS##STRLCPY_OBJ= $(OUTPRE)strlcpy.$(OBJEXT)
+
+FNMATCH_ST_OBJ= @FNMATCH_ST_OBJ@
+FNMATCH_OBJ= @FNMATCH_OBJ@
+##DOS##FNMATCH_ST_OBJ= fnmatch.o
+##DOS##FNMATCH_OBJ= $(OUTPRE)fnmatch.$(OBJEXT)
+
+PRINTF_ST_OBJ= @PRINTF_ST_OBJ@
+PRINTF_OBJ= @PRINTF_OBJ@
+##DOS##PRINTF_ST_OBJ= printf.o
+##DOS##PRINTF_OBJ= $(OUTPRE)printf.$(OBJEXT)
+
+GETOPT_ST_OBJ= @GETOPT_ST_OBJ@
+GETOPT_OBJ= @GETOPT_OBJ@
+##DOS##GETOPT_ST_OBJ= getopt.o
+##DOS##GETOPT_OBJ= $(OUTPRE)getopt.$(OBJEXT)
+
+GETOPT_LONG_ST_OBJ= @GETOPT_LONG_ST_OBJ@
+GETOPT_LONG_OBJ= @GETOPT_LONG_OBJ@
+##DOS##GETOPT_LONG_ST_OBJ= getopt_long.o
+##DOS##GETOPT_LONG_OBJ= $(OUTPRE)getopt_long.$(OBJEXT)
+
+IPC_ST_OBJ=
+IPC_OBJ=
+##DOS##IPC_ST_OBJ= ipc_stream.o
+##DOS##IPC_OBJ= $(OUTPRE)ipc_stream.$(OBJEXT)
+IPC_SYMS= \
+ krb5int_ipc_stream_data krb5int_ipc_stream_new \
+ krb5int_ipc_stream_write krb5int_ipc_stream_read \
+ krb5int_ipc_stream_read_int32 krb5int_ipc_stream_write_int32 \
+ krb5int_ipc_stream_read_int64 krb5int_ipc_stream_write_int64 \
+ krb5int_ipc_stream_read_uint32 krb5int_ipc_stream_write_uint32 \
+ krb5int_ipc_stream_read_string krb5int_ipc_stream_write_string \
+ krb5int_ipc_stream_release krb5int_ipc_stream_size \
+ krb5int_ipc_stream_free_string
+
+STLIBOBJS= \
+ threads.o \
+ init-addrinfo.o \
+ plugins.o \
+ errors.o \
+ k5buf.o \
+ gmt_mktime.o \
+ fake-addrinfo.o \
+ utf8.o \
+ utf8_conv.o \
+ zap.o \
+ path.o \
+ base64.o \
+ json.o \
+ bcmp.o \
+ strerror_r.o \
+ $(GETTIMEOFDAY_ST_OBJ) \
+ $(IPC_ST_OBJ) \
+ $(STRLCPY_ST_OBJ) \
+ $(FNMATCH_ST_OBJ) \
+ $(PRINTF_ST_OBJ) \
+ $(MKSTEMP_ST_OBJ) \
+ $(GETOPT_ST_OBJ) \
+ $(GETOPT_LONG_ST_OBJ)
+
+LIBOBJS= \
+ $(OUTPRE)threads.$(OBJEXT) \
+ $(OUTPRE)init-addrinfo.$(OBJEXT) \
+ $(OUTPRE)plugins.$(OBJEXT) \
+ $(OUTPRE)errors.$(OBJEXT) \
+ $(OUTPRE)k5buf.$(OBJEXT) \
+ $(OUTPRE)gmt_mktime.$(OBJEXT) \
+ $(OUTPRE)fake-addrinfo.$(OBJEXT) \
+ $(OUTPRE)utf8.$(OBJEXT) \
+ $(OUTPRE)utf8_conv.$(OBJEXT) \
+ $(OUTPRE)zap.$(OBJEXT) \
+ $(OUTPRE)path.$(OBJEXT) \
+ $(OUTPRE)base64.$(OBJEXT) \
+ $(OUTPRE)json.$(OBJEXT) \
+ $(OUTPRE)bcmp.$(OBJEXT) \
+ $(OUTPRE)strerror_r.$(OBJEXT) \
+ $(GETTIMEOFDAY_OBJ) \
+ $(IPC_OBJ) \
+ $(STRLCPY_OBJ) \
+ $(FNMATCH_OBJ) \
+ $(PRINTF_OBJ) \
+ $(MKSTEMP_OBJ) \
+ $(GETOPT_OBJ) \
+ $(GETOPT_LONG_OBJ)
+
+SRCS=\
+ $(srcdir)/threads.c \
+ $(srcdir)/init-addrinfo.c \
+ $(srcdir)/plugins.c \
+ $(srcdir)/errors.c \
+ $(srcdir)/k5buf.c \
+ $(srcdir)/gmt_mktime.c \
+ $(srcdir)/fake-addrinfo.c \
+ $(srcdir)/utf8.c \
+ $(srcdir)/utf8_conv.c \
+ $(srcdir)/gettimeofday.c \
+ $(srcdir)/strlcpy.c \
+ $(srcdir)/fnmatch.c \
+ $(srcdir)/printf.c \
+ $(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)/base64.c \
+ $(srcdir)/json.c \
+ $(srcdir)/bcmp.c \
+ $(srcdir)/strerror_r.c \
+ $(srcdir)/t_utf8.c \
+ $(srcdir)/getopt.c \
+ $(srcdir)/getopt_long.c
+
+SHLIB_EXPDEPS =
+# Add -lm if dumping thread stats, for sqrt.
+SHLIB_EXPLIBS= $(LIBS) $(DL_LIB)
+
+DEPLIBS=
+
+#
+all-unix: all-liblinks
+
+install-unix: install-libs
+
+clean-unix:: clean-liblinks clean-libs clean-libobjs
+
+##DOS##!if 0
+$(BUILDTOP)/include/autoconf.h: $(top_srcdir)/include/autoconf.h.in
+ (cd $(BUILDTOP)/include; $(MAKE) autoconf.h)
+##DOS##!endif
+
+t_mktime: gmt_mktime.c
+ $(CC) $(ALL_CFLAGS) -DTEST_LEAP -o t_mktime $(srcdir)/gmt_mktime.c
+
+SHLIB_EXPORT_FILE=libkrb5support.exports
+##DOS##all-windows: libkrb5support.exports
+
+EXTRA_SUPPORT_SYMS= @EXTRA_SUPPORT_SYMS@
+##DOS##EXTRA_SUPPORT_SYMS= krb5int_mkstemp krb5int_strlcpy krb5int_strlcat \
+##DOS## k5_optind k5_optarg k5_opterr k5_optopt k5_getopt k5_getopt_long \
+##DOS## krb5int_vasprintf krb5int_asprintf krb5int_gettimeofday $(IPC_SYMS)
+
+##DOS##!if 0
+libkrb5support.exports: $(srcdir)/libkrb5support-fixed.exports Makefile
+ cat $(srcdir)/libkrb5support-fixed.exports > new-exports
+ for i in $(EXTRA_SUPPORT_SYMS) .; do \
+ if test "$$i" != .; then echo $$i >> new-exports; else :; fi ; \
+ done
+ $(MV) new-exports libkrb5support.exports
+##DOS##!endif
+##DOS##libkrb5support.exports: libkrb5support-fixed.exports Makefile
+##DOS## $(CP) libkrb5support-fixed.exports new-exports
+##DOS## for %%x in ($(EXTRA_SUPPORT_SYMS) .) do if not %%x==. echo %%x >> new-exports
+##DOS## $(RM) libkrb5support.exports
+##DOS## $(MV) new-exports libkrb5support.exports
+
+T_K5BUF_OBJS= t_k5buf.o k5buf.o $(PRINTF_ST_OBJ)
+
+t_k5buf: $(T_K5BUF_OBJS)
+ $(CC_LINK) -o t_k5buf $(T_K5BUF_OBJS)
+
+t_path: t_path.o path.o $(PRINTF_ST_OBJ)
+ $(CC_LINK) -o $@ t_path.o path.o $(PRINTF_ST_OBJ)
+
+t_path_win: t_path_win.o path_win.o $(PRINTF_ST_OBJ)
+ $(CC_LINK) -o $@ t_path_win.o path_win.o $(PRINTF_ST_OBJ)
+
+t_path_win.o: $(srcdir)/t_path.c
+ $(CC) $(ALL_CFLAGS) -DWINDOWS_PATHS -c $(srcdir)/t_path.c -o $@
+
+path_win.o: $(srcdir)/path.c
+ $(CC) $(ALL_CFLAGS) -DWINDOWS_PATHS -c $(srcdir)/path.c -o $@
+
+t_base64: t_base64.o base64.o
+ $(CC_LINK) -o $@ t_base64.o base64.o
+
+T_JSON_OBJS= t_json.o json.o base64.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
+
+t_utf8: t_utf8.o utf8.o
+ $(CC_LINK) -o t_utf8 t_utf8.o utf8.o
+
+TEST_PROGS= t_k5buf t_path t_path_win t_base64 t_json t_unal t_utf8
+
+check-unix: $(TEST_PROGS)
+ ./t_k5buf
+ ./t_path
+ ./t_path_win
+ ./t_base64
+ ./t_json
+ ./t_unal
+ ./t_utf8
+
+clean:
+ $(RM) t_k5buf.o t_k5buf t_unal.o t_unal path_win.o path_win
+ $(RM) t_path_win.o t_path_win t_path.o t_path t_base64.o t_base64
+ $(RM) t_json.o t_json libkrb5support.exports t_utf8.o t_utf8
+
+@lib_frag@
+@libobj_frag@
+
diff --git a/src/util/support/base64.c b/src/util/support/base64.c
new file mode 100644
index 000000000000..e964a389e60d
--- /dev/null
+++ b/src/util/support/base64.c
@@ -0,0 +1,145 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/base64.c - base64 encoder and decoder */
+/*
+ * Copyright (c) 1995-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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.
+ */
+
+#include <k5-platform.h>
+#include <k5-base64.h>
+
+static const char base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+char *
+k5_base64_encode(const void *data, size_t len)
+{
+ char *s, *p;
+ size_t i;
+ unsigned int c;
+ const unsigned char *q;
+
+ if (len > SIZE_MAX / 4)
+ return NULL;
+
+ p = s = malloc(len * 4 / 3 + 4);
+ if (p == NULL)
+ return NULL;
+ q = (const unsigned char *)data;
+
+ for (i = 0; i < len;) {
+ c = q[i++];
+ c *= 256;
+ if (i < len)
+ c += q[i];
+ i++;
+ c *= 256;
+ if (i < len)
+ c += q[i];
+ i++;
+ p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+ p[1] = base64_chars[(c & 0x0003f000) >> 12];
+ p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+ p[3] = base64_chars[(c & 0x0000003f) >> 0];
+ if (i > len)
+ p[3] = '=';
+ if (i > len + 1)
+ p[2] = '=';
+ p += 4;
+ }
+ *p = '\0';
+ return s;
+}
+
+#define DECODE_ERROR 0xffffffff
+
+/* Decode token, which must be four bytes long. */
+static unsigned int
+decode_token(const char *token)
+{
+ int i, marker = 0;
+ unsigned int val = 0;
+ const char *p;
+
+ for (i = 0; i < 4; i++) {
+ val *= 64;
+ if (token[i] == '=') {
+ marker++;
+ } else if (marker > 0) {
+ return DECODE_ERROR;
+ } else {
+ p = strchr(base64_chars, token[i]);
+ if (p == NULL)
+ return DECODE_ERROR;
+ val += p - base64_chars;
+ }
+ }
+ if (marker > 2)
+ return DECODE_ERROR;
+ return (marker << 24) | val;
+}
+
+void *
+k5_base64_decode(const char *str, size_t *len_out)
+{
+ unsigned char *data, *q;
+ unsigned int val, marker;
+ size_t len;
+
+ *len_out = SIZE_MAX;
+
+ /* Allocate the output buffer. */
+ len = strlen(str);
+ if (len % 4)
+ return NULL;
+ q = data = malloc(len / 4 * 3);
+ if (data == NULL) {
+ *len_out = 0;
+ return NULL;
+ }
+
+ /* Decode the string. */
+ for (; *str != '\0'; str += 4) {
+ val = decode_token(str);
+ if (val == DECODE_ERROR) {
+ free(data);
+ return NULL;
+ }
+ marker = (val >> 24) & 0xff;
+ *q++ = (val >> 16) & 0xff;
+ if (marker < 2)
+ *q++ = (val >> 8) & 0xff;
+ if (marker < 1)
+ *q++ = val & 0xff;
+ }
+ *len_out = q - data;
+ return data;
+}
diff --git a/src/util/support/bcmp.c b/src/util/support/bcmp.c
new file mode 100644
index 000000000000..71728a6872e8
--- /dev/null
+++ b/src/util/support/bcmp.c
@@ -0,0 +1,44 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/bitcmp.c - Constant-time byte comparison function */
+/*
+ * Copyright (C) 2013 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 "k5-platform.h"
+
+int
+k5_bcmp(const void *p1, const void *p2, size_t n)
+{
+ const unsigned char *c1 = p1, *c2 = p2;
+ unsigned char x = 0;
+
+ for (; n > 0; c1++, c2++, n--)
+ x |= *c1 ^ *c2;
+ return x;
+}
diff --git a/src/util/support/cache-addrinfo.h b/src/util/support/cache-addrinfo.h
new file mode 100644
index 000000000000..a1b7fb28becb
--- /dev/null
+++ b/src/util/support/cache-addrinfo.h
@@ -0,0 +1,131 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004 by the Massachusetts Institute of Technology,
+ * Cambridge, MA, USA. All Rights Reserved.
+ *
+ * This software is being provided to you, the LICENSEE, by the
+ * Massachusetts Institute of Technology (M.I.T.) under the following
+ * license. By obtaining, using and/or copying this software, you agree
+ * that you have read, understood, and will comply with these terms and
+ * conditions:
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify and distribute
+ * this software and its documentation for any purpose and without fee or
+ * royalty is hereby granted, provided that you agree to comply with the
+ * following copyright notice and statements, including the disclaimer, and
+ * that the same appear on ALL copies of the software and documentation,
+ * including modifications that you make for internal use or for
+ * distribution:
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", AND M.I.T. MAKES NO REPRESENTATIONS
+ * OR WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not
+ * limitation, M.I.T. MAKES NO REPRESENTATIONS OR WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF
+ * THE LICENSED SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY
+ * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+ *
+ * The name of the Massachusetts Institute of Technology or M.I.T. may NOT
+ * be used in advertising or publicity pertaining to distribution of the
+ * software. Title to copyright in this software and any associated
+ * documentation shall at all times remain with M.I.T., and USER agrees to
+ * preserve same.
+ *
+ * Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ */
+
+/*
+ * Approach overview:
+ *
+ * If a system version is available but buggy, save handles to it,
+ * redefine the names to refer to static functions defined here, and
+ * in those functions, call the system versions and fix up the
+ * returned data. Use the native data structures and flag values.
+ *
+ * If no system version exists, use gethostby* and fake it. Define
+ * the data structures and flag values locally.
+ *
+ *
+ * On Mac OS X, getaddrinfo results aren't cached (though
+ * gethostbyname results are), so we need to build a cache here. Now
+ * things are getting really messy. Because the cache is in use, we
+ * use getservbyname, and throw away thread safety. (Not that the
+ * cache is thread safe, but when we get locking support, that'll be
+ * dealt with.) This code needs tearing down and rebuilding, soon.
+ *
+ *
+ * Note that recent Windows developers' code has an interesting hack:
+ * When you include the right header files, with the right set of
+ * macros indicating system versions, you'll get an inline function
+ * that looks for getaddrinfo (or whatever) in the system library, and
+ * calls it if it's there. If it's not there, it fakes it with
+ * gethostby* calls.
+ *
+ * We're taking a simpler approach: A system provides these routines or
+ * it does not.
+ *
+ * Someday, we may want to take into account different versions (say,
+ * different revs of GNU libc) where some are broken in one way, and
+ * some work or are broken in another way. Cross that bridge when we
+ * come to it.
+ */
+
+/* To do, maybe:
+ *
+ * + For AIX 4.3.3, using the RFC 2133 definition: Implement
+ * AI_NUMERICHOST. It's not defined in the header file.
+ *
+ * For certain (old?) versions of GNU libc, AI_NUMERICHOST is
+ * defined but not implemented.
+ *
+ * + Use gethostbyname2, inet_aton and other IPv6 or thread-safe
+ * functions if available. But, see
+ * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=135182 for one
+ * gethostbyname2 problem on Linux. And besides, if a platform is
+ * supporting IPv6 at all, they really should be doing getaddrinfo
+ * by now.
+ *
+ * + inet_ntop, inet_pton
+ *
+ * + Conditionally export/import the function definitions, so a
+ * library can have a single copy instead of multiple.
+ *
+ * + Upgrade host requirements to include working implementations of
+ * these functions, and throw all this away. Pleeease? :-)
+ */
+
+#include "k5-platform.h"
+#include "k5-thread.h"
+#include "port-sockets.h"
+#include "socket-utils.h"
+#include "fake-addrinfo.h"
+
+#if defined (__APPLE__) && defined (__MACH__) && 0
+#define FAI_CACHE
+#endif
+
+struct face {
+ struct in_addr *addrs4;
+ struct in6_addr *addrs6;
+ unsigned int naddrs4, naddrs6;
+ time_t expiration;
+ char *canonname, *name;
+ struct face *next;
+};
+
+/* fake addrinfo cache */
+struct fac {
+ k5_mutex_t lock;
+ struct face *data;
+};
+
+extern struct fac krb5int_fac;
+
+extern int krb5int_init_fac (void);
+extern void krb5int_fini_fac (void);
diff --git a/src/util/support/deps b/src/util/support/deps
new file mode 100644
index 000000000000..4dff014f463b
--- /dev/null
+++ b/src/util/support/deps
@@ -0,0 +1,92 @@
+#
+# Generated makefile dependencies follow.
+#
+threads.so threads.po $(OUTPRE)threads.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/fake-addrinfo.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h cache-addrinfo.h \
+ supp-int.h threads.c
+init-addrinfo.so init-addrinfo.po $(OUTPRE)init-addrinfo.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(top_srcdir)/include/fake-addrinfo.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ cache-addrinfo.h init-addrinfo.c
+plugins.so plugins.po $(OUTPRE)plugins.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ plugins.c
+errors.so errors.po $(OUTPRE)errors.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h errors.c supp-int.h
+k5buf.so k5buf.po $(OUTPRE)k5buf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h k5buf.c
+gmt_mktime.so gmt_mktime.po $(OUTPRE)gmt_mktime.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ gmt_mktime.c
+fake-addrinfo.so fake-addrinfo.po $(OUTPRE)fake-addrinfo.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(top_srcdir)/include/fake-addrinfo.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ cache-addrinfo.h fake-addrinfo.c supp-int.h
+utf8.so utf8.po $(OUTPRE)utf8.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-utf8.h supp-int.h utf8.c
+utf8_conv.so utf8_conv.po $(OUTPRE)utf8_conv.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-utf8.h \
+ supp-int.h utf8_conv.c
+gettimeofday.so gettimeofday.po $(OUTPRE)gettimeofday.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h gettimeofday.c
+strlcpy.so strlcpy.po $(OUTPRE)strlcpy.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ strlcpy.c
+fnmatch.so fnmatch.po $(OUTPRE)fnmatch.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ fnmatch.c
+printf.so printf.po $(OUTPRE)printf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ printf.c
+mkstemp.so mkstemp.po $(OUTPRE)mkstemp.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ mkstemp.c
+t_k5buf.so t_k5buf.po $(OUTPRE)t_k5buf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h t_k5buf.c
+t_unal.so t_unal.po $(OUTPRE)t_unal.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ t_unal.c
+t_path.so t_path.po $(OUTPRE)t_path.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ t_path.c
+t_json.so t_json.po $(OUTPRE)t_json.$(OBJEXT): $(top_srcdir)/include/k5-json.h \
+ t_json.c
+zap.so zap.po $(OUTPRE)zap.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ zap.c
+path.so path.po $(OUTPRE)path.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ path.c
+base64.so base64.po $(OUTPRE)base64.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-base64.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h base64.c
+json.so json.po $(OUTPRE)json.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-base64.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h json.c
+bcmp.so bcmp.po $(OUTPRE)bcmp.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ bcmp.c
+strerror_r.so strerror_r.po $(OUTPRE)strerror_r.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h strerror_r.c
+t_utf8.so t_utf8.po $(OUTPRE)t_utf8.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-utf8.h t_utf8.c
+getopt.so getopt.po $(OUTPRE)getopt.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ getopt.c
+getopt_long.so getopt_long.po $(OUTPRE)getopt_long.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h getopt_long.c
diff --git a/src/util/support/errors.c b/src/util/support/errors.c
new file mode 100644
index 000000000000..70e1d59d095b
--- /dev/null
+++ b/src/util/support/errors.c
@@ -0,0 +1,123 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Can't include krb5.h here, or k5-int.h which includes it, because krb5.h
+ * needs to be generated with error tables, after util/et, which builds after
+ * this directory.
+ */
+#include "k5-platform.h"
+#include "k5-err.h"
+#include "k5-thread.h"
+#include "supp-int.h"
+
+/*
+ * It would be nice to just use error_message() always. Pity that it's defined
+ * in a library that depends on this one, and we're not allowed to make
+ * circular dependencies.
+ */
+/*
+ * We really want a rwlock here, since we should hold it while calling the
+ * function and copying out its results. But I haven't implemented shims for
+ * rwlock yet.
+ */
+static k5_mutex_t krb5int_error_info_support_mutex =
+ K5_MUTEX_PARTIAL_INITIALIZER;
+static const char *(KRB5_CALLCONV *fptr)(long); /* = &error_message */
+
+/* Fallback error message if we cannot allocate a copy of the real one. */
+static char *oom_msg = "Out of memory";
+
+int
+krb5int_err_init (void)
+{
+ return k5_mutex_finish_init(&krb5int_error_info_support_mutex);
+}
+#define initialize() krb5int_call_thread_support_init()
+#define lock() k5_mutex_lock(&krb5int_error_info_support_mutex)
+#define unlock() k5_mutex_unlock(&krb5int_error_info_support_mutex)
+
+void
+k5_set_error(struct errinfo *ep, long code, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ k5_vset_error(ep, code, fmt, args);
+ va_end(args);
+}
+
+void
+k5_vset_error(struct errinfo *ep, long code, const char *fmt, va_list args)
+{
+ char *str;
+
+ k5_clear_error(ep);
+ ep->code = code;
+
+ if (vasprintf(&str, fmt, args) < 0)
+ return;
+ ep->msg = str;
+}
+
+static inline const char *
+oom_check(const char *str)
+{
+ return (str == NULL) ? oom_msg : str;
+}
+
+const char *
+k5_get_error(struct errinfo *ep, long code)
+{
+ const char *r;
+ char buf[128];
+
+ if (code == ep->code && ep->msg != NULL)
+ return oom_check(strdup(ep->msg));
+
+ if (initialize())
+ return oom_check(strdup(_("Kerberos library initialization failure")));
+
+ lock();
+ if (fptr == NULL) {
+ unlock();
+ if (strerror_r(code, buf, sizeof(buf)) == 0)
+ return oom_check(strdup(buf));
+ return oom_check(strdup(strerror(code)));
+ }
+ r = fptr(code);
+#ifndef HAVE_COM_ERR_INTL
+ /* Translate com_err results here if libcom_err won't do it. */
+ r = _(r);
+#endif
+ if (r == NULL) {
+ unlock();
+ snprintf(buf, sizeof(buf), _("error %ld"), code);
+ return oom_check(strdup(buf));
+ }
+
+ r = strdup(r);
+ unlock();
+ return oom_check(r);
+}
+
+void
+k5_free_error(struct errinfo *ep, const char *msg)
+{
+ if (msg != oom_msg)
+ free((char *)msg);
+}
+
+void
+k5_clear_error(struct errinfo *ep)
+{
+ k5_free_error(ep, ep->msg);
+ ep->msg = NULL;
+}
+
+void
+k5_set_error_info_callout_fn(const char *(KRB5_CALLCONV *f)(long))
+{
+ initialize();
+ lock();
+ fptr = f;
+ unlock();
+}
diff --git a/src/util/support/fake-addrinfo.c b/src/util/support/fake-addrinfo.c
new file mode 100644
index 000000000000..df1cc1dec558
--- /dev/null
+++ b/src/util/support/fake-addrinfo.c
@@ -0,0 +1,1361 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2001,2002,2003,2004,2005,2006 by the Massachusetts Institute of Technology,
+ * Cambridge, MA, USA. All Rights Reserved.
+ *
+ * This software is being provided to you, the LICENSEE, by the
+ * Massachusetts Institute of Technology (M.I.T.) under the following
+ * license. By obtaining, using and/or copying this software, you agree
+ * that you have read, understood, and will comply with these terms and
+ * conditions:
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify and distribute
+ * this software and its documentation for any purpose and without fee or
+ * royalty is hereby granted, provided that you agree to comply with the
+ * following copyright notice and statements, including the disclaimer, and
+ * that the same appear on ALL copies of the software and documentation,
+ * including modifications that you make for internal use or for
+ * distribution:
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", AND M.I.T. MAKES NO REPRESENTATIONS
+ * OR WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not
+ * limitation, M.I.T. MAKES NO REPRESENTATIONS OR WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF
+ * THE LICENSED SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY
+ * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+ *
+ * The name of the Massachusetts Institute of Technology or M.I.T. may NOT
+ * be used in advertising or publicity pertaining to distribution of the
+ * software. Title to copyright in this software and any associated
+ * documentation shall at all times remain with M.I.T., and USER agrees to
+ * preserve same.
+ *
+ * Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ */
+
+/*
+ * Approach overview:
+ *
+ * If a system version is available but buggy, save handles to it,
+ * redefine the names to refer to static functions defined here, and
+ * in those functions, call the system versions and fix up the
+ * returned data. Use the native data structures and flag values.
+ *
+ * If no system version exists, use gethostby* and fake it. Define
+ * the data structures and flag values locally.
+ *
+ *
+ * On Mac OS X, getaddrinfo results aren't cached (though
+ * gethostbyname results are), so we need to build a cache here. Now
+ * things are getting really messy. Because the cache is in use, we
+ * use getservbyname, and throw away thread safety. (Not that the
+ * cache is thread safe, but when we get locking support, that'll be
+ * dealt with.) This code needs tearing down and rebuilding, soon.
+ *
+ *
+ * Note that recent Windows developers' code has an interesting hack:
+ * When you include the right header files, with the right set of
+ * macros indicating system versions, you'll get an inline function
+ * that looks for getaddrinfo (or whatever) in the system library, and
+ * calls it if it's there. If it's not there, it fakes it with
+ * gethostby* calls.
+ *
+ * We're taking a simpler approach: A system provides these routines or
+ * it does not.
+ *
+ * Someday, we may want to take into account different versions (say,
+ * different revs of GNU libc) where some are broken in one way, and
+ * some work or are broken in another way. Cross that bridge when we
+ * come to it.
+ */
+
+/*
+ * To do, maybe:
+ *
+ * + For AIX 4.3.3, using the RFC 2133 definition: Implement
+ * AI_NUMERICHOST. It's not defined in the header file.
+ *
+ * For certain (old?) versions of GNU libc, AI_NUMERICHOST is
+ * defined but not implemented.
+ *
+ * + Use gethostbyname2, inet_aton and other IPv6 or thread-safe
+ * functions if available. But, see
+ * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=135182 for one
+ * gethostbyname2 problem on Linux. And besides, if a platform is
+ * supporting IPv6 at all, they really should be doing getaddrinfo
+ * by now.
+ *
+ * + inet_ntop, inet_pton
+ *
+ * + Conditionally export/import the function definitions, so a
+ * library can have a single copy instead of multiple.
+ *
+ * + Upgrade host requirements to include working implementations of
+ * these functions, and throw all this away. Pleeease? :-)
+ */
+
+#include "k5-platform.h"
+#include "k5-thread.h"
+#include "port-sockets.h"
+#include "socket-utils.h"
+#include "supp-int.h"
+
+#define IMPLEMENT_FAKE_GETADDRINFO
+#include "fake-addrinfo.h"
+
+#ifdef S_SPLINT_S
+/*@-incondefs@*/
+extern int
+getaddrinfo (/*@in@*/ /*@null@*/ const char *,
+ /*@in@*/ /*@null@*/ const char *,
+ /*@in@*/ /*@null@*/ const struct addrinfo *,
+ /*@out@*/ struct addrinfo **)
+ ;
+extern void
+freeaddrinfo (/*@only@*/ /*@out@*/ struct addrinfo *)
+ ;
+extern int
+getnameinfo (const struct sockaddr *addr, socklen_t addrsz,
+ /*@out@*/ /*@null@*/ char *h, socklen_t hsz,
+ /*@out@*/ /*@null@*/ char *s, socklen_t ssz,
+ int flags)
+/*@requires (maxSet(h)+1) >= hsz /\ (maxSet(s)+1) >= ssz @*/
+/* too hard: maxRead(addr) >= (addrsz-1) */
+ /*@modifies *h, *s@*/;
+extern /*@dependent@*/ char *gai_strerror (int code) /*@*/;
+/*@=incondefs@*/
+#endif
+
+
+#include "cache-addrinfo.h"
+
+#if (defined (__linux__) && defined(HAVE_GETADDRINFO)) || defined (_AIX)
+/* See comments below. */
+# define WRAP_GETADDRINFO
+#endif
+
+#if defined (__linux__) && defined(HAVE_GETADDRINFO)
+/* Define COPY_FIRST_CANONNAME for glibc 2.3 and prior. */
+#include <features.h>
+# ifdef __GLIBC_PREREQ
+# if ! __GLIBC_PREREQ(2, 4)
+# define COPY_FIRST_CANONNAME
+# endif
+# else
+# define COPY_FIRST_CANONNAME
+# endif
+#endif
+
+#ifdef _AIX
+# define NUMERIC_SERVICE_BROKEN
+# define COPY_FIRST_CANONNAME
+#endif
+
+
+#ifdef COPY_FIRST_CANONNAME
+# include <string.h>
+#endif
+
+#ifdef NUMERIC_SERVICE_BROKEN
+# include <ctype.h> /* isdigit */
+# include <stdlib.h> /* strtoul */
+#endif
+
+
+/* Do we actually have *any* systems we care about that don't provide
+ either getaddrinfo or one of these two flavors of
+ gethostbyname_r? */
+#if !defined(HAVE_GETHOSTBYNAME_R) || defined(THREADSAFE_GETHOSTBYNAME)
+typedef struct hostent *GET_HOST_TMP;
+#define GET_HOST_BY_NAME(NAME, HP, ERR, TMP) \
+ { TMP = gethostbyname (NAME); (ERR) = h_errno; (HP) = TMP; }
+#define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR, TMP) \
+ { TMP = gethostbyaddr ((ADDR), (ADDRLEN), (FAMILY)); (ERR) = h_errno; (HP) = TMP; }
+#else
+#ifdef _AIX /* XXX should have a feature test! */
+typedef struct {
+ struct hostent ent;
+ struct hostent_data data;
+} GET_HOST_TMP;
+#define GET_HOST_BY_NAME(NAME, HP, ERR, TMP) \
+ { \
+ (HP) = (gethostbyname_r((NAME), &TMP.ent, &TMP.data) \
+ ? 0 \
+ : &TMP.ent); \
+ (ERR) = h_errno; \
+ }
+/*
+ #define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR) \
+ { \
+ struct hostent my_h_ent; \
+ struct hostent_data my_h_ent_data; \
+ (HP) = (gethostbyaddr_r((ADDR), (ADDRLEN), (FAMILY), &my_h_ent, \
+ &my_h_ent_data) \
+ ? 0 \
+ : &my_h_ent); \
+ (ERR) = my_h_err; \
+ }
+*/
+#else
+#ifdef GETHOSTBYNAME_R_RETURNS_INT
+typedef struct {
+ struct hostent ent;
+ char buf[8192];
+} GET_HOST_TMP;
+#define GET_HOST_BY_NAME(NAME, HP, ERR, TMP) \
+ { \
+ struct hostent *my_hp = NULL; \
+ int my_h_err, my_ret; \
+ my_ret = gethostbyname_r((NAME), &TMP.ent, \
+ TMP.buf, sizeof (TMP.buf), &my_hp, \
+ &my_h_err); \
+ (HP) = (((my_ret != 0) || (my_hp != &TMP.ent)) \
+ ? 0 \
+ : &TMP.ent); \
+ (ERR) = my_h_err; \
+ }
+#define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR, TMP) \
+ { \
+ struct hostent *my_hp; \
+ int my_h_err, my_ret; \
+ my_ret = gethostbyaddr_r((ADDR), (ADDRLEN), (FAMILY), &TMP.ent, \
+ TMP.buf, sizeof (TMP.buf), &my_hp, \
+ &my_h_err); \
+ (HP) = (((my_ret != 0) || (my_hp != &TMP.ent)) \
+ ? 0 \
+ : &TMP.ent); \
+ (ERR) = my_h_err; \
+ }
+#else
+typedef struct {
+ struct hostent ent;
+ char buf[8192];
+} GET_HOST_TMP;
+#define GET_HOST_BY_NAME(NAME, HP, ERR, TMP) \
+ { \
+ int my_h_err; \
+ (HP) = gethostbyname_r((NAME), &TMP.ent, \
+ TMP.buf, sizeof (TMP.buf), &my_h_err); \
+ (ERR) = my_h_err; \
+ }
+#define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR, TMP) \
+ { \
+ int my_h_err; \
+ (HP) = gethostbyaddr_r((ADDR), (ADDRLEN), (FAMILY), &TMP.ent, \
+ TMP.buf, sizeof (TMP.buf), &my_h_err); \
+ (ERR) = my_h_err; \
+ }
+#endif /* returns int? */
+#endif /* _AIX */
+#endif
+
+/* Now do the same for getservby* functions. */
+#ifndef HAVE_GETSERVBYNAME_R
+typedef struct servent *GET_SERV_TMP;
+#define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR, TMP) \
+ (TMP = getservbyname (NAME, PROTO), (SP) = TMP, (ERR) = (SP) ? 0 : -1)
+#define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR, TMP) \
+ (TMP = getservbyport (PORT, PROTO), (SP) = TMP, (ERR) = (SP) ? 0 : -1)
+#else
+#ifdef GETSERVBYNAME_R_RETURNS_INT
+typedef struct {
+ struct servent ent;
+ char buf[8192];
+} GET_SERV_TMP;
+#define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR, TMP) \
+ { \
+ struct servent *my_sp; \
+ int my_s_err; \
+ (SP) = (getservbyname_r((NAME), (PROTO), &TMP.ent, \
+ TMP.buf, sizeof (TMP.buf), &my_sp, \
+ &my_s_err) \
+ ? 0 \
+ : &TMP.ent); \
+ (ERR) = my_s_err; \
+ }
+#define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR, TMP) \
+ { \
+ struct servent *my_sp; \
+ int my_s_err; \
+ (SP) = (getservbyport_r((PORT), (PROTO), &TMP.ent, \
+ TMP.buf, sizeof (TMP.buf), &my_sp, \
+ &my_s_err) \
+ ? 0 \
+ : &TMP.ent); \
+ (ERR) = my_s_err; \
+ }
+#else
+/* returns ptr -- IRIX? */
+typedef struct {
+ struct servent ent;
+ char buf[8192];
+} GET_SERV_TMP;
+#define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR, TMP) \
+ { \
+ (SP) = getservbyname_r((NAME), (PROTO), &TMP.ent, \
+ TMP.buf, sizeof (TMP.buf)); \
+ (ERR) = (SP) == NULL; \
+ }
+
+#define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR, TMP) \
+ { \
+ struct servent *my_sp; \
+ my_sp = getservbyport_r((PORT), (PROTO), &TMP.ent, \
+ TMP.buf, sizeof (TMP.buf)); \
+ (SP) = my_sp; \
+ (ERR) = my_sp == 0; \
+ (ERR) = (ERR); /* avoid "unused" warning */ \
+ }
+#endif
+#endif
+
+#if defined(WRAP_GETADDRINFO) || defined(FAI_CACHE)
+static inline int
+system_getaddrinfo (const char *name, const char *serv,
+ const struct addrinfo *hint,
+ struct addrinfo **res)
+{
+ return getaddrinfo(name, serv, hint, res);
+}
+
+static inline void
+system_freeaddrinfo (struct addrinfo *ai)
+{
+ freeaddrinfo(ai);
+}
+
+/* Note: Implementations written to RFC 2133 use size_t, while RFC
+ 2553 implementations use socklen_t, for the second parameter.
+
+ Mac OS X (10.2) and AIX 4.3.3 appear to be in the RFC 2133 camp,
+ but we don't have an autoconf test for that right now. */
+static inline int
+system_getnameinfo (const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen, char *serv, size_t servlen,
+ int flags)
+{
+ return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
+}
+#endif
+
+#if !defined (HAVE_GETADDRINFO) || defined(WRAP_GETADDRINFO) || defined(FAI_CACHE)
+
+#undef getaddrinfo
+#define getaddrinfo my_fake_getaddrinfo
+#undef freeaddrinfo
+#define freeaddrinfo my_fake_freeaddrinfo
+
+#endif
+
+#if !defined (HAVE_GETADDRINFO)
+
+#undef gai_strerror
+#define gai_strerror my_fake_gai_strerror
+
+#endif /* ! HAVE_GETADDRINFO */
+
+#if (!defined (HAVE_GETADDRINFO) || defined (WRAP_GETADDRINFO)) && defined(DEBUG_ADDRINFO)
+/* Some debug routines. */
+
+static const char *protoname (int p, char *buf, size_t bufsize) {
+#define X(N) if (p == IPPROTO_ ## N) return #N
+
+ X(TCP);
+ X(UDP);
+ X(ICMP);
+ X(IPV6);
+#ifdef IPPROTO_GRE
+ X(GRE);
+#endif
+ X(NONE);
+ X(RAW);
+#ifdef IPPROTO_COMP
+ X(COMP);
+#endif
+#ifdef IPPROTO_IGMP
+ X(IGMP);
+#endif
+
+ snprintf(buf, bufsize, " %-2d", p);
+ return buf;
+}
+
+static const char *socktypename (int t, char *buf, size_t bufsize) {
+ switch (t) {
+ case SOCK_DGRAM: return "DGRAM";
+ case SOCK_STREAM: return "STREAM";
+ case SOCK_RAW: return "RAW";
+ case SOCK_RDM: return "RDM";
+ case SOCK_SEQPACKET: return "SEQPACKET";
+ }
+ snprintf(buf, bufsize, " %-2d", t);
+ return buf;
+}
+
+static const char *familyname (int f, char *buf, size_t bufsize) {
+ switch (f) {
+ default:
+ snprintf(buf, bufsize, "AF %d", f);
+ return buf;
+ case AF_INET: return "AF_INET";
+ case AF_INET6: return "AF_INET6";
+#ifdef AF_UNIX
+ case AF_UNIX: return "AF_UNIX";
+#endif
+ }
+}
+
+static void debug_dump_getaddrinfo_args (const char *name, const char *serv,
+ const struct addrinfo *hint)
+{
+ const char *sep;
+ fprintf(stderr,
+ "getaddrinfo(hostname %s, service %s,\n"
+ " hints { ",
+ name ? name : "(null)", serv ? serv : "(null)");
+ if (hint) {
+ char buf[30];
+ sep = "";
+#define Z(FLAG) if (hint->ai_flags & AI_##FLAG) fprintf(stderr, "%s%s", sep, #FLAG), sep = "|"
+ Z(CANONNAME);
+ Z(PASSIVE);
+#ifdef AI_NUMERICHOST
+ Z(NUMERICHOST);
+#endif
+ if (sep[0] == 0)
+ fprintf(stderr, "no-flags");
+ if (hint->ai_family)
+ fprintf(stderr, " %s", familyname(hint->ai_family, buf,
+ sizeof(buf)));
+ if (hint->ai_socktype)
+ fprintf(stderr, " SOCK_%s", socktypename(hint->ai_socktype, buf,
+ sizeof(buf)));
+ if (hint->ai_protocol)
+ fprintf(stderr, " IPPROTO_%s", protoname(hint->ai_protocol, buf,
+ sizeof(buf)));
+ } else
+ fprintf(stderr, "(null)");
+ fprintf(stderr, " }):\n");
+}
+
+static void debug_dump_error (int err)
+{
+ fprintf(stderr, "error %d: %s\n", err, gai_strerror(err));
+}
+
+static void debug_dump_addrinfos (const struct addrinfo *ai)
+{
+ int count = 0;
+ char buf[10];
+ fprintf(stderr, "addrinfos returned:\n");
+ while (ai) {
+ fprintf(stderr, "%p...", ai);
+ fprintf(stderr, " socktype=%s", socktypename(ai->ai_socktype, buf,
+ sizeof(buf)));
+ fprintf(stderr, " ai_family=%s", familyname(ai->ai_family, buf,
+ sizeof(buf)));
+ if (ai->ai_family != ai->ai_addr->sa_family)
+ fprintf(stderr, " sa_family=%s",
+ familyname(ai->ai_addr->sa_family, buf, sizeof(buf)));
+ fprintf(stderr, "\n");
+ ai = ai->ai_next;
+ count++;
+ }
+ fprintf(stderr, "end addrinfos returned (%d)\n");
+}
+
+#endif
+
+#if !defined (HAVE_GETADDRINFO) || defined (WRAP_GETADDRINFO)
+
+static
+int getaddrinfo (const char *name, const char *serv,
+ const struct addrinfo *hint, struct addrinfo **result);
+
+static
+void freeaddrinfo (struct addrinfo *ai);
+
+#endif
+
+#if !defined (HAVE_GETADDRINFO)
+
+#define HAVE_FAKE_GETADDRINFO /* was not originally HAVE_GETADDRINFO */
+#define HAVE_GETADDRINFO
+#define NEED_FAKE_GETNAMEINFO
+#undef HAVE_GETNAMEINFO
+#define HAVE_GETNAMEINFO 1
+
+#undef getnameinfo
+#define getnameinfo my_fake_getnameinfo
+
+static
+char *gai_strerror (int code);
+
+#endif
+
+#if !defined (HAVE_GETADDRINFO)
+static
+int getnameinfo (const struct sockaddr *addr, socklen_t len,
+ char *host, socklen_t hostlen,
+ char *service, socklen_t servicelen,
+ int flags);
+#endif
+
+/* Fudge things on older gai implementations. */
+/* AIX 4.3.3 is based on RFC 2133; no AI_NUMERICHOST. */
+#ifndef AI_NUMERICHOST
+# define AI_NUMERICHOST 0
+#endif
+/* Partial RFC 2553 implementations may not have AI_ADDRCONFIG and
+ friends, which RFC 3493 says are now part of the getaddrinfo
+ interface, and we'll want to use. */
+#ifndef AI_ADDRCONFIG
+# define AI_ADDRCONFIG 0
+#endif
+#ifndef AI_V4MAPPED
+# define AI_V4MAPPED 0
+#endif
+#ifndef AI_ALL
+# define AI_ALL 0
+#endif
+#ifndef AI_DEFAULT
+# define AI_DEFAULT (AI_ADDRCONFIG|AI_V4MAPPED)
+#endif
+
+#if defined(HAVE_FAKE_GETADDRINFO) || defined(FAI_CACHE)
+#define NEED_FAKE_GETADDRINFO
+#endif
+
+#if defined(NEED_FAKE_GETADDRINFO) || defined(WRAP_GETADDRINFO)
+#include <stdlib.h>
+#endif
+
+#ifdef NEED_FAKE_GETADDRINFO
+#include <string.h> /* for strspn */
+
+static inline int translate_h_errno (int h);
+
+static inline int fai_add_entry (struct addrinfo **result, void *addr,
+ int port, const struct addrinfo *template)
+{
+ struct addrinfo *n = malloc (sizeof (struct addrinfo));
+ if (n == 0)
+ return EAI_MEMORY;
+ if (template->ai_family != AF_INET
+ && template->ai_family != AF_INET6
+ )
+ return EAI_FAMILY;
+ *n = *template;
+ if (template->ai_family == AF_INET) {
+ struct sockaddr_in *sin4;
+ sin4 = malloc (sizeof (struct sockaddr_in));
+ if (sin4 == 0)
+ return EAI_MEMORY;
+ memset (sin4, 0, sizeof (struct sockaddr_in)); /* for sin_zero */
+ n->ai_addr = (struct sockaddr *) sin4;
+ sin4->sin_family = AF_INET;
+ sin4->sin_addr = *(struct in_addr *)addr;
+ sin4->sin_port = port;
+ }
+ if (template->ai_family == AF_INET6) {
+ struct sockaddr_in6 *sin6;
+ sin6 = malloc (sizeof (struct sockaddr_in6));
+ if (sin6 == 0)
+ return EAI_MEMORY;
+ memset (sin6, 0, sizeof (struct sockaddr_in6)); /* for sin_zero */
+ n->ai_addr = (struct sockaddr *) sin6;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = *(struct in6_addr *)addr;
+ sin6->sin6_port = port;
+ }
+ n->ai_next = *result;
+ *result = n;
+ return 0;
+}
+
+#ifdef FAI_CACHE
+/* fake addrinfo cache entries */
+#define CACHE_ENTRY_LIFETIME 15 /* seconds */
+
+static void plant_face (const char *name, struct face *entry)
+{
+ entry->name = strdup(name);
+ if (entry->name == NULL)
+ /* @@ Wastes memory. */
+ return;
+ k5_mutex_assert_locked(&krb5int_fac.lock);
+ entry->next = krb5int_fac.data;
+ entry->expiration = time(0) + CACHE_ENTRY_LIFETIME;
+ krb5int_fac.data = entry;
+#ifdef DEBUG_ADDRINFO
+ printf("added cache entry '%s' at %p: %d ipv4, %d ipv6; expire %d\n",
+ name, entry, entry->naddrs4, entry->naddrs6, entry->expiration);
+#endif
+}
+
+static int find_face (const char *name, struct face **entry)
+{
+ struct face *fp, **fpp;
+ time_t now = time(0);
+
+ /* First, scan for expired entries and free them.
+ (Future improvement: Integrate these two loops.) */
+#ifdef DEBUG_ADDRINFO
+ printf("scanning cache at %d for '%s'...\n", now, name);
+#endif
+ k5_mutex_assert_locked(&krb5int_fac.lock);
+ for (fpp = &krb5int_fac.data; *fpp; ) {
+ fp = *fpp;
+#ifdef DEBUG_ADDRINFO
+ printf(" checking expiration time of @%p: %d\n",
+ fp, fp->expiration);
+#endif
+ if (fp->expiration < now) {
+#ifdef DEBUG_ADDRINFO
+ printf("\texpiring cache entry\n");
+#endif
+ free(fp->name);
+ free(fp->canonname);
+ free(fp->addrs4);
+ free(fp->addrs6);
+ *fpp = fp->next;
+ free(fp);
+ /* Stay at this point in the list, and check again. */
+ } else
+ /* Move forward. */
+ fpp = &(*fpp)->next;
+ }
+
+ for (fp = krb5int_fac.data; fp; fp = fp->next) {
+#ifdef DEBUG_ADDRINFO
+ printf(" comparing entry @%p\n", fp);
+#endif
+ if (!strcasecmp(fp->name, name)) {
+#ifdef DEBUG_ADDRINFO
+ printf("\tMATCH!\n");
+#endif
+ *entry = fp;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#endif
+
+#ifdef FAI_CACHE
+static int krb5int_lock_fac(void), krb5int_unlock_fac(void);
+#endif
+
+static inline int fai_add_hosts_by_name (const char *name,
+ struct addrinfo *template,
+ int portnum, int flags,
+ struct addrinfo **result)
+{
+#ifdef FAI_CACHE
+
+ struct face *ce;
+ int i, r, err;
+
+ err = krb5int_lock_fac();
+ if (err) {
+ errno = err;
+ return EAI_SYSTEM;
+ }
+ if (!find_face(name, &ce)) {
+ struct addrinfo myhints = { 0 }, *ai, *ai2;
+ int i4, i6, aierr;
+
+#ifdef DEBUG_ADDRINFO
+ printf("looking up new data for '%s'...\n", name);
+#endif
+ myhints.ai_socktype = SOCK_STREAM;
+ myhints.ai_flags = AI_CANONNAME;
+ /* Don't set ai_family -- we want to cache all address types,
+ because the next lookup may not use the same constraints as
+ the current one. We *could* cache them separately, so that
+ we never have to look up an IPv6 address if we are always
+ asked for IPv4 only, but let's deal with that later, if we
+ have to. */
+ /* Try NULL for the service for now.
+
+ It would be nice to use the requested service name, and not
+ have to patch things up, but then we'd be doing multiple
+ queries for the same host when we get different services.
+ We were using "telnet" for a little more confidence that
+ getaddrinfo would heed the hints to only give us stream
+ socket types (with no socket type and null service name, we
+ might get stream *and* dgram *and* raw, for each address,
+ or only raw). The RFC 3493 description of ai_socktype
+ sometimes associates it with the specified service,
+ sometimes not.
+
+ But on Mac OS X (10.3, 10.4) they've "extended" getaddrinfo
+ to make SRV RR queries. (Please, somebody, show me
+ something in the specs that actually supports this? RFC
+ 3493 says nothing about it, but it does say getaddrinfo is
+ the new way to look up hostnames. RFC 2782 says SRV
+ records should *not* be used unless the application
+ protocol spec says to do so. The Telnet spec does not say
+ to do it.) And then they complain when our code
+ "unexpectedly" seems to use this "extension" in cases where
+ they don't want it to be used.
+
+ Fortunately, it appears that if we specify ai_socktype as
+ SOCK_STREAM and use a null service name, we only get one
+ copy of each address on all the platforms I've tried,
+ although it may not have ai_socktype filled in properly.
+ So, we'll fudge it with that for now. */
+ aierr = system_getaddrinfo(name, NULL, &myhints, &ai);
+ if (aierr) {
+ krb5int_unlock_fac();
+ return aierr;
+ }
+ ce = malloc(sizeof(struct face));
+ memset(ce, 0, sizeof(*ce));
+ ce->expiration = time(0) + 30;
+ for (ai2 = ai; ai2; ai2 = ai2->ai_next) {
+#ifdef DEBUG_ADDRINFO
+ printf(" found an address in family %d...\n", ai2->ai_family);
+#endif
+ switch (ai2->ai_family) {
+ case AF_INET:
+ ce->naddrs4++;
+ break;
+ case AF_INET6:
+ ce->naddrs6++;
+ break;
+ default:
+ break;
+ }
+ }
+ ce->addrs4 = calloc(ce->naddrs4, sizeof(*ce->addrs4));
+ if (ce->addrs4 == NULL && ce->naddrs4 != 0) {
+ krb5int_unlock_fac();
+ system_freeaddrinfo(ai);
+ return EAI_MEMORY;
+ }
+ ce->addrs6 = calloc(ce->naddrs6, sizeof(*ce->addrs6));
+ if (ce->addrs6 == NULL && ce->naddrs6 != 0) {
+ krb5int_unlock_fac();
+ free(ce->addrs4);
+ system_freeaddrinfo(ai);
+ return EAI_MEMORY;
+ }
+ for (ai2 = ai, i4 = i6 = 0; ai2; ai2 = ai2->ai_next) {
+ switch (ai2->ai_family) {
+ case AF_INET:
+ ce->addrs4[i4++] = ((struct sockaddr_in *)ai2->ai_addr)->sin_addr;
+ break;
+ case AF_INET6:
+ ce->addrs6[i6++] = ((struct sockaddr_in6 *)ai2->ai_addr)->sin6_addr;
+ break;
+ default:
+ break;
+ }
+ }
+ ce->canonname = ai->ai_canonname ? strdup(ai->ai_canonname) : 0;
+ system_freeaddrinfo(ai);
+ plant_face(name, ce);
+ }
+ template->ai_family = AF_INET6;
+ template->ai_addrlen = sizeof(struct sockaddr_in6);
+ for (i = 0; i < ce->naddrs6; i++) {
+ r = fai_add_entry (result, &ce->addrs6[i], portnum, template);
+ if (r) {
+ krb5int_unlock_fac();
+ return r;
+ }
+ }
+ template->ai_family = AF_INET;
+ template->ai_addrlen = sizeof(struct sockaddr_in);
+ for (i = 0; i < ce->naddrs4; i++) {
+ r = fai_add_entry (result, &ce->addrs4[i], portnum, template);
+ if (r) {
+ krb5int_unlock_fac();
+ return r;
+ }
+ }
+ if (*result && (flags & AI_CANONNAME))
+ (*result)->ai_canonname = (ce->canonname
+ ? strdup(ce->canonname)
+ : NULL);
+ krb5int_unlock_fac();
+ return 0;
+
+#else
+
+ struct hostent *hp;
+ int i, r;
+ int herr;
+ GET_HOST_TMP htmp;
+
+ GET_HOST_BY_NAME (name, hp, herr, htmp);
+ if (hp == 0)
+ return translate_h_errno (herr);
+ for (i = 0; hp->h_addr_list[i]; i++) {
+ r = fai_add_entry (result, hp->h_addr_list[i], portnum, template);
+ if (r)
+ return r;
+ }
+ if (*result && (flags & AI_CANONNAME))
+ (*result)->ai_canonname = strdup (hp->h_name);
+ return 0;
+
+#endif
+}
+
+static inline void
+fake_freeaddrinfo (struct addrinfo *ai)
+{
+ struct addrinfo *next;
+ while (ai) {
+ next = ai->ai_next;
+ if (ai->ai_canonname)
+ free (ai->ai_canonname);
+ if (ai->ai_addr)
+ free (ai->ai_addr);
+ free (ai);
+ ai = next;
+ }
+}
+
+static inline int
+fake_getaddrinfo (const char *name, const char *serv,
+ const struct addrinfo *hint, struct addrinfo **result)
+{
+ struct addrinfo *res = 0;
+ int ret;
+ int port = 0, socktype;
+ int flags;
+ struct addrinfo template;
+
+#ifdef DEBUG_ADDRINFO
+ debug_dump_getaddrinfo_args(name, serv, hint);
+#endif
+
+ if (hint != 0) {
+ if (hint->ai_family != 0 && hint->ai_family != AF_INET)
+ return EAI_NODATA;
+ socktype = hint->ai_socktype;
+ flags = hint->ai_flags;
+ } else {
+ socktype = 0;
+ flags = 0;
+ }
+
+ if (serv) {
+ size_t numlen = strspn (serv, "0123456789");
+ if (serv[numlen] == '\0') {
+ /* pure numeric */
+ unsigned long p = strtoul (serv, 0, 10);
+ if (p == 0 || p > 65535)
+ return EAI_NONAME;
+ port = htons (p);
+ } else {
+ struct servent *sp;
+ int try_dgram_too = 0, s_err;
+ GET_SERV_TMP stmp;
+
+ if (socktype == 0) {
+ try_dgram_too = 1;
+ socktype = SOCK_STREAM;
+ }
+ try_service_lookup:
+ GET_SERV_BY_NAME(serv, socktype == SOCK_STREAM ? "tcp" : "udp",
+ sp, s_err, stmp);
+ if (sp == 0) {
+ if (try_dgram_too) {
+ socktype = SOCK_DGRAM;
+ goto try_service_lookup;
+ }
+ return EAI_SERVICE;
+ }
+ port = sp->s_port;
+ }
+ }
+
+ if (name == 0) {
+ name = (flags & AI_PASSIVE) ? "0.0.0.0" : "127.0.0.1";
+ flags |= AI_NUMERICHOST;
+ }
+
+ template.ai_family = AF_INET;
+ template.ai_addrlen = sizeof (struct sockaddr_in);
+ template.ai_socktype = socktype;
+ template.ai_protocol = 0;
+ template.ai_flags = 0;
+ template.ai_canonname = 0;
+ template.ai_next = 0;
+ template.ai_addr = 0;
+
+ /* If NUMERICHOST is set, parse a numeric address.
+ If it's not set, don't accept such names. */
+ if (flags & AI_NUMERICHOST) {
+ struct in_addr addr4;
+#if 0
+ ret = inet_aton (name, &addr4);
+ if (ret)
+ return EAI_NONAME;
+#else
+ addr4.s_addr = inet_addr (name);
+ if (addr4.s_addr == 0xffffffff || addr4.s_addr == -1)
+ /* 255.255.255.255 or parse error, both bad */
+ return EAI_NONAME;
+#endif
+ ret = fai_add_entry (&res, &addr4, port, &template);
+ } else {
+ ret = fai_add_hosts_by_name (name, &template, port, flags,
+ &res);
+ }
+
+ if (ret && ret != NO_ADDRESS) {
+ fake_freeaddrinfo (res);
+ return ret;
+ }
+ if (res == 0)
+ return NO_ADDRESS;
+ *result = res;
+ return 0;
+}
+
+#ifdef NEED_FAKE_GETNAMEINFO
+static inline int
+fake_getnameinfo (const struct sockaddr *sa, socklen_t len,
+ char *host, socklen_t hostlen,
+ char *service, socklen_t servicelen,
+ int flags)
+{
+ struct hostent *hp;
+ const struct sockaddr_in *sinp;
+ struct servent *sp;
+ size_t hlen, slen;
+
+ if (sa->sa_family != AF_INET) {
+ return EAI_FAMILY;
+ }
+ sinp = (const struct sockaddr_in *) sa;
+
+ hlen = hostlen;
+ if (hostlen < 0 || hlen != hostlen) {
+ errno = EINVAL;
+ return EAI_SYSTEM;
+ }
+ slen = servicelen;
+ if (servicelen < 0 || slen != servicelen) {
+ errno = EINVAL;
+ return EAI_SYSTEM;
+ }
+
+ if (host) {
+ if (flags & NI_NUMERICHOST) {
+#if (defined(__GNUC__) && defined(__mips__)) || 1 /* thread safety always */
+ /* The inet_ntoa call, passing a struct, fails on IRIX 6.5
+ using gcc 2.95; we get back "0.0.0.0". Since this in a
+ configuration still important at Athena, here's the
+ workaround, which also happens to be thread-safe.... */
+ const unsigned char *uc;
+ char tmpbuf[20];
+ numeric_host:
+ uc = (const unsigned char *) &sinp->sin_addr;
+ snprintf(tmpbuf, sizeof(tmpbuf), "%d.%d.%d.%d",
+ uc[0], uc[1], uc[2], uc[3]);
+ strncpy(host, tmpbuf, hlen);
+#else
+ char *p;
+ numeric_host:
+ p = inet_ntoa (sinp->sin_addr);
+ strncpy (host, p, hlen);
+#endif
+ } else {
+ int herr;
+ GET_HOST_TMP htmp;
+
+ GET_HOST_BY_ADDR((const char *) &sinp->sin_addr,
+ sizeof (struct in_addr),
+ sa->sa_family, hp, herr, htmp);
+ if (hp == 0) {
+ if (herr == NO_ADDRESS && !(flags & NI_NAMEREQD)) /* ??? */
+ goto numeric_host;
+ return translate_h_errno (herr);
+ }
+ /* According to the Open Group spec, getnameinfo can
+ silently truncate, but must still return a
+ null-terminated string. */
+ strncpy (host, hp->h_name, hlen);
+ }
+ host[hostlen-1] = 0;
+ }
+
+ if (service) {
+ if (flags & NI_NUMERICSERV) {
+ char numbuf[10];
+ int port;
+ numeric_service:
+ port = ntohs (sinp->sin_port);
+ if (port < 0 || port > 65535)
+ return EAI_FAIL;
+ snprintf (numbuf, sizeof(numbuf), "%d", port);
+ strncpy (service, numbuf, slen);
+ } else {
+ int serr;
+ GET_SERV_TMP stmp;
+
+ GET_SERV_BY_PORT(sinp->sin_port,
+ (flags & NI_DGRAM) ? "udp" : "tcp",
+ sp, serr, stmp);
+ if (sp == 0)
+ goto numeric_service;
+ strncpy (service, sp->s_name, slen);
+ }
+ service[servicelen-1] = 0;
+ }
+
+ return 0;
+}
+#endif
+
+#if defined(HAVE_FAKE_GETADDRINFO) || defined(NEED_FAKE_GETNAMEINFO)
+
+static inline
+char *gai_strerror (int code)
+{
+ switch (code) {
+ case EAI_ADDRFAMILY: return "address family for nodename not supported";
+ case EAI_AGAIN: return "temporary failure in name resolution";
+ case EAI_BADFLAGS: return "bad flags to getaddrinfo/getnameinfo";
+ case EAI_FAIL: return "non-recoverable failure in name resolution";
+ case EAI_FAMILY: return "ai_family not supported";
+ case EAI_MEMORY: return "out of memory";
+ case EAI_NODATA: return "no address associated with hostname";
+ case EAI_NONAME: return "name does not exist";
+ case EAI_SERVICE: return "service name not supported for specified socket type";
+ case EAI_SOCKTYPE: return "ai_socktype not supported";
+ case EAI_SYSTEM: return strerror (errno);
+ default: return "bogus getaddrinfo error?";
+ }
+}
+#endif
+
+static inline int translate_h_errno (int h)
+{
+ switch (h) {
+ case 0:
+ return 0;
+#ifdef NETDB_INTERNAL
+ case NETDB_INTERNAL:
+ if (errno == ENOMEM)
+ return EAI_MEMORY;
+ return EAI_SYSTEM;
+#endif
+ case HOST_NOT_FOUND:
+ return EAI_NONAME;
+ case TRY_AGAIN:
+ return EAI_AGAIN;
+ case NO_RECOVERY:
+ return EAI_FAIL;
+ case NO_DATA:
+#if NO_DATA != NO_ADDRESS
+ case NO_ADDRESS:
+#endif
+ return EAI_NODATA;
+ default:
+ return EAI_SYSTEM;
+ }
+}
+
+#if defined(HAVE_FAKE_GETADDRINFO) || defined(FAI_CACHE)
+static inline
+int getaddrinfo (const char *name, const char *serv,
+ const struct addrinfo *hint, struct addrinfo **result)
+{
+ return fake_getaddrinfo(name, serv, hint, result);
+}
+
+static inline
+void freeaddrinfo (struct addrinfo *ai)
+{
+ fake_freeaddrinfo(ai);
+}
+
+#ifdef NEED_FAKE_GETNAMEINFO
+static inline
+int getnameinfo (const struct sockaddr *sa, socklen_t len,
+ char *host, socklen_t hostlen,
+ char *service, socklen_t servicelen,
+ int flags)
+{
+ return fake_getnameinfo(sa, len, host, hostlen, service, servicelen,
+ flags);
+}
+#endif /* NEED_FAKE_GETNAMEINFO */
+#endif /* HAVE_FAKE_GETADDRINFO */
+#endif /* NEED_FAKE_GETADDRINFO */
+
+
+#ifdef WRAP_GETADDRINFO
+
+static inline
+int
+getaddrinfo (const char *name, const char *serv, const struct addrinfo *hint,
+ struct addrinfo **result)
+{
+ int aierr;
+#if defined(_AIX) || defined(COPY_FIRST_CANONNAME)
+ struct addrinfo *ai;
+#endif
+#ifdef NUMERIC_SERVICE_BROKEN
+ int service_is_numeric = 0;
+ int service_port = 0;
+ int socket_type = 0;
+#endif
+
+#ifdef DEBUG_ADDRINFO
+ debug_dump_getaddrinfo_args(name, serv, hint);
+#endif
+
+#ifdef NUMERIC_SERVICE_BROKEN
+ /* AIX 4.3.3 is broken. (Or perhaps out of date?)
+
+ If a numeric service is provided, and it doesn't correspond to
+ a known service name for tcp or udp (as appropriate), an error
+ code (for "host not found") is returned. If the port maps to a
+ known service for both udp and tcp, all is well. */
+ if (serv && serv[0] && isdigit(serv[0])) {
+ unsigned long lport;
+ char *end;
+ lport = strtoul(serv, &end, 10);
+ if (!*end) {
+ if (lport > 65535)
+ return EAI_SOCKTYPE;
+ service_is_numeric = 1;
+ service_port = lport;
+#ifdef AI_NUMERICSERV
+ if (hint && hint->ai_flags & AI_NUMERICSERV)
+ serv = "9";
+ else
+#endif
+ serv = "discard"; /* defined for both udp and tcp */
+ if (hint)
+ socket_type = hint->ai_socktype;
+ }
+ }
+#endif
+
+ aierr = system_getaddrinfo (name, serv, hint, result);
+ if (aierr || *result == 0) {
+#ifdef DEBUG_ADDRINFO
+ debug_dump_error(aierr);
+#endif
+ return aierr;
+ }
+
+ /* Linux libc version 6 prior to 2.3.4 is broken.
+
+ RFC 2553 says that when AI_CANONNAME is set, the ai_canonname
+ flag of the first returned structure has the canonical name of
+ the host. Instead, GNU libc sets ai_canonname in each returned
+ structure to the name that the corresponding address maps to,
+ if any, or a printable numeric form.
+
+ RFC 2553 bis and the new Open Group spec say that field will be
+ the canonical name if it can be determined, otherwise, the
+ provided hostname or a copy of it.
+
+ IMNSHO, "canonical name" means CNAME processing and not PTR
+ processing, but I can see arguing it. Using the numeric form
+ when that's not the form provided is just wrong. So, let's fix
+ it.
+
+ The glibc 2.2.5 sources indicate that the canonical name is
+ *not* allocated separately, it's just some extra storage tacked
+ on the end of the addrinfo structure. So, let's try this
+ approach: If getaddrinfo sets ai_canonname, we'll replace the
+ *first* one with allocated storage, and free up that pointer in
+ freeaddrinfo if it's set; the other ai_canonname fields will be
+ left untouched. And we'll just pray that the application code
+ won't mess around with the list structure; if we start doing
+ that, we'll have to start replacing and freeing all of the
+ ai_canonname fields.
+
+ Ref: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=133668 .
+
+ Since it's dependent on the target hostname, it's hard to check
+ for at configure time. The bug was fixed in glibc 2.3.4.
+ After the fix, the ai_canonname field is allocated, so our
+ workaround leaks memory. We disable the workaround for glibc
+ >= 2.4, but there is no easy way to test for glibc patch
+ versions, so we still leak memory under glibc 2.3.4 through
+ 2.3.6.
+
+ Some Windows documentation says that even when AI_CANONNAME is
+ set, the returned ai_canonname field can be null. The NetBSD
+ 1.5 implementation also does this, if the input hostname is a
+ numeric host address string. That case isn't handled well at
+ the moment.
+
+ Libc version 5 didn't have getaddrinfo at all. */
+
+#ifdef COPY_FIRST_CANONNAME
+ /*
+ * This code must *always* return an error, return a null
+ * ai_canonname, or return an ai_canonname allocated here using
+ * malloc, so that freeaddrinfo can always free a non-null
+ * ai_canonname. Note that it really doesn't matter if the
+ * AI_CANONNAME flag was set.
+ */
+ ai = *result;
+ if (ai->ai_canonname) {
+ struct hostent *hp;
+ const char *name2 = 0;
+ int i, herr;
+ GET_HOST_TMP htmp;
+
+ /*
+ * Current versions of GET_HOST_BY_NAME will fail if the
+ * target hostname has IPv6 addresses only. Make sure it
+ * fails fairly cleanly.
+ */
+ GET_HOST_BY_NAME (name, hp, herr, htmp);
+ if (hp == 0) {
+ /*
+ * This case probably means it's an IPv6-only name. If
+ * ai_canonname is a numeric address, get rid of it.
+ */
+ if (ai->ai_canonname && strchr(ai->ai_canonname, ':'))
+ ai->ai_canonname = 0;
+ name2 = ai->ai_canonname ? ai->ai_canonname : name;
+ } else {
+ /*
+ * Sometimes gethostbyname will be directed to /etc/hosts
+ * first, and sometimes that file will have entries with
+ * the unqualified name first. So take the first entry
+ * that looks like it could be a FQDN. Starting with h_name
+ * and then all the aliases.
+ */
+ for (i = 0, name2 = hp->h_name; name2; i++) {
+ if (strchr(name2, '.') != 0)
+ break;
+ name2 = hp->h_aliases[i];
+ }
+ if (name2 == 0)
+ name2 = hp->h_name;
+ }
+
+ ai->ai_canonname = strdup(name2);
+ if (name2 != 0 && ai->ai_canonname == 0) {
+ system_freeaddrinfo(ai);
+ *result = 0;
+#ifdef DEBUG_ADDRINFO
+ debug_dump_error(EAI_MEMORY);
+#endif
+ return EAI_MEMORY;
+ }
+ /* Zap the remaining ai_canonname fields glibc fills in, in
+ case the application messes around with the list
+ structure. */
+ while ((ai = ai->ai_next) != NULL)
+ ai->ai_canonname = 0;
+ }
+#endif
+
+#ifdef NUMERIC_SERVICE_BROKEN
+ if (service_port != 0) {
+ for (ai = *result; ai; ai = ai->ai_next) {
+ if (socket_type != 0 && ai->ai_socktype == 0)
+ /* Is this check actually needed? */
+ ai->ai_socktype = socket_type;
+ sa_setport(ai->ai_addr, service_port);
+ }
+ }
+#endif
+
+#ifdef _AIX
+ for (ai = *result; ai; ai = ai->ai_next) {
+ /* AIX 4.3.3 libc is broken. It doesn't set the family or len
+ fields of the sockaddr structures. Usually, sa_family is
+ zero, but I've seen it set to 1 in some cases also (maybe
+ just leftover from previous contents of the memory
+ block?). So, always override what libc returned. */
+ ai->ai_addr->sa_family = ai->ai_family;
+ }
+#endif
+
+ /* Not dealt with currently:
+
+ - Some versions of GNU libc can lose some IPv4 addresses in
+ certain cases when multiple IPv4 and IPv6 addresses are
+ available. */
+
+#ifdef DEBUG_ADDRINFO
+ debug_dump_addrinfos(*result);
+#endif
+
+ return 0;
+}
+
+static inline
+void freeaddrinfo (struct addrinfo *ai)
+{
+#ifdef COPY_FIRST_CANONNAME
+ if (ai) {
+ free(ai->ai_canonname);
+ ai->ai_canonname = 0;
+ system_freeaddrinfo(ai);
+ }
+#else
+ system_freeaddrinfo(ai);
+#endif
+}
+#endif /* WRAP_GETADDRINFO */
+
+#ifdef FAI_CACHE
+static int krb5int_lock_fac (void)
+{
+ int err;
+ err = krb5int_call_thread_support_init();
+ if (err)
+ return err;
+ return k5_mutex_lock(&krb5int_fac.lock);
+}
+
+static int krb5int_unlock_fac (void)
+{
+ return k5_mutex_unlock(&krb5int_fac.lock);
+}
+#endif
+
+/* Some systems don't define in6addr_any. */
+const struct in6_addr krb5int_in6addr_any = IN6ADDR_ANY_INIT;
+
+int krb5int_getaddrinfo (const char *node, const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **aip)
+{
+ return getaddrinfo(node, service, hints, aip);
+}
+
+void krb5int_freeaddrinfo (struct addrinfo *ai)
+{
+ freeaddrinfo(ai);
+}
+
+const char *krb5int_gai_strerror(int err)
+{
+ return gai_strerror(err);
+}
+
+int krb5int_getnameinfo (const struct sockaddr *sa, socklen_t salen,
+ char *hbuf, size_t hbuflen,
+ char *sbuf, size_t sbuflen,
+ int flags)
+{
+ return getnameinfo(sa, salen, hbuf, hbuflen, sbuf, sbuflen, flags);
+}
diff --git a/src/util/support/fnmatch.c b/src/util/support/fnmatch.c
new file mode 100644
index 000000000000..7e5a993f7ec0
--- /dev/null
+++ b/src/util/support/fnmatch.c
@@ -0,0 +1,207 @@
+/* $NetBSD: fnmatch.c,v 1.24 2011/01/31 19:10:18 christos Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * 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 University 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 REGENTS 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 REGENTS 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94";
+#else
+__RCSID("$NetBSD: fnmatch.c,v 1.24 2011/01/31 19:10:18 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ * Compares a filename or pathname to a pattern.
+ */
+
+#include "k5-platform.h"
+#include <ctype.h>
+#ifndef _DIAGASSERT
+#define _DIAGASSERT(x)
+#endif
+
+#define EOS '\0'
+
+static inline int
+foldcase(int ch, int flags)
+{
+
+ if ((flags & FNM_CASEFOLD) != 0 && isupper(ch))
+ return tolower(ch);
+ return ch;
+}
+
+#define FOLDCASE(ch, flags) foldcase((unsigned char)(ch), (flags))
+
+static const char *
+rangematch(const char *pattern, int test, int flags)
+{
+ int negate, ok;
+ char c, c2;
+
+ _DIAGASSERT(pattern != NULL);
+
+ /*
+ * A bracket expression starting with an unquoted circumflex
+ * character produces unspecified results (IEEE 1003.2-1992,
+ * 3.13.2). This implementation treats it like '!', for
+ * consistency with the regular expression syntax.
+ * J.T. Conklin (conklin@ngai.kaleida.com)
+ */
+ if ((negate = (*pattern == '!' || *pattern == '^')) != 0)
+ ++pattern;
+
+ for (ok = 0; (c = FOLDCASE(*pattern++, flags)) != ']';) {
+ if (c == '\\' && !(flags & FNM_NOESCAPE))
+ c = FOLDCASE(*pattern++, flags);
+ if (c == EOS)
+ return NULL;
+ if (*pattern == '-'
+ && (c2 = FOLDCASE(*(pattern + 1), flags)) != EOS &&
+ c2 != ']') {
+ pattern += 2;
+ if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+ c2 = FOLDCASE(*pattern++, flags);
+ if (c2 == EOS)
+ return NULL;
+ if (c <= test && test <= c2)
+ ok = 1;
+ } else if (c == test)
+ ok = 1;
+ }
+ return ok == negate ? NULL : pattern;
+}
+
+
+static int
+fnmatchx(const char *pattern, const char *string, int flags, size_t recursion)
+{
+ const char *stringstart;
+ char c, test;
+
+ _DIAGASSERT(pattern != NULL);
+ _DIAGASSERT(string != NULL);
+
+ if (recursion-- == 0)
+ return FNM_NORES;
+
+ for (stringstart = string;;) {
+ switch (c = FOLDCASE(*pattern++, flags)) {
+ case EOS:
+ if ((flags & FNM_LEADING_DIR) && *string == '/')
+ return 0;
+ return *string == EOS ? 0 : FNM_NOMATCH;
+ case '?':
+ if (*string == EOS)
+ return FNM_NOMATCH;
+ if (*string == '/' && (flags & FNM_PATHNAME))
+ return FNM_NOMATCH;
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return FNM_NOMATCH;
+ ++string;
+ break;
+ case '*':
+ c = FOLDCASE(*pattern, flags);
+ /* Collapse multiple stars. */
+ while (c == '*')
+ c = FOLDCASE(*++pattern, flags);
+
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return FNM_NOMATCH;
+
+ /* Optimize for pattern with * at end or before /. */
+ if (c == EOS) {
+ if (flags & FNM_PATHNAME)
+ return (flags & FNM_LEADING_DIR) ||
+ strchr(string, '/') == NULL ?
+ 0 : FNM_NOMATCH;
+ else
+ return 0;
+ } else if (c == '/' && flags & FNM_PATHNAME) {
+ if ((string = strchr(string, '/')) == NULL)
+ return FNM_NOMATCH;
+ break;
+ }
+
+ /* General case, use recursion. */
+ while ((test = FOLDCASE(*string, flags)) != EOS) {
+ int e;
+ switch ((e = fnmatchx(pattern, string,
+ flags & ~FNM_PERIOD, recursion))) {
+ case FNM_NOMATCH:
+ break;
+ default:
+ return e;
+ }
+ if (test == '/' && flags & FNM_PATHNAME)
+ break;
+ ++string;
+ }
+ return FNM_NOMATCH;
+ case '[':
+ if (*string == EOS)
+ return FNM_NOMATCH;
+ if (*string == '/' && flags & FNM_PATHNAME)
+ return FNM_NOMATCH;
+ if ((pattern = rangematch(pattern,
+ FOLDCASE(*string, flags), flags)) == NULL)
+ return FNM_NOMATCH;
+ ++string;
+ break;
+ case '\\':
+ if (!(flags & FNM_NOESCAPE)) {
+ if ((c = FOLDCASE(*pattern++, flags)) == EOS) {
+ c = '\\';
+ --pattern;
+ }
+ }
+ /* FALLTHROUGH */
+ default:
+ if (c != FOLDCASE(*string++, flags))
+ return FNM_NOMATCH;
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
+
+int
+k5_fnmatch(const char *pattern, const char *string, int flags)
+{
+ return fnmatchx(pattern, string, flags, 64);
+}
diff --git a/src/util/support/getopt.c b/src/util/support/getopt.c
new file mode 100644
index 000000000000..44cda68de5bb
--- /dev/null
+++ b/src/util/support/getopt.c
@@ -0,0 +1,149 @@
+/* -*- mode: c; c-file-style: "bsd"; indent-tabs-mode: t -*- */
+/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */
+
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ */
+
+#if !defined(HAVE_GETOPT) || !defined(HAVE_UNISTD_H)
+#if 0
+static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "k5-platform.h"
+
+#define __P(x) x
+#define _DIAGASSERT(x) assert(x)
+
+int opterr = 1, /* if error message should be printed */
+ optind = 1, /* index into parent argv vector */
+ optopt; /* character checked for validity */
+char *optarg; /* argument associated with option */
+
+static char * _progname __P((char *));
+int getopt_internal __P((int, char * const *, const char *));
+
+static char *
+_progname(nargv0)
+ char * nargv0;
+{
+ char * tmp;
+
+ _DIAGASSERT(nargv0 != NULL);
+
+ tmp = strrchr(nargv0, '/');
+ if (tmp)
+ tmp++;
+ else
+ tmp = nargv0;
+ return(tmp);
+}
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt(nargc, nargv, ostr)
+ int nargc;
+ char * const nargv[];
+ const char *ostr;
+{
+ static char *__progname = 0;
+ static char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+ __progname = __progname?__progname:_progname(*nargv);
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(ostr != NULL);
+
+ if (!*place) { /* update scanning pointer */
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-' /* found "--" */
+ && place[1] == '\0') {
+ ++optind;
+ place = EMSG;
+ return (-1);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (optopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)fprintf(stderr,
+ "%s: illegal option -- %c\n", __progname, optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ }
+ else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if (*ostr == ':')
+ return (BADARG);
+ if (opterr)
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ __progname, optopt);
+ return (BADCH);
+ }
+ else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
+#endif /* !defined(HAVE_GETOPT) || !defined(HAVE_UNISTD_H) */
diff --git a/src/util/support/getopt_long.c b/src/util/support/getopt_long.c
new file mode 100644
index 000000000000..1b508a149585
--- /dev/null
+++ b/src/util/support/getopt_long.c
@@ -0,0 +1,232 @@
+/* -*- mode: c; c-file-style: "bsd"; indent-tabs-mode: t -*- */
+/*
+ * Copyright (c) 1987, 1993, 1994, 1996
+ * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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 HAVE_GETOPT_LONG
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "k5-platform.h"
+
+#define __P(x) x
+#define _DIAGASSERT(x) assert(x)
+
+static char * __progname __P((char *));
+int getopt_internal __P((int, char * const *, const char *));
+
+static char *
+__progname(nargv0)
+ char * nargv0;
+{
+ char * tmp;
+
+ _DIAGASSERT(nargv0 != NULL);
+
+ tmp = strrchr(nargv0, '/');
+ if (tmp)
+ tmp++;
+ else
+ tmp = nargv0;
+ return(tmp);
+}
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_internal(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ static char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(ostr != NULL);
+
+ if (!*place) { /* update scanning pointer */
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ /* ++optind; */
+ place = EMSG;
+ return (-2);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (optopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)fprintf(stderr,
+ "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ } else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if ((opterr) && (*ostr != ':'))
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ __progname(nargv[0]), optopt);
+ return (BADARG);
+ } else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
+
+#if 0
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt2(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ int retval;
+
+ if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
+ retval = -1;
+ ++optind;
+ }
+ return(retval);
+}
+#endif
+
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long(nargc, nargv, options, long_options, index)
+ int nargc;
+ char ** nargv;
+ char * options;
+ struct option * long_options;
+ int * index;
+{
+ int retval;
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(options != NULL);
+ _DIAGASSERT(long_options != NULL);
+ /* index may be NULL */
+
+ if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
+ char *current_argv = nargv[optind++] + 2, *has_equal;
+ int i, current_argv_len, match = -1;
+
+ if (*current_argv == '\0') {
+ return(-1);
+ }
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ if (strncmp(current_argv, long_options[i].name, current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == (unsigned)current_argv_len) {
+ match = i;
+ break;
+ }
+ if (match == -1)
+ match = i;
+ }
+ if (match != -1) {
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else
+ optarg = nargv[optind++];
+ }
+ if ((long_options[match].has_arg == required_argument)
+ && (optarg == NULL)) {
+ /*
+ * Missing argument, leading :
+ * indicates no error should be generated
+ */
+ if ((opterr) && (*options != ':'))
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %s\n",
+ __progname(nargv[0]), current_argv);
+ return (BADARG);
+ }
+ } else { /* No matching argument */
+ if ((opterr) && (*options != ':'))
+ (void)fprintf(stderr,
+ "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
+ return (BADCH);
+ }
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ retval = 0;
+ } else
+ retval = long_options[match].val;
+ if (index)
+ *index = match;
+ }
+ return(retval);
+}
+#endif /* not HAVE_GETOPT_LONG */
diff --git a/src/util/support/gettimeofday.c b/src/util/support/gettimeofday.c
new file mode 100644
index 000000000000..8d8fcd84f507
--- /dev/null
+++ b/src/util/support/gettimeofday.c
@@ -0,0 +1,102 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/gettimeofday.c */
+/*
+ * Copyright (C) 2011 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/*
+ * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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.
+ */
+
+#include "k5-platform.h"
+
+#ifdef _WIN32
+
+#include <winsock2.h>
+
+int
+krb5int_gettimeofday (struct timeval *tp, void *ignore)
+{
+ FILETIME ft;
+ ULARGE_INTEGER li;
+ ULONGLONG ull;
+
+ GetSystemTimeAsFileTime(&ft);
+ li.LowPart = ft.dwLowDateTime;
+ li.HighPart = ft.dwHighDateTime;
+ ull = li.QuadPart;
+
+ ull -= 116444736000000000i64;
+ ull /= 10i64; /* ull is now in microseconds */
+
+ tp->tv_usec = (long)(ull % 1000000i64);
+ tp->tv_sec = (long)(ull / 1000000i64);
+
+ return 0;
+}
+
+#else
+
+/*
+ * Simple gettimeofday that only returns seconds.
+ */
+int
+krb5int_gettimeofday (struct timeval *tp, void *ignore)
+{
+ time_t t;
+
+ t = time(NULL);
+ tp->tv_sec = (long) t;
+ tp->tv_usec = 0;
+ return 0;
+}
+
+#endif /* !_WIN32 */
diff --git a/src/util/support/gmt_mktime.c b/src/util/support/gmt_mktime.c
new file mode 100644
index 000000000000..32fef4386cd4
--- /dev/null
+++ b/src/util/support/gmt_mktime.c
@@ -0,0 +1,168 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* This code placed in the public domain by Mark W. Eichin */
+
+#include "autoconf.h"
+#include <stdio.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#endif
+#else
+#include <time.h>
+#endif
+
+#include "k5-gmt_mktime.h"
+
+#if !HAVE_TIMEGM || TEST_LEAP
+static time_t gmt_mktime(struct tm *t);
+#endif
+
+/*
+ * Use the nonstandard timegm() (if available) to convert broken-down
+ * UTC times into time_t values. Use our custom gmt_mktime() if
+ * timegm() is not available.
+ *
+ * We use gmtime() (or gmtime_r()) when encoding ASN.1 GeneralizedTime
+ * values. On systems where a "right" (leap-second-aware) time zone
+ * is configured, gmtime() adjusts for the presence of accumulated
+ * leap seconds in the input time_t value. POSIX requires that time_t
+ * values omit leap seconds; systems configured to include leap
+ * seconds in their time_t values are non-conforming and will have
+ * difficulties exchanging timestamp information with other systems.
+ *
+ * We use krb5int_gmt_mktime() for decoding ASN.1 GeneralizedTime
+ * values. If timegm() is not available, krb5int_gmt_mktime() won't
+ * be the inverse of gmtime() on a system that counts leap seconds. A
+ * system configured with a "right" time zone probably has timegm()
+ * available; without it, an application would have no reliable way of
+ * converting broken-down UTC times into time_t values.
+ */
+time_t
+krb5int_gmt_mktime(struct tm *t)
+{
+#if HAVE_TIMEGM
+ return timegm(t);
+#else
+ return gmt_mktime(t);
+#endif
+}
+
+#if !HAVE_TIMEGM || TEST_LEAP
+
+/* take a struct tm, return seconds from GMT epoch */
+/* like mktime, this ignores tm_wday and tm_yday. */
+/* unlike mktime, this does not set them... it only passes a return value. */
+
+static const int days_in_month[12] = {
+ 0, /* jan 31 */
+ 31, /* feb 28 */
+ 59, /* mar 31 */
+ 90, /* apr 30 */
+ 120, /* may 31 */
+ 151, /* jun 30 */
+ 181, /* jul 31 */
+ 212, /* aug 31 */
+ 243, /* sep 30 */
+ 273, /* oct 31 */
+ 304, /* nov 30 */
+ 334 /* dec 31 */
+};
+
+#define hasleapday(year) (year%400?(year%100?(year%4?0:1):0):1)
+
+static time_t
+gmt_mktime(struct tm *t)
+{
+ time_t accum;
+
+#define assert_time(cnd) if(!(cnd)) return (time_t) -1
+
+ /*
+ * For 32-bit signed time_t centered on 1/1/1970, the range is:
+ * time 0x80000000 -> Fri Dec 13 16:45:52 1901
+ * time 0x7fffffff -> Mon Jan 18 22:14:07 2038
+ *
+ * So years 1901 and 2038 are allowable, but we can't encode all
+ * dates in those years, and we're not doing overflow/underflow
+ * checking for such cases.
+ */
+ assert_time(t->tm_year>=1);
+ assert_time(t->tm_year<=138);
+
+ assert_time(t->tm_mon>=0);
+ assert_time(t->tm_mon<=11);
+ assert_time(t->tm_mday>=1);
+ assert_time(t->tm_mday<=31);
+ assert_time(t->tm_hour>=0);
+ assert_time(t->tm_hour<=23);
+ assert_time(t->tm_min>=0);
+ assert_time(t->tm_min<=59);
+ assert_time(t->tm_sec>=0);
+ assert_time(t->tm_sec<=62);
+
+#undef assert_time
+
+
+ accum = t->tm_year - 70;
+ accum *= 365; /* 365 days/normal year */
+
+ /* add in leap day for all previous years */
+ if (t->tm_year >= 70)
+ accum += (t->tm_year - 69) / 4;
+ else
+ accum -= (72 - t->tm_year) / 4;
+ /* add in leap day for this year */
+ if(t->tm_mon >= 2) /* march or later */
+ if(hasleapday((t->tm_year + 1900))) accum += 1;
+
+ accum += days_in_month[t->tm_mon];
+ accum += t->tm_mday-1; /* days of month are the only 1-based field */
+ accum *= 24; /* 24 hour/day */
+ accum += t->tm_hour;
+ accum *= 60; /* 60 minute/hour */
+ accum += t->tm_min;
+ accum *= 60; /* 60 seconds/minute */
+ accum += t->tm_sec;
+
+ return accum;
+}
+#endif /* !HAVE_TIMEGM || TEST_LEAP */
+
+#ifdef TEST_LEAP
+int
+main (int argc, char *argv[])
+{
+ int yr;
+ time_t t;
+ struct tm tm = {
+ .tm_mon = 0, .tm_mday = 1,
+ .tm_hour = 0, .tm_min = 0, .tm_sec = 0,
+ };
+ for (yr = 60; yr <= 104; yr++)
+ {
+ printf ("1/1/%d%c -> ", 1900 + yr, hasleapday((1900+yr)) ? '*' : ' ');
+ tm.tm_year = yr;
+ t = gmt_mktime (&tm);
+ if (t == (time_t) -1)
+ printf ("-1\n");
+ else
+ {
+ long u;
+ if (t % (24 * 60 * 60))
+ printf ("(not integral multiple of days) ");
+ u = t / (24 * 60 * 60);
+ printf ("%3ld*365%+ld\t0x%08lx\n",
+ (long) (u / 365), (long) (u % 365),
+ (long) t);
+ }
+ }
+ t = 0x80000000, printf ("time 0x%lx -> %s", t, ctime (&t));
+ t = 0x7fffffff, printf ("time 0x%lx -> %s", t, ctime (&t));
+ return 0;
+}
+#endif
diff --git a/src/util/support/init-addrinfo.c b/src/util/support/init-addrinfo.c
new file mode 100644
index 000000000000..68c3dd6ed799
--- /dev/null
+++ b/src/util/support/init-addrinfo.c
@@ -0,0 +1,68 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004 by the Massachusetts Institute of Technology,
+ * Cambridge, MA, USA. All Rights Reserved.
+ *
+ * This software is being provided to you, the LICENSEE, by the
+ * Massachusetts Institute of Technology (M.I.T.) under the following
+ * license. By obtaining, using and/or copying this software, you agree
+ * that you have read, understood, and will comply with these terms and
+ * conditions:
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify and distribute
+ * this software and its documentation for any purpose and without fee or
+ * royalty is hereby granted, provided that you agree to comply with the
+ * following copyright notice and statements, including the disclaimer, and
+ * that the same appear on ALL copies of the software and documentation,
+ * including modifications that you make for internal use or for
+ * distribution:
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", AND M.I.T. MAKES NO REPRESENTATIONS
+ * OR WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not
+ * limitation, M.I.T. MAKES NO REPRESENTATIONS OR WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF
+ * THE LICENSED SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY
+ * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+ *
+ * The name of the Massachusetts Institute of Technology or M.I.T. may NOT
+ * be used in advertising or publicity pertaining to distribution of the
+ * software. Title to copyright in this software and any associated
+ * documentation shall at all times remain with M.I.T., and USER agrees to
+ * preserve same.
+ *
+ * Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ */
+
+/* Stuff that needs initialization for fake-addrinfo.c.
+
+ Separated out, so that static linking against this library doesn't
+ require pulling in socket/nsl/whatever libraries for code not using
+ getaddrinfo. */
+
+#include "k5-platform.h"
+#include "k5-thread.h"
+#include "port-sockets.h"
+#include "socket-utils.h"
+
+#define IMPLEMENT_FAKE_GETADDRINFO
+#include "fake-addrinfo.h"
+#include "cache-addrinfo.h"
+
+struct fac krb5int_fac = { K5_MUTEX_PARTIAL_INITIALIZER, 0 };
+
+int krb5int_init_fac (void)
+{
+ return k5_mutex_finish_init(&krb5int_fac.lock);
+}
+
+void krb5int_fini_fac (void)
+{
+ k5_mutex_destroy(&krb5int_fac.lock);
+}
diff --git a/src/util/support/ipc_stream.c b/src/util/support/ipc_stream.c
new file mode 100644
index 000000000000..72d3070549eb
--- /dev/null
+++ b/src/util/support/ipc_stream.c
@@ -0,0 +1,468 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/ipc_stream.c */
+/*
+ * Copyright 2006, 2007, 2009 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+#include "k5-ipc_stream.h"
+
+#if !defined(htonll)
+#define htonll(x) k5_htonll(x)
+#endif
+
+#if !defined(ntohll)
+#define ntohll(x) k5_ntohll(x)
+#endif
+
+/* Add debugging later */
+#define k5_check_error(x) (x)
+
+struct k5_ipc_stream_s {
+ char *data;
+ uint64_t size;
+ uint64_t max_size;
+};
+
+static const struct k5_ipc_stream_s k5_ipc_stream_initializer = { NULL, 0, 0 };
+
+#define K5_IPC_STREAM_SIZE_INCREMENT 128
+
+/* ------------------------------------------------------------------------ */
+
+static uint32_t krb5int_ipc_stream_reallocate (k5_ipc_stream io_stream,
+ uint64_t in_new_size)
+{
+ int32_t err = 0;
+ uint64_t new_max_size = 0;
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ uint64_t old_max_size = io_stream->max_size;
+ new_max_size = io_stream->max_size;
+
+ if (in_new_size > old_max_size) {
+ /* Expand the stream */
+ while (in_new_size > new_max_size) {
+ new_max_size += K5_IPC_STREAM_SIZE_INCREMENT;
+ }
+
+
+ } else if ((in_new_size + K5_IPC_STREAM_SIZE_INCREMENT) < old_max_size) {
+ /* Shrink the array, but never drop below K5_IPC_STREAM_SIZE_INCREMENT */
+ while ((in_new_size + K5_IPC_STREAM_SIZE_INCREMENT) < new_max_size &&
+ (new_max_size > K5_IPC_STREAM_SIZE_INCREMENT)) {
+ new_max_size -= K5_IPC_STREAM_SIZE_INCREMENT;
+ }
+ }
+ }
+
+ if (!err && new_max_size != io_stream->max_size) {
+ char *data = io_stream->data;
+
+ if (!data) {
+ data = malloc (new_max_size * sizeof (*data));
+ } else {
+ data = realloc (data, new_max_size * sizeof (*data));
+ }
+
+ if (data) {
+ io_stream->data = data;
+ io_stream->max_size = new_max_size;
+ } else {
+ err = k5_check_error (ENOMEM);
+ }
+ }
+
+ return k5_check_error (err);
+}
+
+/* ------------------------------------------------------------------------ */
+
+int32_t krb5int_ipc_stream_new (k5_ipc_stream *out_stream)
+{
+ int32_t err = 0;
+ k5_ipc_stream stream = NULL;
+
+ if (!out_stream) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ stream = malloc (sizeof (*stream));
+ if (stream) {
+ *stream = k5_ipc_stream_initializer;
+ } else {
+ err = k5_check_error (ENOMEM);
+ }
+ }
+
+ if (!err) {
+ *out_stream = stream;
+ stream = NULL;
+ }
+
+ krb5int_ipc_stream_release (stream);
+
+ return k5_check_error (err);
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_release (k5_ipc_stream io_stream)
+{
+ int32_t err = 0;
+
+ if (!err && io_stream) {
+ free (io_stream->data);
+ free (io_stream);
+ }
+
+ return err;
+}
+
+/* ------------------------------------------------------------------------ */
+
+uint64_t krb5int_ipc_stream_size (k5_ipc_stream in_stream)
+{
+ return in_stream ? in_stream->size : 0;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+const char *krb5int_ipc_stream_data (k5_ipc_stream in_stream)
+{
+ return in_stream ? in_stream->data : NULL;
+}
+
+#ifdef TARGET_OS_MAC
+#pragma mark -
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_read (k5_ipc_stream io_stream,
+ void *io_data,
+ uint64_t in_size)
+{
+ int32_t err = 0;
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+ if (!io_data ) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ if (in_size > io_stream->size) {
+ err = k5_check_error (EINVAL);
+ }
+ }
+
+ if (!err) {
+ memcpy (io_data, io_stream->data, in_size);
+ memmove (io_stream->data, &io_stream->data[in_size],
+ io_stream->size - in_size);
+
+ err = krb5int_ipc_stream_reallocate (io_stream, io_stream->size - in_size);
+
+ if (!err) {
+ io_stream->size -= in_size;
+ }
+ }
+
+ return k5_check_error (err);
+}
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_write (k5_ipc_stream io_stream,
+ const void *in_data,
+ uint64_t in_size)
+{
+ int32_t err = 0;
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+ if (!in_data ) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ /* Security check: Do not let the caller overflow the length */
+ if (in_size > (UINT64_MAX - io_stream->size)) {
+ err = k5_check_error (EINVAL);
+ }
+ }
+
+ if (!err) {
+ err = krb5int_ipc_stream_reallocate (io_stream, io_stream->size + in_size);
+ }
+
+ if (!err) {
+ memcpy (&io_stream->data[io_stream->size], in_data, in_size);
+ io_stream->size += in_size;
+ }
+
+ return k5_check_error (err);
+}
+
+#ifdef TARGET_OS_MAC
+#pragma mark -
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+void krb5int_ipc_stream_free_string (char *in_string)
+{
+ free (in_string);
+}
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_read_string (k5_ipc_stream io_stream,
+ char **out_string)
+{
+ int32_t err = 0;
+ uint32_t length = 0;
+ char *string = NULL;
+
+ if (!io_stream ) { err = k5_check_error (EINVAL); }
+ if (!out_string) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ err = krb5int_ipc_stream_read_uint32 (io_stream, &length);
+ }
+
+ if (!err) {
+ string = malloc (length);
+ if (!string) { err = k5_check_error (ENOMEM); }
+ }
+
+ if (!err) {
+ err = krb5int_ipc_stream_read (io_stream, string, length);
+ }
+
+ if (!err) {
+ *out_string = string;
+ string = NULL;
+ }
+
+ free (string);
+
+ return k5_check_error (err);
+}
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_write_string (k5_ipc_stream io_stream,
+ const char *in_string)
+{
+ int32_t err = 0;
+ uint32_t length = 0;
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+ if (!in_string) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ length = strlen (in_string) + 1;
+
+ err = krb5int_ipc_stream_write_uint32 (io_stream, length);
+ }
+
+ if (!err) {
+ err = krb5int_ipc_stream_write (io_stream, in_string, length);
+ }
+
+ return k5_check_error (err);
+}
+
+#ifdef TARGET_OS_MAC
+#pragma mark -
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_read_int32 (k5_ipc_stream io_stream,
+ int32_t *out_int32)
+{
+ int32_t err = 0;
+ int32_t int32 = 0;
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+ if (!out_int32) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ err = krb5int_ipc_stream_read (io_stream, &int32, sizeof (int32));
+ }
+
+ if (!err) {
+ *out_int32 = ntohl (int32);
+ }
+
+ return k5_check_error (err);
+}
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_write_int32 (k5_ipc_stream io_stream,
+ int32_t in_int32)
+{
+ int32_t err = 0;
+ int32_t int32 = htonl (in_int32);
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ err = krb5int_ipc_stream_write (io_stream, &int32, sizeof (int32));
+ }
+
+ return k5_check_error (err);
+}
+
+#ifdef TARGET_OS_MAC
+#pragma mark -
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_read_uint32 (k5_ipc_stream io_stream,
+ uint32_t *out_uint32)
+{
+ int32_t err = 0;
+ uint32_t uint32 = 0;
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+ if (!out_uint32) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ err = krb5int_ipc_stream_read (io_stream, &uint32, sizeof (uint32));
+ }
+
+ if (!err) {
+ *out_uint32 = ntohl (uint32);
+ }
+
+ return k5_check_error (err);
+}
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_write_uint32 (k5_ipc_stream io_stream,
+ uint32_t in_uint32)
+{
+ int32_t err = 0;
+ int32_t uint32 = htonl (in_uint32);
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ err = krb5int_ipc_stream_write (io_stream, &uint32, sizeof (uint32));
+ }
+
+ return k5_check_error (err);
+}
+
+#ifdef TARGET_OS_MAC
+#pragma mark -
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_read_int64 (k5_ipc_stream io_stream,
+ int64_t *out_int64)
+{
+ int32_t err = 0;
+ uint64_t int64 = 0;
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+ if (!out_int64) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ err = krb5int_ipc_stream_read (io_stream, &int64, sizeof (int64));
+ }
+
+ if (!err) {
+ *out_int64 = ntohll (int64);
+ }
+
+ return k5_check_error (err);
+}
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_write_int64 (k5_ipc_stream io_stream,
+ int64_t in_int64)
+{
+ int32_t err = 0;
+ int64_t int64 = htonll (in_int64);
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ err = krb5int_ipc_stream_write (io_stream, &int64, sizeof (int64));
+ }
+
+ return k5_check_error (err);
+}
+
+
+#ifdef TARGET_OS_MAC
+#pragma mark -
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_read_uint64 (k5_ipc_stream io_stream,
+ uint64_t *out_uint64)
+{
+ int32_t err = 0;
+ uint64_t uint64 = 0;
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+ if (!out_uint64) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ err = krb5int_ipc_stream_read (io_stream, &uint64, sizeof (uint64));
+ }
+
+ if (!err) {
+ *out_uint64 = ntohll (uint64);
+ }
+
+ return k5_check_error (err);
+}
+
+/* ------------------------------------------------------------------------ */
+
+uint32_t krb5int_ipc_stream_write_uint64 (k5_ipc_stream io_stream,
+ uint64_t in_uint64)
+{
+ int32_t err = 0;
+ int64_t uint64 = htonll (in_uint64);
+
+ if (!io_stream) { err = k5_check_error (EINVAL); }
+
+ if (!err) {
+ err = krb5int_ipc_stream_write (io_stream, &uint64, sizeof (uint64));
+ }
+
+ return k5_check_error (err);
+}
diff --git a/src/util/support/json.c b/src/util/support/json.c
new file mode 100644
index 000000000000..ae2feae974d3
--- /dev/null
+++ b/src/util/support/json.c
@@ -0,0 +1,1092 @@
+/* -*- 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 <k5-platform.h>
+#include <k5-base64.h>
+#include <k5-json.h>
+#include <k5-buf.h>
+
+#define MAX_DECODE_DEPTH 64
+#define MIN_ALLOC_SLOT 16
+
+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;
+
+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))
+
+k5_json_value
+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;
+}
+
+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;
+}
+
+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 = { K5_JSON_TID_NULL, "null", NULL };
+
+int
+k5_json_null_create(k5_json_null *val_out)
+{
+ *val_out = alloc_value(&null_type, 0);
+ return (*val_out == NULL) ? ENOMEM : 0;
+}
+
+int
+k5_json_null_create_val(k5_json_value *val_out)
+{
+ *val_out = alloc_value(&null_type, 0);
+ return (*val_out == NULL) ? ENOMEM : 0;
+}
+
+/*** Boolean type ***/
+
+static struct json_type_st bool_type = { K5_JSON_TID_BOOL, "bool", NULL };
+
+int
+k5_json_bool_create(int truth, k5_json_bool *val_out)
+{
+ k5_json_bool b;
+
+ *val_out = NULL;
+ b = alloc_value(&bool_type, 1);
+ if (b == NULL)
+ return ENOMEM;
+ *(unsigned char *)b = !!truth;
+ *val_out = b;
+ return 0;
+}
+
+int
+k5_json_bool_value(k5_json_bool bval)
+{
+ return *(unsigned char *)bval;
+}
+
+/*** 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 = {
+ K5_JSON_TID_ARRAY, "array", array_dealloc
+};
+
+int
+k5_json_array_create(k5_json_array *val_out)
+{
+ *val_out = alloc_value(&array_type, sizeof(struct k5_json_array_st));
+ return (*val_out == NULL) ? ENOMEM : 0;
+}
+
+int
+k5_json_array_add(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% (MIN_ALLOC_SLOT minimum). */
+ new_alloc = array->len + 1 + (array->len >> 1);
+ if (new_alloc < MIN_ALLOC_SLOT)
+ new_alloc = MIN_ALLOC_SLOT;
+ 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;
+}
+
+size_t
+k5_json_array_length(k5_json_array array)
+{
+ return array->len;
+}
+
+k5_json_value
+k5_json_array_get(k5_json_array array, size_t idx)
+{
+ if (idx >= array->len)
+ abort();
+ return array->values[idx];
+}
+
+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);
+}
+
+int
+k5_json_array_fmt(k5_json_array *array_out, const char *template, ...)
+{
+ const char *p;
+ va_list ap;
+ const char *cstring;
+ unsigned char *data;
+ size_t len;
+ long long nval;
+ k5_json_array array;
+ k5_json_value val;
+ k5_json_number num;
+ k5_json_string str;
+ k5_json_bool b;
+ k5_json_null null;
+ int truth, ret;
+
+ *array_out = NULL;
+ if (k5_json_array_create(&array))
+ return ENOMEM;
+ va_start(ap, template);
+ for (p = template; *p != '\0'; p++) {
+ switch (*p) {
+ case 'v':
+ val = k5_json_retain(va_arg(ap, k5_json_value));
+ break;
+ case 'n':
+ if (k5_json_null_create(&null))
+ goto err;
+ val = null;
+ break;
+ case 'b':
+ truth = va_arg(ap, int);
+ if (k5_json_bool_create(truth, &b))
+ goto err;
+ val = b;
+ break;
+ case 'i':
+ nval = va_arg(ap, int);
+ if (k5_json_number_create(nval, &num))
+ goto err;
+ val = num;
+ break;
+ case 'L':
+ nval = va_arg(ap, long long);
+ if (k5_json_number_create(nval, &num))
+ goto err;
+ val = num;
+ break;
+ case 's':
+ cstring = va_arg(ap, const char *);
+ if (cstring == NULL) {
+ if (k5_json_null_create(&null))
+ goto err;
+ val = null;
+ } else {
+ if (k5_json_string_create(cstring, &str))
+ goto err;
+ val = str;
+ }
+ break;
+ case 'B':
+ data = va_arg(ap, unsigned char *);
+ len = va_arg(ap, size_t);
+ if (k5_json_string_create_base64(data, len, &str))
+ goto err;
+ val = str;
+ break;
+ default:
+ goto err;
+ }
+ ret = k5_json_array_add(array, val);
+ k5_json_release(val);
+ if (ret)
+ goto err;
+ }
+ va_end(ap);
+ *array_out = array;
+ return 0;
+
+err:
+ va_end(ap);
+ k5_json_release(array);
+ return ENOMEM;
+}
+
+/*** 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 = {
+ K5_JSON_TID_OBJECT, "object", object_dealloc
+};
+
+int
+k5_json_object_create(k5_json_object *val_out)
+{
+ *val_out = alloc_value(&object_type, sizeof(struct k5_json_object_st));
+ return (*val_out == NULL) ? ENOMEM : 0;
+}
+
+size_t
+k5_json_object_count(k5_json_object obj)
+{
+ return obj->len;
+}
+
+/* Return the entry for key within obj, or NULL if none exists. */
+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;
+}
+
+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;
+}
+
+int
+k5_json_object_set(k5_json_object obj, const char *key, k5_json_value val)
+{
+ struct entry *ent, *ptr;
+ size_t new_alloc, i;
+
+ ent = object_search(obj, key);
+ if (ent != NULL) {
+ k5_json_release(ent->value);
+ if (val == NULL) {
+ /* Remove this key. */
+ free(ent->key);
+ for (i = ent - obj->entries; i < obj->len - 1; i++)
+ obj->entries[i] = obj->entries[i + 1];
+ obj->len--;
+ } else {
+ /* Overwrite this key's value with the new one. */
+ ent->value = k5_json_retain(val);
+ }
+ return 0;
+ }
+
+ /* If didn't find a key the caller asked to remove, do nothing. */
+ if (val == NULL)
+ return 0;
+
+ if (obj->len >= obj->allocated) {
+ /* Increase the number of slots by 50% (MIN_ALLOC_SLOT minimum). */
+ new_alloc = obj->len + 1 + (obj->len >> 1);
+ if (new_alloc < MIN_ALLOC_SLOT)
+ new_alloc = MIN_ALLOC_SLOT;
+ 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;
+}
+
+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 = {
+ K5_JSON_TID_STRING, "string", NULL
+};
+
+int
+k5_json_string_create(const char *cstring, k5_json_string *val_out)
+{
+ return k5_json_string_create_len(cstring, strlen(cstring), val_out);
+}
+
+int
+k5_json_string_create_len(const void *data, size_t len,
+ k5_json_string *val_out)
+{
+ char *s;
+
+ *val_out = NULL;
+ s = alloc_value(&string_type, len + 1);
+ if (s == NULL)
+ return ENOMEM;
+ if (len > 0)
+ memcpy(s, data, len);
+ s[len] = '\0';
+ *val_out = (k5_json_string)s;
+ return 0;
+}
+
+int
+k5_json_string_create_base64(const void *data, size_t len,
+ k5_json_string *val_out)
+{
+ char *base64;
+ int ret;
+
+ *val_out = NULL;
+ base64 = k5_base64_encode(data, len);
+ if (base64 == NULL)
+ return ENOMEM;
+ ret = k5_json_string_create(base64, val_out);
+ free(base64);
+ return ret;
+}
+
+const char *
+k5_json_string_utf8(k5_json_string string)
+{
+ return (const char *)string;
+}
+
+int
+k5_json_string_unbase64(k5_json_string string, unsigned char **data_out,
+ size_t *len_out)
+{
+ unsigned char *data;
+ size_t len;
+
+ *data_out = NULL;
+ *len_out = 0;
+ data = k5_base64_decode((const char *)string, &len);
+ if (data == NULL)
+ return (len == 0) ? ENOMEM : EINVAL;
+ *data_out = data;
+ *len_out = len;
+ return 0;
+}
+
+/*** Number type ***/
+
+static struct json_type_st number_type = {
+ K5_JSON_TID_NUMBER, "number", NULL
+};
+
+int
+k5_json_number_create(long long number, k5_json_number *val_out)
+{
+ k5_json_number n;
+
+ *val_out = NULL;
+ n = alloc_value(&number_type, sizeof(long long));
+ if (n == NULL)
+ return ENOMEM;
+ *((long long *)n) = number;
+ *val_out = n;
+ return 0;
+}
+
+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";
+
+static int encode_value(struct k5buf *buf, k5_json_value val);
+
+static void
+encode_string(struct k5buf *buf, const char *str)
+{
+ size_t n;
+ const char *p;
+
+ k5_buf_add(buf, "\"");
+ while (*str != '\0') {
+ n = strcspn(str, needs_quote);
+ k5_buf_add_len(buf, str, n);
+ str += n;
+ if (*str == '\0')
+ break;
+ k5_buf_add(buf, "\\");
+ p = strchr(quotemap_c, *str);
+ if (p != NULL)
+ k5_buf_add_len(buf, quotemap_json + (p - quotemap_c), 1);
+ else
+ k5_buf_add_fmt(buf, "u00%02X", (unsigned int)*str);
+ str++;
+ }
+ k5_buf_add(buf, "\"");
+}
+
+struct obj_ctx {
+ struct k5buf *buf;
+ int ret;
+ int first;
+};
+
+static void
+encode_obj_entry(void *ctx, const char *key, k5_json_value value)
+{
+ struct obj_ctx *j = ctx;
+
+ if (j->ret)
+ return;
+ if (j->first)
+ j->first = 0;
+ else
+ k5_buf_add(j->buf, ",");
+ encode_string(j->buf, key);
+ k5_buf_add(j->buf, ":");
+ j->ret = encode_value(j->buf, value);
+}
+
+static int
+encode_value(struct k5buf *buf, k5_json_value val)
+{
+ k5_json_tid type;
+ int ret;
+ size_t i, len;
+ struct obj_ctx ctx;
+
+ if (val == NULL)
+ return EINVAL;
+
+ type = k5_json_get_tid(val);
+ switch (type) {
+ case K5_JSON_TID_ARRAY:
+ k5_buf_add(buf, "[");
+ len = k5_json_array_length(val);
+ for (i = 0; i < len; i++) {
+ if (i != 0)
+ k5_buf_add(buf, ",");
+ ret = encode_value(buf, k5_json_array_get(val, i));
+ if (ret)
+ return ret;
+ }
+ k5_buf_add(buf, "]");
+ return 0;
+
+ case K5_JSON_TID_OBJECT:
+ k5_buf_add(buf, "{");
+ ctx.buf = buf;
+ ctx.ret = 0;
+ ctx.first = 1;
+ k5_json_object_iterate(val, encode_obj_entry, &ctx);
+ k5_buf_add(buf, "}");
+ return ctx.ret;
+
+ case K5_JSON_TID_STRING:
+ encode_string(buf, k5_json_string_utf8(val));
+ return 0;
+
+ case K5_JSON_TID_NUMBER:
+ k5_buf_add_fmt(buf, "%lld", k5_json_number_value(val));
+ return 0;
+
+ case K5_JSON_TID_NULL:
+ k5_buf_add(buf, "null");
+ return 0;
+
+ case K5_JSON_TID_BOOL:
+ k5_buf_add(buf, k5_json_bool_value(val) ? "true" : "false");
+ return 0;
+
+ default:
+ return EINVAL;
+ }
+}
+
+int
+k5_json_encode(k5_json_value val, char **json_out)
+{
+ struct k5buf buf;
+ int ret;
+
+ *json_out = NULL;
+ k5_buf_init_dynamic(&buf);
+ ret = encode_value(&buf, val);
+ if (ret) {
+ k5_buf_free(&buf);
+ return ret;
+ }
+ if (k5_buf_status(&buf) != 0)
+ return ENOMEM;
+ *json_out = buf.data;
+ return 0;
+}
+
+/*** JSON decoding ***/
+
+struct decode_ctx {
+ const unsigned char *p;
+ size_t depth;
+};
+
+static int parse_value(struct decode_ctx *ctx, k5_json_value *val_out);
+
+/* 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 (per RFC 5234 HEXDIG). */
+static inline int
+is_hex_digit(unsigned char c)
+{
+ return is_digit(c) || ('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;
+ abort();
+}
+
+/* Parse a JSON number (which must be an integer in the signed 64-bit range; we
+ * do not allow floating-point numbers). */
+static int
+parse_number(struct decode_ctx *ctx, k5_json_number *val_out)
+{
+ const unsigned long long umax = ~0ULL, smax = (1ULL << 63) - 1;
+ unsigned long long number = 0;
+ int neg = 1;
+
+ *val_out = NULL;
+
+ if (*ctx->p == '-') {
+ neg = -1;
+ ctx->p++;
+ }
+
+ if (!is_digit(*ctx->p))
+ return EINVAL;
+
+ /* 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 EOVERFLOW;
+ 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 EOVERFLOW;
+
+ return k5_json_number_create(number * neg, val_out);
+}
+
+/* Parse a JSON string (which must not quote Unicode code points above 256). */
+static int
+parse_string(struct decode_ctx *ctx, char **str_out)
+{
+ const unsigned char *p, *start, *end = NULL;
+ const char *q;
+ char *buf, *pos;
+ unsigned int code;
+
+ *str_out = NULL;
+
+ /* Find the start and end of the string. */
+ if (*ctx->p != '"')
+ return EINVAL;
+ start = ++ctx->p;
+ for (; *ctx->p != '\0'; ctx->p++) {
+ if (*ctx->p == '\\') {
+ ctx->p++;
+ if (*ctx->p == '\0')
+ return EINVAL;
+ } else if (*ctx->p == '"') {
+ end = ctx->p++;
+ break;
+ }
+ }
+ if (end == NULL)
+ return EINVAL;
+
+ pos = buf = malloc(end - start + 1);
+ if (buf == NULL)
+ return ENOMEM;
+ 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 EINVAL;
+ }
+ p += 5;
+ } else {
+ q = strchr(quotemap_json, *p);
+ if (q != NULL) {
+ *pos++ = quotemap_c[q - quotemap_json];
+ } else {
+ free(buf);
+ return EINVAL;
+ }
+ p++;
+ }
+ } else {
+ *pos++ = *p++;
+ }
+ }
+ *pos = '\0';
+ *str_out = buf;
+ return 0;
+}
+
+/* Parse an object association and place it into obj. */
+static int
+parse_object_association(k5_json_object obj, struct decode_ctx *ctx)
+{
+ char *key = NULL;
+ k5_json_value val;
+ int ret;
+
+ /* Parse the key and value. */
+ ret = parse_string(ctx, &key);
+ if (ret)
+ return ret;
+ if (white_spaces(ctx))
+ goto invalid;
+ if (*ctx->p != ':')
+ goto invalid;
+ ctx->p++;
+ if (white_spaces(ctx))
+ goto invalid;
+ ret = parse_value(ctx, &val);
+ if (ret) {
+ free(key);
+ return ret;
+ }
+
+ /* Add the key and value to obj. */
+ ret = k5_json_object_set(obj, key, val);
+ free(key);
+ k5_json_release(val);
+ return ret;
+
+invalid:
+ free(key);
+ return EINVAL;
+}
+
+/* Parse a JSON object. */
+static int
+parse_object(struct decode_ctx *ctx, k5_json_object *val_out)
+{
+ k5_json_object obj = NULL;
+ int ret;
+
+ *val_out = NULL;
+
+ /* Parse past the opening brace. */
+ if (*ctx->p != '{')
+ return EINVAL;
+ ctx->p++;
+ if (white_spaces(ctx))
+ return EINVAL;
+
+ ret = k5_json_object_create(&obj);
+ if (ret)
+ return ret;
+
+ /* Pairs associations until we reach the terminating brace. */
+ if (*ctx->p != '}') {
+ while (1) {
+ ret = parse_object_association(obj, ctx);
+ if (ret) {
+ k5_json_release(obj);
+ return ret;
+ }
+ if (white_spaces(ctx))
+ goto invalid;
+ if (*ctx->p == '}')
+ break;
+ if (*ctx->p != ',')
+ goto invalid;
+ ctx->p++;
+ if (white_spaces(ctx))
+ goto invalid;
+ }
+ }
+ ctx->p++;
+ *val_out = obj;
+ return 0;
+
+invalid:
+ k5_json_release(obj);
+ return EINVAL;
+}
+
+/* Parse an value and place it into array. */
+static int
+parse_array_item(k5_json_array array, struct decode_ctx *ctx)
+{
+ k5_json_value val;
+ int ret;
+
+ ret = parse_value(ctx, &val);
+ if (ret)
+ return ret;
+ ret = k5_json_array_add(array, val);
+ k5_json_release(val);
+ return ret;
+}
+
+/* Parse a JSON array. */
+static int
+parse_array(struct decode_ctx *ctx, k5_json_array *val_out)
+{
+ k5_json_array array = NULL;
+ int ret;
+
+ *val_out = NULL;
+
+ /* Parse past the opening bracket. */
+ if (*ctx->p != '[')
+ return EINVAL;
+ ctx->p++;
+ if (white_spaces(ctx))
+ return EINVAL;
+
+ ret = k5_json_array_create(&array);
+ if (ret)
+ return ret;
+
+ /* Pairs values until we reach the terminating bracket. */
+ if (*ctx->p != ']') {
+ while (1) {
+ ret = parse_array_item(array, ctx);
+ if (ret) {
+ k5_json_release(array);
+ return ret;
+ }
+ if (white_spaces(ctx))
+ goto invalid;
+ if (*ctx->p == ']')
+ break;
+ if (*ctx->p != ',')
+ goto invalid;
+ ctx->p++;
+ if (white_spaces(ctx))
+ goto invalid;
+ }
+ }
+ ctx->p++;
+ *val_out = array;
+ return 0;
+
+invalid:
+ k5_json_release(array);
+ return EINVAL;
+}
+
+/* Parse a JSON value of any type. */
+static int
+parse_value(struct decode_ctx *ctx, k5_json_value *val_out)
+{
+ k5_json_null null;
+ k5_json_bool bval;
+ k5_json_number num;
+ k5_json_string str;
+ k5_json_object obj;
+ k5_json_array array;
+ char *cstring;
+ int ret;
+
+ *val_out = NULL;
+
+ if (white_spaces(ctx))
+ return EINVAL;
+
+ if (*ctx->p == '"') {
+ ret = parse_string(ctx, &cstring);
+ if (ret)
+ return ret;
+ ret = k5_json_string_create(cstring, &str);
+ free(cstring);
+ if (ret)
+ return ret;
+ *val_out = str;
+ } else if (*ctx->p == '{') {
+ if (ctx->depth-- == 1)
+ return EINVAL;
+ ret = parse_object(ctx, &obj);
+ if (ret)
+ return ret;
+ ctx->depth++;
+ *val_out = obj;
+ } else if (*ctx->p == '[') {
+ if (ctx->depth-- == 1)
+ return EINVAL;
+ ret = parse_array(ctx, &array);
+ ctx->depth++;
+ *val_out = array;
+ } else if (is_digit(*ctx->p) || *ctx->p == '-') {
+ ret = parse_number(ctx, &num);
+ if (ret)
+ return ret;
+ *val_out = num;
+ } else if (strncmp((char *)ctx->p, "null", 4) == 0) {
+ ctx->p += 4;
+ ret = k5_json_null_create(&null);
+ if (ret)
+ return ret;
+ *val_out = null;
+ } else if (strncmp((char *)ctx->p, "true", 4) == 0) {
+ ctx->p += 4;
+ ret = k5_json_bool_create(1, &bval);
+ if (ret)
+ return ret;
+ *val_out = bval;
+ } else if (strncmp((char *)ctx->p, "false", 5) == 0) {
+ ctx->p += 5;
+ ret = k5_json_bool_create(0, &bval);
+ if (ret)
+ return ret;
+ *val_out = bval;
+ } else {
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+int
+k5_json_decode(const char *string, k5_json_value *val_out)
+{
+ struct decode_ctx ctx;
+ k5_json_value val;
+ int ret;
+
+ *val_out = NULL;
+ ctx.p = (unsigned char *)string;
+ ctx.depth = MAX_DECODE_DEPTH;
+ ret = parse_value(&ctx, &val);
+ if (ret)
+ return ret;
+ if (white_spaces(&ctx) == 0) {
+ k5_json_release(val);
+ return EINVAL;
+ }
+ *val_out = val;
+ return 0;
+}
diff --git a/src/util/support/k5buf.c b/src/util/support/k5buf.c
new file mode 100644
index 000000000000..f619f6a489ba
--- /dev/null
+++ b/src/util/support/k5buf.c
@@ -0,0 +1,243 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/k5buf.c - string buffer functions */
+
+/*
+ * Copyright 2008 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Can't include krb5.h here, or k5-int.h which includes it, because krb5.h
+ * needs to be generated with error tables, after util/et, which builds after
+ * this directory.
+ */
+#include "k5-platform.h"
+#include "k5-buf.h"
+#include <assert.h>
+
+/*
+ * Structure invariants:
+ *
+ * buftype is K5BUF_FIXED, K5BUF_DYNAMIC, or K5BUF_ERROR
+ * if buftype is K5BUF_ERROR, the other fields are NULL or 0
+ * if buftype is not K5BUF_ERROR:
+ * space > 0
+ * len < space
+ * data[len] = '\0'
+ */
+
+/* Return a character pointer to the current end of buf. */
+static inline char *
+endptr(struct k5buf *buf)
+{
+ return (char *)buf->data + buf->len;
+}
+
+static inline void
+set_error(struct k5buf *buf)
+{
+ buf->buftype = K5BUF_ERROR;
+ buf->data = NULL;
+ buf->space = buf->len = 0;
+}
+
+/*
+ * Make sure there is room for LEN more characters in BUF, in addition to the
+ * null terminator and what's already in there. Return true on success. On
+ * failure, set the error flag and return false.
+ */
+static int
+ensure_space(struct k5buf *buf, size_t len)
+{
+ size_t new_space;
+ char *new_data;
+
+ if (buf->buftype == K5BUF_ERROR)
+ return 0;
+ if (buf->space - 1 - buf->len >= len) /* Enough room already. */
+ return 1;
+ if (buf->buftype == K5BUF_FIXED) /* Can't resize a fixed buffer. */
+ goto error_exit;
+ assert(buf->buftype == K5BUF_DYNAMIC);
+ new_space = buf->space * 2;
+ while (new_space - buf->len - 1 < len) {
+ if (new_space > SIZE_MAX / 2)
+ goto error_exit;
+ new_space *= 2;
+ }
+ new_data = realloc(buf->data, new_space);
+ if (new_data == NULL)
+ goto error_exit;
+ buf->data = new_data;
+ buf->space = new_space;
+ return 1;
+
+error_exit:
+ if (buf->buftype == K5BUF_DYNAMIC)
+ free(buf->data);
+ set_error(buf);
+ return 0;
+}
+
+void
+k5_buf_init_fixed(struct k5buf *buf, char *data, size_t space)
+{
+ assert(space > 0);
+ buf->buftype = K5BUF_FIXED;
+ buf->data = data;
+ buf->space = space;
+ buf->len = 0;
+ *endptr(buf) = '\0';
+}
+
+void
+k5_buf_init_dynamic(struct k5buf *buf)
+{
+ buf->buftype = K5BUF_DYNAMIC;
+ buf->space = 128;
+ buf->data = malloc(buf->space);
+ if (buf->data == NULL) {
+ set_error(buf);
+ return;
+ }
+ buf->len = 0;
+ *endptr(buf) = '\0';
+}
+
+void
+k5_buf_add(struct k5buf *buf, const char *data)
+{
+ k5_buf_add_len(buf, data, strlen(data));
+}
+
+void
+k5_buf_add_len(struct k5buf *buf, const void *data, size_t len)
+{
+ if (!ensure_space(buf, len))
+ return;
+ if (len > 0)
+ memcpy(endptr(buf), data, len);
+ buf->len += len;
+ *endptr(buf) = '\0';
+}
+
+void
+k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
+{
+ va_list ap;
+ int r;
+ size_t remaining;
+ char *tmp;
+
+ if (buf->buftype == K5BUF_ERROR)
+ return;
+ remaining = buf->space - buf->len;
+
+ if (buf->buftype == K5BUF_FIXED) {
+ /* Format the data directly into the fixed buffer. */
+ va_start(ap, fmt);
+ r = vsnprintf(endptr(buf), remaining, fmt, ap);
+ va_end(ap);
+ if (SNPRINTF_OVERFLOW(r, remaining))
+ set_error(buf);
+ else
+ buf->len += (unsigned int) r;
+ return;
+ }
+
+ /* Optimistically format the data directly into the dynamic buffer. */
+ assert(buf->buftype == K5BUF_DYNAMIC);
+ va_start(ap, fmt);
+ r = vsnprintf(endptr(buf), remaining, fmt, ap);
+ va_end(ap);
+ if (!SNPRINTF_OVERFLOW(r, remaining)) {
+ buf->len += (unsigned int) r;
+ return;
+ }
+
+ if (r >= 0) {
+ /* snprintf correctly told us how much space is required. */
+ if (!ensure_space(buf, r))
+ return;
+ remaining = buf->space - buf->len;
+ va_start(ap, fmt);
+ r = vsnprintf(endptr(buf), remaining, fmt, ap);
+ va_end(ap);
+ if (SNPRINTF_OVERFLOW(r, remaining)) /* Shouldn't ever happen. */
+ k5_buf_free(buf);
+ else
+ buf->len += (unsigned int) r;
+ return;
+ }
+
+ /* It's a pre-C99 snprintf implementation, or something else went wrong.
+ * Fall back to asprintf. */
+ va_start(ap, fmt);
+ r = vasprintf(&tmp, fmt, ap);
+ va_end(ap);
+ if (r < 0) {
+ k5_buf_free(buf);
+ return;
+ }
+ if (ensure_space(buf, r)) {
+ /* Copy the temporary string into buf, including terminator. */
+ memcpy(endptr(buf), tmp, r + 1);
+ buf->len += r;
+ }
+ free(tmp);
+}
+
+void *
+k5_buf_get_space(struct k5buf *buf, size_t len)
+{
+ if (!ensure_space(buf, len))
+ return NULL;
+ buf->len += len;
+ *endptr(buf) = '\0';
+ return endptr(buf) - len;
+}
+
+void
+k5_buf_truncate(struct k5buf *buf, size_t len)
+{
+ if (buf->buftype == K5BUF_ERROR)
+ return;
+ assert(len <= buf->len);
+ buf->len = len;
+ *endptr(buf) = '\0';
+}
+
+int
+k5_buf_status(struct k5buf *buf)
+{
+ return (buf->buftype == K5BUF_ERROR) ? ENOMEM : 0;
+}
+
+void
+k5_buf_free(struct k5buf *buf)
+{
+ if (buf->buftype == K5BUF_ERROR)
+ return;
+ assert(buf->buftype == K5BUF_DYNAMIC);
+ free(buf->data);
+ set_error(buf);
+}
diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports
new file mode 100644
index 000000000000..d5d4177b72dc
--- /dev/null
+++ b/src/util/support/libkrb5support-fixed.exports
@@ -0,0 +1,88 @@
+k5_base64_decode
+k5_base64_encode
+k5_bcmp
+k5_buf_init_fixed
+k5_buf_init_dynamic
+k5_buf_add
+k5_buf_add_len
+k5_buf_add_fmt
+k5_buf_get_space
+k5_buf_truncate
+k5_buf_status
+k5_buf_free
+k5_set_error
+k5_vset_error
+k5_get_error
+k5_free_error
+k5_clear_error
+k5_set_error_info_callout_fn
+k5_json_array_add
+k5_json_array_create
+k5_json_array_fmt
+k5_json_array_get
+k5_json_array_length
+k5_json_array_set
+k5_json_bool_create
+k5_json_bool_value
+k5_json_decode
+k5_json_encode
+k5_json_get_tid
+k5_json_null_create
+k5_json_null_create_val
+k5_json_number_create
+k5_json_number_value
+k5_json_object_count
+k5_json_object_create
+k5_json_object_get
+k5_json_object_iterate
+k5_json_object_set
+k5_json_release
+k5_json_retain
+k5_json_string_create
+k5_json_string_create_base64
+k5_json_string_create_len
+k5_json_string_unbase64
+k5_json_string_utf8
+k5_os_mutex_init
+k5_os_mutex_destroy
+k5_os_mutex_lock
+k5_os_mutex_unlock
+k5_once
+k5_path_isabs
+k5_path_join
+k5_path_split
+k5_strerror_r
+krb5int_key_register
+krb5int_key_delete
+krb5int_getspecific
+krb5int_setspecific
+krb5int_getaddrinfo
+krb5int_freeaddrinfo
+krb5int_gai_strerror
+krb5int_getnameinfo
+krb5int_in6addr_any
+krb5int_pthread_loaded
+krb5int_open_plugin
+krb5int_close_plugin
+krb5int_get_plugin_data
+krb5int_get_plugin_func
+krb5int_open_plugin_dirs
+krb5int_close_plugin_dirs
+krb5int_get_plugin_dir_data
+krb5int_get_plugin_dir_func
+krb5int_free_plugin_dir_data
+krb5int_free_plugin_dir_func
+krb5int_mutex_alloc
+krb5int_mutex_free
+krb5int_mutex_lock
+krb5int_mutex_unlock
+krb5int_gmt_mktime
+krb5int_utf8cs_to_ucs2les
+krb5int_utf8s_to_ucs2les
+krb5int_ucs2lecs_to_utf8s
+krb5int_ucs4_to_utf8
+krb5int_utf8_to_ucs4
+krb5int_utf8_lentab
+krb5int_utf8_mintab
+krb5int_utf8_next
+krb5int_zap
diff --git a/src/util/support/mkstemp.c b/src/util/support/mkstemp.c
new file mode 100644
index 000000000000..9ef586a0975e
--- /dev/null
+++ b/src/util/support/mkstemp.c
@@ -0,0 +1,138 @@
+/* -*- mode: c; c-file-style: "bsd"; indent-tabs-mode: t -*- */
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "k5-platform.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if !defined S_ISDIR
+#if defined S_IFMT
+#define S_ISDIR(MODE) (((MODE) & S_IFMT) == S_IFDIR)
+#elif defined _S_IFMT
+#define S_ISDIR(MODE) (((MODE) & _S_IFMT) == _S_IFDIR)
+#else
+/* Hope that there's a S_ISDIR function defined. */
+#endif
+#endif
+
+static int _gettemp(char *, int *);
+
+int mkstemp(path)
+ char *path;
+{
+ int fd;
+
+ return (_gettemp(path, &fd) ? fd : -1);
+}
+
+static int
+_gettemp(path, doopen)
+ char *path;
+ register int *doopen;
+{
+ register char *start, *trv;
+ struct stat sbuf;
+ u_int pid;
+
+ pid = getpid();
+ for (trv = path; *trv; ++trv); /* extra X's get set to 0's */
+ while (*--trv == 'X') {
+ *trv = (pid % 10) + '0';
+ pid /= 10;
+ }
+
+ /*
+ * check the target directory; if you have six X's and it
+ * doesn't exist this runs for a *very* long time.
+ */
+ for (start = trv + 1;; --trv) {
+ if (trv <= path)
+ break;
+ if (*trv == '/') {
+ *trv = '\0';
+ if (stat(path, &sbuf))
+ return(0);
+ if (!S_ISDIR(sbuf.st_mode)) {
+ errno = ENOTDIR;
+ return(0);
+ }
+ *trv = '/';
+ break;
+ }
+ }
+
+ for (;;) {
+ if (doopen) {
+ if ((*doopen =
+ open(path, O_CREAT|O_EXCL|O_RDWR|O_BINARY, 0600)) >= 0)
+ return(1);
+ if (errno != EEXIST)
+ return(0);
+ }
+ else if (stat(path, &sbuf))
+ return(errno == ENOENT ? 1 : 0);
+
+ /* tricky little algorithm for backward compatibility */
+ for (trv = start;;) {
+ if (!*trv)
+ return(0);
+ if (*trv == 'z')
+ *trv++ = 'a';
+ else {
+ if (isdigit(*trv))
+ *trv = 'a';
+ else
+ ++*trv;
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
diff --git a/src/util/support/path.c b/src/util/support/path.c
new file mode 100644
index 000000000000..7a051abc9e12
--- /dev/null
+++ b/src/util/support/path.c
@@ -0,0 +1,161 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/path.c - Portable path manipulation functions */
+/*
+ * Copyright (C) 2011 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <k5-platform.h>
+
+/* For testing purposes, use a different symbol for Windows path semantics. */
+#ifdef _WIN32
+#define WINDOWS_PATHS
+#endif
+
+/*
+ * This file implements a limited set of portable path manipulation functions.
+ * When in doubt about edge cases, we follow the Python os.path semantics.
+ */
+
+#ifdef WINDOWS_PATHS
+#define SEP '\\'
+#define IS_SEPARATOR(c) ((c) == '\\' || (c) == '/')
+#else
+#define SEP '/'
+#define IS_SEPARATOR(c) ((c) == '/')
+#endif
+
+/* Find the rightmost path separator in path, or NULL if there is none. */
+static inline const char *
+find_sep(const char *path)
+{
+#ifdef WINDOWS_PATHS
+ const char *slash, *backslash;
+
+ slash = strrchr(path, '/');
+ backslash = strrchr(path, '\\');
+ if (slash != NULL && backslash != NULL)
+ return (slash > backslash) ? slash : backslash;
+ else
+ return (slash != NULL) ? slash : backslash;
+#else
+ return strrchr(path, '/');
+#endif
+}
+
+/* XXX drive letter prefixes */
+long
+k5_path_split(const char *path, char **parent_out, char **basename_out)
+{
+ const char *pathstart, *sep, *pend, *bstart;
+ char *parent = NULL, *basename = NULL;
+
+ if (parent_out != NULL)
+ *parent_out = NULL;
+ if (basename_out != NULL)
+ *basename_out = NULL;
+
+ pathstart = path;
+#ifdef WINDOWS_PATHS
+ if (*path != '\0' && path[1] == ':')
+ pathstart = path + 2;
+#endif
+
+ sep = find_sep(pathstart);
+ if (sep != NULL) {
+ bstart = sep + 1;
+ /* Strip off excess separators before the one we found. */
+ pend = sep;
+ while (pend > pathstart && IS_SEPARATOR(pend[-1]))
+ pend--;
+ /* But if we hit the start, keep the whole separator sequence. */
+ if (pend == pathstart)
+ pend = sep + 1;
+ } else {
+ bstart = pathstart;
+ pend = pathstart;
+ }
+
+ if (parent_out) {
+ parent = malloc(pend - path + 1);
+ if (parent == NULL)
+ return ENOMEM;
+ memcpy(parent, path, pend - path);
+ parent[pend - path] = '\0';
+ }
+ if (basename_out) {
+ basename = strdup(bstart);
+ if (basename == NULL) {
+ free(parent);
+ return ENOMEM;
+ }
+ }
+
+ if (parent_out)
+ *parent_out = parent;
+ if (basename_out)
+ *basename_out = basename;
+ return 0;
+}
+
+long
+k5_path_join(const char *path1, const char *path2, char **path_out)
+{
+ char *path, c;
+ int ret;
+
+ *path_out = NULL;
+ if (k5_path_isabs(path2) || *path1 == '\0') {
+ /* Discard path1 and return a copy of path2. */
+ path = strdup(path2);
+ if (path == NULL)
+ return ENOMEM;
+ } else {
+ /*
+ * Compose path1 and path2, adding a separator if path1 is non-empty
+ * there's no separator between them already. (*path2 can be a
+ * separator in the weird case where it starts with /: or \: on
+ * Windows, and Python doesn't insert a separator in this case.)
+ */
+ c = path1[strlen(path1) - 1];
+ if (IS_SEPARATOR(c) || IS_SEPARATOR(*path2))
+ ret = asprintf(&path, "%s%s", path1, path2);
+ else
+ ret = asprintf(&path, "%s%c%s", path1, SEP, path2);
+ if (ret < 0)
+ return ENOMEM;
+ }
+ *path_out = path;
+ return 0;
+}
+
+int
+k5_path_isabs(const char *path)
+{
+#ifdef WINDOWS_PATHS
+ if (*path != '\0' && path[1] == ':')
+ path += 2;
+ return (*path == '/' || *path == '\\');
+#else
+ return (*path == '/');
+#endif
+}
diff --git a/src/util/support/plugins.c b/src/util/support/plugins.c
new file mode 100644
index 000000000000..b0bb2ada8755
--- /dev/null
+++ b/src/util/support/plugins.c
@@ -0,0 +1,787 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/plugins.c - Plugin module support functions */
+/*
+ * Copyright 2006, 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-platform.h"
+#include "k5-plugin.h"
+#if USE_DLOPEN
+#include <dlfcn.h>
+#endif
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if USE_DLOPEN
+#ifdef RTLD_GROUP
+#define GROUP RTLD_GROUP
+#else
+#define GROUP 0
+#endif
+#ifdef RTLD_NODELETE
+#define NODELETE RTLD_NODELETE
+#else
+#define NODELETE 0
+#endif
+#define PLUGIN_DLOPEN_FLAGS (RTLD_NOW | RTLD_LOCAL | GROUP | NODELETE)
+#endif
+
+#if USE_DLOPEN && USE_CFBUNDLE
+#include <CoreFoundation/CoreFoundation.h>
+
+/* Currently CoreFoundation only exists on the Mac so we just use
+ * pthreads directly to avoid creating empty function calls on other
+ * platforms. If a thread initializer ever gets created in the common
+ * plugin code, move this there */
+static pthread_mutex_t krb5int_bundle_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+#include <stdarg.h>
+static void Tprintf (const char *fmt, ...)
+{
+#ifdef DEBUG
+ va_list va;
+ va_start (va, fmt);
+ vfprintf (stderr, fmt, va);
+ va_end (va);
+#endif
+}
+
+struct plugin_file_handle {
+#if USE_DLOPEN
+ void *dlhandle;
+#endif
+#ifdef _WIN32
+ HMODULE hinstPlugin;
+#endif
+#if !defined (USE_DLOPEN) && !defined (_WIN32)
+ char dummy;
+#endif
+};
+
+#ifdef _WIN32
+struct dirent {
+ long d_ino; /* inode (always 1 in WIN32) */
+ off_t d_off; /* offset to this dirent */
+ unsigned short d_reclen; /* length of d_name */
+ char d_name[_MAX_FNAME+1]; /* filename (null terminated) */
+};
+
+typedef struct {
+ intptr_t handle; /* _findfirst/_findnext handle */
+ short offset; /* offset into directory */
+ short finished; /* 1 if there are not more files */
+ struct _finddata_t fileinfo;/* from _findfirst/_findnext */
+ char *dir; /* the dir we are reading */
+ struct dirent dent; /* the dirent to return */
+} DIR;
+
+DIR * opendir(const char *dir)
+{
+ DIR *dp;
+ char *filespec;
+ intptr_t handle;
+ int index;
+
+ filespec = malloc(strlen(dir) + 2 + 1);
+ strcpy(filespec, dir);
+ index = strlen(filespec) - 1;
+ if (index >= 0 && (filespec[index] == '/' || filespec[index] == '\\'))
+ filespec[index] = '\0';
+ strcat(filespec, "/*");
+
+ dp = (DIR *)malloc(sizeof(DIR));
+ dp->offset = 0;
+ dp->finished = 0;
+ dp->dir = strdup(dir);
+
+ if ((handle = _findfirst(filespec, &(dp->fileinfo))) < 0) {
+ if (errno == ENOENT)
+ dp->finished = 1;
+ else {
+ free(filespec);
+ free(dp->dir);
+ free(dp);
+ return NULL;
+ }
+ }
+
+ dp->handle = handle;
+ free(filespec);
+
+ return dp;
+}
+
+struct dirent * readdir(DIR *dp)
+{
+ if (!dp || dp->finished) return NULL;
+
+ if (dp->offset != 0) {
+ if (_findnext(dp->handle, &(dp->fileinfo)) < 0) {
+ dp->finished = 1;
+ return NULL;
+ }
+ }
+ dp->offset++;
+
+ strncpy(dp->dent.d_name, dp->fileinfo.name, _MAX_FNAME);
+ dp->dent.d_ino = 1;
+ dp->dent.d_reclen = (unsigned short)strlen(dp->dent.d_name);
+ dp->dent.d_off = dp->offset;
+
+ return &(dp->dent);
+}
+
+int closedir(DIR *dp)
+{
+ if (!dp) return 0;
+ _findclose(dp->handle);
+ free(dp->dir);
+ free(dp);
+
+ return 0;
+}
+#endif
+
+long KRB5_CALLCONV
+krb5int_open_plugin (const char *filepath, struct plugin_file_handle **h, struct errinfo *ep)
+{
+ long err = 0;
+ struct stat statbuf;
+ struct plugin_file_handle *htmp = NULL;
+ int got_plugin = 0;
+
+ if (!err) {
+ if (stat (filepath, &statbuf) < 0) {
+ err = errno;
+ Tprintf ("stat(%s): %s\n", filepath, strerror (err));
+ k5_set_error(ep, err, _("unable to find plugin [%s]: %s"),
+ filepath, strerror(err));
+ }
+ }
+
+ if (!err) {
+ htmp = calloc (1, sizeof (*htmp)); /* calloc initializes ptrs to NULL */
+ if (htmp == NULL) { err = ENOMEM; }
+ }
+
+#if USE_DLOPEN
+ if (!err && ((statbuf.st_mode & S_IFMT) == S_IFREG
+#if USE_CFBUNDLE
+ || (statbuf.st_mode & S_IFMT) == S_IFDIR
+#endif /* USE_CFBUNDLE */
+ )) {
+ void *handle = NULL;
+
+#if USE_CFBUNDLE
+ char executablepath[MAXPATHLEN];
+
+ if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
+ int lock_err = 0;
+ CFStringRef pluginString = NULL;
+ CFURLRef pluginURL = NULL;
+ CFBundleRef pluginBundle = NULL;
+ CFURLRef executableURL = NULL;
+
+ /* Lock around CoreFoundation calls since objects are refcounted
+ * and the refcounts are not thread-safe. Using pthreads directly
+ * because this code is Mac-specific */
+ lock_err = pthread_mutex_lock(&krb5int_bundle_mutex);
+ if (lock_err) { err = lock_err; }
+
+ if (!err) {
+ pluginString = CFStringCreateWithCString (kCFAllocatorDefault,
+ filepath,
+ kCFStringEncodingASCII);
+ if (pluginString == NULL) { err = ENOMEM; }
+ }
+
+ if (!err) {
+ pluginURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault,
+ pluginString,
+ kCFURLPOSIXPathStyle,
+ true);
+ if (pluginURL == NULL) { err = ENOMEM; }
+ }
+
+ if (!err) {
+ pluginBundle = CFBundleCreate (kCFAllocatorDefault, pluginURL);
+ if (pluginBundle == NULL) { err = ENOENT; } /* XXX need better error */
+ }
+
+ if (!err) {
+ executableURL = CFBundleCopyExecutableURL (pluginBundle);
+ if (executableURL == NULL) { err = ENOMEM; }
+ }
+
+ if (!err) {
+ if (!CFURLGetFileSystemRepresentation (executableURL,
+ true, /* absolute */
+ (UInt8 *)executablepath,
+ sizeof (executablepath))) {
+ err = ENOMEM;
+ }
+ }
+
+ if (!err) {
+ /* override the path the caller passed in */
+ filepath = executablepath;
+ }
+
+ if (executableURL != NULL) { CFRelease (executableURL); }
+ if (pluginBundle != NULL) { CFRelease (pluginBundle); }
+ if (pluginURL != NULL) { CFRelease (pluginURL); }
+ if (pluginString != NULL) { CFRelease (pluginString); }
+
+ /* unlock after CFRelease calls since they modify refcounts */
+ if (!lock_err) { pthread_mutex_unlock (&krb5int_bundle_mutex); }
+ }
+#endif /* USE_CFBUNDLE */
+
+ if (!err) {
+ handle = dlopen(filepath, PLUGIN_DLOPEN_FLAGS);
+ if (handle == NULL) {
+ const char *e = dlerror();
+ if (e == NULL)
+ e = _("unknown failure");
+ Tprintf ("dlopen(%s): %s\n", filepath, e);
+ err = ENOENT; /* XXX */
+ k5_set_error(ep, err, _("unable to load plugin [%s]: %s"),
+ filepath, e);
+ }
+ }
+
+ if (!err) {
+ got_plugin = 1;
+ htmp->dlhandle = handle;
+ handle = NULL;
+ }
+
+ if (handle != NULL) { dlclose (handle); }
+ }
+#endif /* USE_DLOPEN */
+
+#ifdef _WIN32
+ if (!err && (statbuf.st_mode & S_IFMT) == S_IFREG) {
+ HMODULE handle = NULL;
+
+ handle = LoadLibrary(filepath);
+ if (handle == NULL) {
+ Tprintf ("Unable to load dll: %s\n", filepath);
+ err = ENOENT; /* XXX */
+ k5_set_error(ep, err, _("unable to load DLL [%s]"), filepath);
+ }
+
+ if (!err) {
+ got_plugin = 1;
+ htmp->hinstPlugin = handle;
+ handle = NULL;
+ }
+
+ if (handle != NULL)
+ FreeLibrary(handle);
+ }
+#endif
+
+ if (!err && !got_plugin) {
+ err = ENOENT; /* no plugin or no way to load plugins */
+ k5_set_error(ep, err, _("plugin unavailable: %s"), strerror(err));
+ }
+
+ if (!err) {
+ *h = htmp;
+ htmp = NULL; /* h takes ownership */
+ }
+
+ free(htmp);
+
+ return err;
+}
+
+static long
+krb5int_get_plugin_sym (struct plugin_file_handle *h,
+ const char *csymname, int isfunc, void **ptr,
+ struct errinfo *ep)
+{
+ long err = 0;
+ void *sym = NULL;
+
+#if USE_DLOPEN
+ if (!err && !sym && (h->dlhandle != NULL)) {
+ /* XXX Do we need to add a leading "_" to the symbol name on any
+ modern platforms? */
+ sym = dlsym (h->dlhandle, csymname);
+ if (sym == NULL) {
+ const char *e = dlerror (); /* XXX copy and save away */
+ if (e == NULL)
+ e = "unknown failure";
+ Tprintf ("dlsym(%s): %s\n", csymname, e);
+ err = ENOENT; /* XXX */
+ k5_set_error(ep, err, "%s", e);
+ }
+ }
+#endif
+
+#ifdef _WIN32
+ LPVOID lpMsgBuf;
+ DWORD dw;
+
+ if (!err && !sym && (h->hinstPlugin != NULL)) {
+ sym = GetProcAddress(h->hinstPlugin, csymname);
+ if (sym == NULL) {
+ const char *e = "unable to get dll symbol"; /* XXX copy and save away */
+ Tprintf ("GetProcAddress(%s): %i\n", csymname, GetLastError());
+ err = ENOENT; /* XXX */
+ k5_set_error(ep, err, "%s", e);
+
+ dw = GetLastError();
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ dw,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0, NULL )) {
+
+ fprintf (stderr, "unable to get dll symbol, %s\n", (LPCTSTR)lpMsgBuf);
+ LocalFree(lpMsgBuf);
+ }
+ }
+ }
+#endif
+
+ if (!err && (sym == NULL)) {
+ err = ENOENT; /* unimplemented */
+ }
+
+ if (!err) {
+ *ptr = sym;
+ }
+
+ return err;
+}
+
+long KRB5_CALLCONV
+krb5int_get_plugin_data (struct plugin_file_handle *h, const char *csymname,
+ void **ptr, struct errinfo *ep)
+{
+ return krb5int_get_plugin_sym (h, csymname, 0, ptr, ep);
+}
+
+long KRB5_CALLCONV
+krb5int_get_plugin_func (struct plugin_file_handle *h, const char *csymname,
+ void (**ptr)(), struct errinfo *ep)
+{
+ void *dptr = NULL;
+ long err = krb5int_get_plugin_sym (h, csymname, 1, &dptr, ep);
+ if (!err) {
+ /* Cast function pointers to avoid code duplication */
+ *ptr = (void (*)()) dptr;
+ }
+ return err;
+}
+
+void KRB5_CALLCONV
+krb5int_close_plugin (struct plugin_file_handle *h)
+{
+#if USE_DLOPEN
+ if (h->dlhandle != NULL) { dlclose(h->dlhandle); }
+#endif
+#ifdef _WIN32
+ if (h->hinstPlugin != NULL) { FreeLibrary(h->hinstPlugin); }
+#endif
+ free (h);
+}
+
+/* autoconf docs suggest using this preference order */
+#if HAVE_DIRENT_H || USE_DIRENT_H
+#include <dirent.h>
+#define NAMELEN(D) strlen((D)->d_name)
+#else
+#ifndef _WIN32
+#define dirent direct
+#define NAMELEN(D) ((D)->d->namlen)
+#else
+#define NAMELEN(D) strlen((D)->d_name)
+#endif
+#if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+#elif HAVE_SYS_DIR_H
+# include <sys/dir.h>
+#elif HAVE_NDIR_H
+# include <ndir.h>
+#endif
+#endif
+
+static long
+krb5int_plugin_file_handle_array_init (struct plugin_file_handle ***harray)
+{
+ long err = 0;
+
+ *harray = calloc (1, sizeof (**harray)); /* calloc initializes to NULL */
+ if (*harray == NULL) { err = ENOMEM; }
+
+ return err;
+}
+
+static long
+krb5int_plugin_file_handle_array_add (struct plugin_file_handle ***harray, size_t *count,
+ struct plugin_file_handle *p)
+{
+ long err = 0;
+ struct plugin_file_handle **newharray = NULL;
+ size_t newcount = *count + 1;
+
+ newharray = realloc (*harray, ((newcount + 1) * sizeof (**harray))); /* +1 for NULL */
+ if (newharray == NULL) {
+ err = ENOMEM;
+ } else {
+ newharray[newcount - 1] = p;
+ newharray[newcount] = NULL;
+ *count = newcount;
+ *harray = newharray;
+ }
+
+ return err;
+}
+
+static void
+krb5int_plugin_file_handle_array_free (struct plugin_file_handle **harray)
+{
+ if (harray != NULL) {
+ int i;
+ for (i = 0; harray[i] != NULL; i++) {
+ krb5int_close_plugin (harray[i]);
+ }
+ free (harray);
+ }
+}
+
+#if TARGET_OS_MAC
+#define FILEEXTS { "", ".bundle", ".dylib", ".so", NULL }
+#elif defined(_WIN32)
+#define FILEEXTS { "", ".dll", NULL }
+#else
+#define FILEEXTS { "", ".so", NULL }
+#endif
+
+
+static void
+krb5int_free_plugin_filenames (char **filenames)
+{
+ if (filenames != NULL) {
+ int i;
+ for (i = 0; filenames[i] != NULL; i++) {
+ free (filenames[i]);
+ }
+ free (filenames);
+ }
+}
+
+
+static long
+krb5int_get_plugin_filenames (const char * const *filebases, char ***filenames)
+{
+ long err = 0;
+ static const char *const fileexts[] = FILEEXTS;
+ char **tempnames = NULL;
+ size_t bases_count = 0;
+ size_t exts_count = 0;
+ size_t i;
+
+ if (!filebases) { err = EINVAL; }
+ if (!filenames) { err = EINVAL; }
+
+ if (!err) {
+ for (i = 0; filebases[i]; i++) { bases_count++; }
+ for (i = 0; fileexts[i]; i++) { exts_count++; }
+ tempnames = calloc ((bases_count * exts_count)+1, sizeof (char *));
+ if (!tempnames) { err = ENOMEM; }
+ }
+
+ if (!err) {
+ size_t j;
+ for (i = 0; !err && filebases[i]; i++) {
+ for (j = 0; !err && fileexts[j]; j++) {
+ if (asprintf(&tempnames[(i*exts_count)+j], "%s%s",
+ filebases[i], fileexts[j]) < 0) {
+ tempnames[(i*exts_count)+j] = NULL;
+ err = ENOMEM;
+ }
+ }
+ }
+ tempnames[bases_count * exts_count] = NULL; /* NUL-terminate */
+ }
+
+ if (!err) {
+ *filenames = tempnames;
+ tempnames = NULL;
+ }
+
+ krb5int_free_plugin_filenames(tempnames);
+
+ return err;
+}
+
+
+/* Takes a NULL-terminated list of directories. If filebases is NULL, filebases is ignored
+ * all plugins in the directories are loaded. If filebases is a NULL-terminated array of names,
+ * only plugins in the directories with those name (plus any platform extension) are loaded. */
+
+long KRB5_CALLCONV
+krb5int_open_plugin_dirs (const char * const *dirnames,
+ const char * const *filebases,
+ struct plugin_dir_handle *dirhandle,
+ struct errinfo *ep)
+{
+ long err = 0;
+ struct plugin_file_handle **h = NULL;
+ size_t count = 0;
+ char **filenames = NULL;
+ int i;
+
+ if (!err) {
+ err = krb5int_plugin_file_handle_array_init (&h);
+ }
+
+ if (!err && (filebases != NULL)) {
+ err = krb5int_get_plugin_filenames (filebases, &filenames);
+ }
+
+ for (i = 0; !err && dirnames[i] != NULL; i++) {
+ if (filenames != NULL) {
+ /* load plugins with names from filenames from each directory */
+ int j;
+
+ for (j = 0; !err && filenames[j] != NULL; j++) {
+ struct plugin_file_handle *handle = NULL;
+ char *filepath = NULL;
+
+ if (!err) {
+ if (asprintf(&filepath, "%s/%s", dirnames[i], filenames[j]) < 0) {
+ filepath = NULL;
+ err = ENOMEM;
+ }
+ }
+
+ if (krb5int_open_plugin (filepath, &handle, ep) == 0) {
+ err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
+ if (!err) { handle = NULL; } /* h takes ownership */
+ }
+
+ free(filepath);
+ if (handle != NULL) { krb5int_close_plugin (handle); }
+ }
+ } else {
+ /* load all plugins in each directory */
+ DIR *dir = opendir (dirnames[i]);
+
+ while (dir != NULL && !err) {
+ struct dirent *d = NULL;
+ char *filepath = NULL;
+ struct plugin_file_handle *handle = NULL;
+
+ d = readdir (dir);
+ if (d == NULL) { break; }
+
+ if ((strcmp (d->d_name, ".") == 0) ||
+ (strcmp (d->d_name, "..") == 0)) {
+ continue;
+ }
+
+ if (!err) {
+ int len = NAMELEN (d);
+ if (asprintf(&filepath, "%s/%*s", dirnames[i], len, d->d_name) < 0) {
+ filepath = NULL;
+ err = ENOMEM;
+ }
+ }
+
+ if (!err) {
+ if (krb5int_open_plugin (filepath, &handle, ep) == 0) {
+ err = krb5int_plugin_file_handle_array_add (&h, &count, handle);
+ if (!err) { handle = NULL; } /* h takes ownership */
+ }
+ }
+
+ free(filepath);
+ if (handle != NULL) { krb5int_close_plugin (handle); }
+ }
+
+ if (dir != NULL) { closedir (dir); }
+ }
+ }
+
+ if (err == ENOENT) {
+ err = 0; /* ran out of plugins -- do nothing */
+ }
+
+ if (!err) {
+ dirhandle->files = h;
+ h = NULL; /* dirhandle->files takes ownership */
+ }
+
+ if (filenames != NULL) { krb5int_free_plugin_filenames (filenames); }
+ if (h != NULL) { krb5int_plugin_file_handle_array_free (h); }
+
+ return err;
+}
+
+void KRB5_CALLCONV
+krb5int_close_plugin_dirs (struct plugin_dir_handle *dirhandle)
+{
+ if (dirhandle->files != NULL) {
+ int i;
+ for (i = 0; dirhandle->files[i] != NULL; i++) {
+ krb5int_close_plugin (dirhandle->files[i]);
+ }
+ free (dirhandle->files);
+ dirhandle->files = NULL;
+ }
+}
+
+void KRB5_CALLCONV
+krb5int_free_plugin_dir_data (void **ptrs)
+{
+ /* Nothing special to be done per pointer. */
+ free(ptrs);
+}
+
+long KRB5_CALLCONV
+krb5int_get_plugin_dir_data (struct plugin_dir_handle *dirhandle,
+ const char *symname,
+ void ***ptrs,
+ struct errinfo *ep)
+{
+ long err = 0;
+ void **p = NULL;
+ size_t count = 0;
+
+ /* XXX Do we need to add a leading "_" to the symbol name on any
+ modern platforms? */
+
+ Tprintf("get_plugin_data_sym(%s)\n", symname);
+
+ if (!err) {
+ p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
+ if (p == NULL) { err = ENOMEM; }
+ }
+
+ if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
+ int i = 0;
+
+ for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
+ void *sym = NULL;
+
+ if (krb5int_get_plugin_data (dirhandle->files[i], symname, &sym, ep) == 0) {
+ void **newp = NULL;
+
+ count++;
+ newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
+ if (newp == NULL) {
+ err = ENOMEM;
+ } else {
+ p = newp;
+ p[count - 1] = sym;
+ p[count] = NULL;
+ }
+ }
+ }
+ }
+
+ if (!err) {
+ *ptrs = p;
+ p = NULL; /* ptrs takes ownership */
+ }
+
+ free(p);
+
+ return err;
+}
+
+void KRB5_CALLCONV
+krb5int_free_plugin_dir_func (void (**ptrs)(void))
+{
+ /* Nothing special to be done per pointer. */
+ free(ptrs);
+}
+
+long KRB5_CALLCONV
+krb5int_get_plugin_dir_func (struct plugin_dir_handle *dirhandle,
+ const char *symname,
+ void (***ptrs)(void),
+ struct errinfo *ep)
+{
+ long err = 0;
+ void (**p)() = NULL;
+ size_t count = 0;
+
+ /* XXX Do we need to add a leading "_" to the symbol name on any
+ modern platforms? */
+
+ Tprintf("get_plugin_data_sym(%s)\n", symname);
+
+ if (!err) {
+ p = calloc (1, sizeof (*p)); /* calloc initializes to NULL */
+ if (p == NULL) { err = ENOMEM; }
+ }
+
+ if (!err && (dirhandle != NULL) && (dirhandle->files != NULL)) {
+ int i = 0;
+
+ for (i = 0; !err && (dirhandle->files[i] != NULL); i++) {
+ void (*sym)() = NULL;
+
+ if (krb5int_get_plugin_func (dirhandle->files[i], symname, &sym, ep) == 0) {
+ void (**newp)() = NULL;
+
+ count++;
+ newp = realloc (p, ((count + 1) * sizeof (*p))); /* +1 for NULL */
+ if (newp == NULL) {
+ err = ENOMEM;
+ } else {
+ p = newp;
+ p[count - 1] = sym;
+ p[count] = NULL;
+ }
+ }
+ }
+ }
+
+ if (!err) {
+ *ptrs = p;
+ p = NULL; /* ptrs takes ownership */
+ }
+
+ free(p);
+
+ return err;
+}
diff --git a/src/util/support/printf.c b/src/util/support/printf.c
new file mode 100644
index 000000000000..8228a40045d2
--- /dev/null
+++ b/src/util/support/printf.c
@@ -0,0 +1,100 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/printf.c */
+/*
+ * Copyright 2003, 2004, 2005, 2007, 2008 Massachusetts Institute of
+ * Technology. All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/* Provide {,v}asprintf for platforms that don't have them. */
+
+#include "k5-platform.h"
+
+/* On error: BSD: Set *ret to NULL. GNU: *ret is undefined.
+
+ Since we want to be able to use the GNU version directly, we need
+ provide only the weaker guarantee in this version. */
+int
+krb5int_vasprintf(char **ret, const char *format, va_list ap)
+{
+ va_list ap2;
+ char *str = NULL, *nstr;
+ size_t len = 80;
+ int len2;
+
+ while (1) {
+ if (len >= INT_MAX || len == 0)
+ goto fail;
+ nstr = realloc(str, len);
+ if (nstr == NULL)
+ goto fail;
+ str = nstr;
+ va_copy(ap2, ap);
+ len2 = vsnprintf(str, len, format, ap2);
+ va_end(ap2);
+ /* ISO C vsnprintf returns the needed length. Some old
+ vsnprintf implementations return -1 on truncation. */
+ if (len2 < 0) {
+ /* Don't know how much space we need, just that we didn't
+ supply enough; get a bigger buffer and try again. */
+ if (len <= SIZE_MAX/2)
+ len *= 2;
+ else if (len < SIZE_MAX)
+ len = SIZE_MAX;
+ else
+ goto fail;
+ } else if ((unsigned int) len2 >= SIZE_MAX) {
+ /* Need more space than we can request. */
+ goto fail;
+ } else if ((size_t) len2 >= len) {
+ /* Need more space, but we know how much. */
+ len = (size_t) len2 + 1;
+ } else {
+ /* Success! */
+ break;
+ }
+ }
+ /* We might've allocated more than we need, if we're still using
+ the initial guess, or we got here by doubling. */
+ if ((size_t) len2 < len - 1) {
+ nstr = realloc(str, (size_t) len2 + 1);
+ if (nstr)
+ str = nstr;
+ }
+ *ret = str;
+ return len2;
+
+fail:
+ free(str);
+ return -1;
+}
+
+int
+krb5int_asprintf(char **ret, const char *format, ...)
+{
+ va_list ap;
+ int n;
+
+ va_start(ap, format);
+ n = krb5int_vasprintf(ret, format, ap);
+ va_end(ap);
+ return n;
+}
diff --git a/src/util/support/strerror_r.c b/src/util/support/strerror_r.c
new file mode 100644
index 000000000000..e1ca5651078d
--- /dev/null
+++ b/src/util/support/strerror_r.c
@@ -0,0 +1,96 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/strerror_r.c - strerror_r compatibility shim */
+/*
+ * Copyright (C) 2014 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 <k5-platform.h>
+#undef strerror_r
+
+#if defined(_WIN32)
+
+/* Implement strerror_r in terms of strerror_s. */
+int
+k5_strerror_r(int errnum, char *buf, size_t buflen)
+{
+ int st;
+
+ st = strerror_s(buf, buflen, errnum);
+ if (st != 0) {
+ errno = st;
+ return -1;
+ }
+}
+
+#elif !defined(HAVE_STRERROR_R)
+
+/* Implement strerror_r in terms of strerror (not thread-safe). */
+int
+k5_strerror_r(int errnum, char *buf, size_t buflen)
+{
+ if (strlcpy(buf, strerror(errnum), buflen) >= buflen) {
+ errno = ERANGE;
+ return -1;
+ }
+ return 0;
+}
+
+#elif defined(STRERROR_R_CHAR_P)
+
+/*
+ * Implement the POSIX strerror_r API in terms of the GNU strerror_r, which
+ * returns a pointer to either the caller buffer or a constant string. This is
+ * the default version on glibc systems when _GNU_SOURCE is defined.
+ */
+int
+k5_strerror_r(int errnum, char *buf, size_t buflen)
+{
+ const char *str;
+
+ str = strerror_r(errnum, buf, buflen);
+ if (str != buf) {
+ if (strlcpy(buf, str, buflen) >= buflen) {
+ errno = ERANGE;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+#else
+
+/* Define a stub in terms of the real strerror_r, just to simplify the library
+ * export list. This shouldn't get used. */
+int
+k5_strerror_r(int errnum, char *buf, size_t buflen)
+{
+ return strerror_r(errnum, buf, buflen);
+}
+
+#endif
diff --git a/src/util/support/strlcpy.c b/src/util/support/strlcpy.c
new file mode 100644
index 000000000000..26ac34effbd8
--- /dev/null
+++ b/src/util/support/strlcpy.c
@@ -0,0 +1,88 @@
+/* -*- mode: c; c-file-style: "bsd"; indent-tabs-mode: t -*- */
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+/* Provide strlcpy and strlcat for platforms that don't have them. */
+
+#include "k5-platform.h"
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+krb5int_strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+krb5int_strlcat(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
diff --git a/src/util/support/supp-int.h b/src/util/support/supp-int.h
new file mode 100644
index 000000000000..5cd030e478b4
--- /dev/null
+++ b/src/util/support/supp-int.h
@@ -0,0 +1,38 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/supp-int.h */
+/*
+ * Copyright (C) 2006 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ *
+ * Internal prototypes for the krb5support library
+ */
+#ifndef KRB5_SUPP_INT_H__
+#define KRB5_SUPP_INT_H__
+
+extern int krb5int_call_thread_support_init (void);
+
+extern int krb5int_err_init (void);
+
+#endif /* KRB5_SUPP_INT_H__ */
diff --git a/src/util/support/t_base64.c b/src/util/support/t_base64.c
new file mode 100644
index 000000000000..3b1fd458cc9f
--- /dev/null
+++ b/src/util/support/t_base64.c
@@ -0,0 +1,110 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/t_base64.c - base64 encoding and decoding tests */
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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.
+ */
+
+#include <k5-platform.h>
+#include <k5-base64.h>
+
+static struct test {
+ void *data;
+ size_t len;
+ const char *result;
+} tests[] = {
+ { "", 0 , "" },
+ { "1", 1, "MQ==" },
+ { "22", 2, "MjI=" },
+ { "333", 3, "MzMz" },
+ { "4444", 4, "NDQ0NA==" },
+ { "55555", 5, "NTU1NTU=" },
+ { "abc:def", 7, "YWJjOmRlZg==" },
+ { "f", 1, "Zg==" },
+ { "fo", 2, "Zm8=" },
+ { "foo", 3, "Zm9v" },
+ { "foob", 4, "Zm9vYg==" },
+ { "fooba", 5, "Zm9vYmE=" },
+ { "foobar", 6, "Zm9vYmFy" },
+ { NULL, 0, NULL }
+};
+
+static char *negative_tests[] = {
+ "M=M=",
+ "MM=M",
+ "MQ===",
+ "====",
+ "M===",
+ NULL
+};
+
+int
+main(int argc, char **argv)
+{
+ char *str, **ntest;
+ void *data;
+ int numerr = 0, numtest = 1;
+ const struct test *t;
+ size_t len;
+
+ for (t = tests; t->data != NULL; t++) {
+ str = k5_base64_encode(t->data, t->len);
+ if (strcmp(str, t->result) != 0) {
+ fprintf(stderr, "failed test %d: %s != %s\n", numtest,
+ str, t->result);
+ numerr++;
+ }
+ free(str);
+ data = k5_base64_decode(t->result, &len);
+ if (len != t->len) {
+ fprintf(stderr, "failed test %d: len %lu != %lu\n", numtest,
+ (unsigned long)len, (unsigned long)t->len);
+ numerr++;
+ } else if (memcmp(data, t->data, t->len) != 0) {
+ fprintf(stderr, "failed test %d: data\n", numtest);
+ numerr++;
+ }
+ free(data);
+ numtest++;
+ }
+
+ for (ntest = negative_tests; *ntest != NULL; ntest++) {
+ data = k5_base64_decode(*ntest, &len);
+ if (data != NULL || len != SIZE_MAX) {
+ fprintf(stderr, "failed test %d: successful decode: %s\n",
+ numtest, *ntest);
+ numerr++;
+ }
+ numtest++;
+ }
+
+ return numerr ? 1 : 0;
+}
diff --git a/src/util/support/t_json.c b/src/util/support/t_json.c
new file mode 100644
index 000000000000..1f229247b496
--- /dev/null
+++ b/src/util/support/t_json.c
@@ -0,0 +1,329 @@
+/* -*- 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_number v2;
+ k5_json_null v3;
+ k5_json_array a;
+ k5_json_value v;
+
+ k5_json_array_create(&a);
+ k5_json_string_create("abc", &v1);
+ k5_json_array_add(a, v1);
+ k5_json_number_create(2, &v2);
+ k5_json_array_add(a, v2);
+ k5_json_null_create(&v3);
+ k5_json_array_add(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_TID_NULL, "array[2] tid");
+ v = k5_json_array_get(a, 1);
+ check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "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_TID_STRING, "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(a);
+
+ k5_json_array_fmt(&a, "vnbiLssB", v3, 1, 9, (long long)-6, "def", NULL,
+ (void *)"ghij", (size_t)4);
+ v = k5_json_array_get(a, 0);
+ check(k5_json_get_tid(v) == K5_JSON_TID_NULL, "fmt array[0] tid");
+ v = k5_json_array_get(a, 1);
+ check(k5_json_get_tid(v) == K5_JSON_TID_NULL, "fmt array[1] tid");
+ v = k5_json_array_get(a, 2);
+ check(k5_json_get_tid(v) == K5_JSON_TID_BOOL, "fmt array[2] tid");
+ check(k5_json_bool_value(v), "fmt array[2] value");
+ v = k5_json_array_get(a, 3);
+ check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "fmt array[3] tid");
+ check(k5_json_number_value(v) == 9, "fmt array[3] value");
+ v = k5_json_array_get(a, 4);
+ check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "fmt array[4] tid");
+ check(k5_json_number_value(v) == -6, "fmt array[4] value");
+ v = k5_json_array_get(a, 5);
+ check(k5_json_get_tid(v) == K5_JSON_TID_STRING, "fmt array[5] tid");
+ check(strcmp(k5_json_string_utf8(v), "def") == 0, "fmt array[5] value");
+ v = k5_json_array_get(a, 6);
+ check(k5_json_get_tid(v) == K5_JSON_TID_NULL, "fmt array[6] tid");
+ v = k5_json_array_get(a, 7);
+ check(k5_json_get_tid(v) == K5_JSON_TID_STRING, "fmt array[7] tid");
+ check(strcmp(k5_json_string_utf8(v), "Z2hpag==") == 0,
+ "fmt array[7] value");
+ k5_json_release(v3);
+ k5_json_release(a);
+}
+
+static void
+test_object(void)
+{
+ k5_json_object object;
+ k5_json_number n, v1;
+ k5_json_string s, v2;
+
+ k5_json_object_create(&object);
+ k5_json_number_create(1, &v1);
+ k5_json_object_set(object, "key1", v1);
+ k5_json_string_create("hejsan", &v2);
+ 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");
+
+ check(k5_json_object_get(object, "key3") == NULL,
+ "object nonexistent key");
+
+ k5_json_object_set(object, "key1", NULL);
+ check(k5_json_object_get(object, "key1") == NULL,
+ "object removed key");
+ check(k5_json_object_get(object, "key2") != NULL,
+ "object remaining key");
+
+ k5_json_release(v1);
+ k5_json_release(v2);
+ k5_json_release(object);
+}
+
+static void
+test_string(void)
+{
+ k5_json_string s1, s2, s3;
+ unsigned char *data;
+ size_t len;
+
+ k5_json_string_create("hejsan", &s1);
+ k5_json_string_create("hejsan", &s2);
+ k5_json_string_create_base64("55555", 5, &s3);
+
+ if (strcmp(k5_json_string_utf8(s1), k5_json_string_utf8(s2)) != 0)
+ err("Identical strings are not identical");
+ if (strcmp(k5_json_string_utf8(s3), "NTU1NTU=") != 0)
+ err("base64 string has incorrect value");
+ k5_json_string_unbase64(s3, &data, &len);
+ if (len != 5 || memcmp(data, "55555", 5) != 0)
+ err("base64 string doesn't decode to correct value");
+ free(data);
+
+ k5_json_release(s1);
+ k5_json_release(s2);
+ k5_json_release(s3);
+}
+
+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;
+
+ check(k5_json_decode("\"string\"", &v) == 0, "string1");
+ check(k5_json_get_tid(v) == K5_JSON_TID_STRING, "string1 tid");
+ check(strcmp(k5_json_string_utf8(v), "string") == 0, "string1 utf8");
+ k5_json_release(v);
+
+ check(k5_json_decode("\t \"foo\\\"bar\" ", &v) == 0, "string2");
+ check(k5_json_get_tid(v) == K5_JSON_TID_STRING, "string2 tid");
+ check(strcmp(k5_json_string_utf8(v), "foo\"bar") == 0, "string2 utf8");
+ k5_json_release(v);
+
+ check(k5_json_decode(" { \"key\" : \"value\" }", &v) == 0, "object1");
+ check(k5_json_get_tid(v) == K5_JSON_TID_OBJECT, "object1 tid");
+ v2 = k5_json_object_get(v, "key");
+ check(v2 != NULL, "object[key]");
+ check(k5_json_get_tid(v2) == K5_JSON_TID_STRING, "object1[key] tid");
+ check(strcmp(k5_json_string_utf8(v2), "value") == 0, "object1[key] utf8");
+ k5_json_release(v);
+
+ check(k5_json_decode("{ \"k1\" : { \"k2\" : \"s2\", \"k3\" : \"s3\" }, "
+ "\"k4\" : \"s4\" }", &v) == 0, "object2");
+ v2 = k5_json_object_get(v, "k1");
+ check(v2 != NULL, "object2[k1]");
+ check(k5_json_get_tid(v2) == K5_JSON_TID_OBJECT, "object2[k1] tid");
+ v2 = k5_json_object_get(v2, "k3");
+ check(v2 != NULL, "object2[k1][k3]");
+ check(k5_json_get_tid(v2) == K5_JSON_TID_STRING, "object2[k1][k3] tid");
+ check(strcmp(k5_json_string_utf8(v2), "s3") == 0, "object2[k1][k3] utf8");
+ k5_json_release(v);
+
+ check(k5_json_decode("{ \"k1\" : 1 }", &v) == 0, "object3");
+ check(k5_json_get_tid(v) == K5_JSON_TID_OBJECT, "object3 id");
+ v2 = k5_json_object_get(v, "k1");
+ check(k5_json_get_tid(v2) == K5_JSON_TID_NUMBER, "object3[k1] tid");
+ check(k5_json_number_value(v2) == 1, "object3[k1] value");
+ k5_json_release(v);
+
+ check(k5_json_decode("-10", &v) == 0, "number1");
+ check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "number1 tid");
+ check(k5_json_number_value(v) == -10, "number1 value");
+ k5_json_release(v);
+
+ check(k5_json_decode("99", &v) == 0, "number2");
+ check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "number2 tid");
+ check(k5_json_number_value(v) == 99, "number2 value");
+ k5_json_release(v);
+
+ check(k5_json_decode(" [ 1 ]", &v) == 0, "array1");
+ check(k5_json_get_tid(v) == K5_JSON_TID_ARRAY, "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_TID_NUMBER, "array1[0] tid");
+ check(k5_json_number_value(v2) == 1, "array1[0] value");
+ k5_json_release(v);
+
+ check(k5_json_decode(" [ -1 ]", &v) == 0, "array2");
+ check(k5_json_get_tid(v) == K5_JSON_TID_ARRAY, "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_TID_NUMBER, "array2[0] tid");
+ check(k5_json_number_value(v2) == -1, "array2[0] value");
+ k5_json_release(v);
+
+ check(k5_json_decode("18446744073709551616", &v) == EOVERFLOW,
+ "unsigned 64-bit overflow");
+ check(k5_json_decode("9223372036854775808", &v) == EOVERFLOW,
+ "signed 64-bit positive overflow");
+ check(k5_json_decode("-9223372036854775809", &v) == EOVERFLOW,
+ "signed 64-bit negative overflow");
+
+ for (tptr = tests; *tptr != NULL; tptr++) {
+ s = strdup(*tptr);
+ if (k5_json_decode(s, &v))
+ err(s);
+ if (k5_json_encode(v, &enc) || strcmp(enc, s) != 0)
+ err(s);
+ free(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_decode(s, &v);
+ k5_json_release(v);
+ }
+ *p = orig;
+ }
+ free(s);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ test_array();
+ test_object();
+ test_string();
+ test_json();
+ return 0;
+}
diff --git a/src/util/support/t_k5buf.c b/src/util/support/t_k5buf.c
new file mode 100644
index 000000000000..ba86851dc47b
--- /dev/null
+++ b/src/util/support/t_k5buf.c
@@ -0,0 +1,260 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/t_k5buf.c - Test the k5buf string buffer module */
+/*
+ * Copyright 2008 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-platform.h"
+#include "k5-buf.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+static void
+fail_if(int condition, const char *name)
+{
+ if (condition) {
+ fprintf(stderr, "%s failed\n", name);
+ exit(1);
+ }
+}
+
+/* Test the invariants of a buffer. */
+static void
+check_buf(struct k5buf *buf, const char *name)
+{
+ fail_if(buf->buftype != K5BUF_FIXED && buf->buftype != K5BUF_DYNAMIC &&
+ buf->buftype != K5BUF_ERROR, name);
+ if (buf->buftype == K5BUF_ERROR) {
+ fail_if(buf->data != NULL, name);
+ fail_if(buf->space != 0 || buf->len != 0, name);
+ } else {
+ fail_if(buf->space == 0, name);
+ fail_if(buf->len >= buf->space, name);
+ fail_if(((char *)buf->data)[buf->len] != 0, name);
+ }
+}
+
+static void
+test_basic()
+{
+ struct k5buf buf;
+ char storage[1024];
+
+ k5_buf_init_fixed(&buf, storage, sizeof(storage));
+ k5_buf_add(&buf, "Hello ");
+ k5_buf_add_len(&buf, "world", 5);
+ check_buf(&buf, "basic fixed");
+ fail_if(buf.data == NULL || buf.len != 11, "basic fixed");
+ fail_if(strcmp(buf.data, "Hello world") != 0, "basic fixed");
+
+ k5_buf_init_dynamic(&buf);
+ k5_buf_add_len(&buf, "Hello", 5);
+ k5_buf_add(&buf, " world");
+ check_buf(&buf, "basic dynamic");
+ fail_if(buf.data == NULL || buf.len != 11, "basic dynamic");
+ fail_if(strcmp(buf.data, "Hello world") != 0, "basic dynamic");
+ k5_buf_free(&buf);
+}
+
+static void
+test_realloc()
+{
+ struct k5buf buf;
+ char data[1024];
+ size_t i;
+
+ for (i = 0; i < sizeof(data); i++)
+ data[i] = 'a';
+
+ /* Cause the buffer size to double from 128 to 256 bytes. */
+ k5_buf_init_dynamic(&buf);
+ k5_buf_add_len(&buf, data, 10);
+ k5_buf_add_len(&buf, data, 128);
+ fail_if(buf.space != 256, "realloc 1");
+ check_buf(&buf, "realloc 1");
+ fail_if(buf.data == NULL || buf.len != 138, "realloc 1");
+ fail_if(memcmp(buf.data, data, buf.len) != 0, "realloc 1");
+
+ /* Cause the same buffer to double in size to 512 bytes. */
+ k5_buf_add_len(&buf, data, 128);
+ fail_if(buf.space != 512, "realloc 2");
+ check_buf(&buf, "realloc 2");
+ fail_if(buf.data == NULL || buf.len != 266, "realloc 2");
+ fail_if(memcmp(buf.data, data, buf.len) != 0, "realloc 2");
+ k5_buf_free(&buf);
+
+ /* Cause a buffer to increase from 128 to 512 bytes directly. */
+ k5_buf_init_dynamic(&buf);
+ k5_buf_add_len(&buf, data, 10);
+ k5_buf_add_len(&buf, data, 256);
+ fail_if(buf.space != 512, "realloc 3");
+ check_buf(&buf, "realloc 3");
+ fail_if(buf.data == NULL || buf.len != 266, "realloc 3");
+ fail_if(memcmp(buf.data, data, buf.len) != 0, "realloc 3");
+ k5_buf_free(&buf);
+
+ /* Cause a buffer to increase from 128 to 1024 bytes directly. */
+ k5_buf_init_dynamic(&buf);
+ k5_buf_add_len(&buf, data, 10);
+ k5_buf_add_len(&buf, data, 512);
+ fail_if(buf.space != 1024, "realloc 4");
+ check_buf(&buf, "realloc 4");
+ fail_if(buf.data == NULL || buf.len != 522, "realloc 4");
+ fail_if(memcmp(buf.data, data, buf.len) != 0, "realloc 4");
+ k5_buf_free(&buf);
+
+ /* Cause a reallocation to fail by integer overflow. */
+ k5_buf_init_dynamic(&buf);
+ k5_buf_add_len(&buf, data, 100);
+ k5_buf_add_len(&buf, NULL, SIZE_MAX);
+ check_buf(&buf, "realloc 5");
+ fail_if(buf.buftype != K5BUF_ERROR, "realloc 5");
+ k5_buf_free(&buf);
+}
+
+static void
+test_overflow()
+{
+ struct k5buf buf;
+ char storage[10];
+
+ /* Cause a fixed-sized buffer overflow. */
+ k5_buf_init_fixed(&buf, storage, sizeof(storage));
+ k5_buf_add(&buf, "12345");
+ k5_buf_add(&buf, "12345");
+ check_buf(&buf, "overflow 1");
+ fail_if(buf.buftype != K5BUF_ERROR, "overflow 1");
+
+ /* Cause a fixed-sized buffer overflow with integer overflow. */
+ k5_buf_init_fixed(&buf, storage, sizeof(storage));
+ k5_buf_add(&buf, "12345");
+ k5_buf_add_len(&buf, NULL, SIZE_MAX);
+ check_buf(&buf, "overflow 2");
+ fail_if(buf.buftype != K5BUF_ERROR, "overflow 2");
+}
+
+static void
+test_error()
+{
+ struct k5buf buf;
+ char storage[1];
+
+ /* Cause an overflow and then perform actions afterwards. */
+ k5_buf_init_fixed(&buf, storage, sizeof(storage));
+ k5_buf_add(&buf, "1");
+ fail_if(buf.buftype != K5BUF_ERROR, "error");
+ check_buf(&buf, "error");
+ k5_buf_add(&buf, "test");
+ check_buf(&buf, "error");
+ k5_buf_add_len(&buf, "test", 4);
+ check_buf(&buf, "error");
+ k5_buf_truncate(&buf, 3);
+ check_buf(&buf, "error");
+ fail_if(buf.buftype != K5BUF_ERROR, "error");
+}
+
+static void
+test_truncate()
+{
+ struct k5buf buf;
+
+ k5_buf_init_dynamic(&buf);
+ k5_buf_add(&buf, "abcde");
+ k5_buf_add(&buf, "fghij");
+ k5_buf_truncate(&buf, 7);
+ check_buf(&buf, "truncate");
+ fail_if(buf.data == NULL || buf.len != 7, "truncate");
+ fail_if(strcmp(buf.data, "abcdefg") != 0, "truncate");
+ k5_buf_free(&buf);
+}
+
+static void
+test_binary()
+{
+ struct k5buf buf;
+ char data[] = { 'a', 0, 'b' }, *s;
+
+ k5_buf_init_dynamic(&buf);
+ k5_buf_add_len(&buf, data, 3);
+ k5_buf_add_len(&buf, data, 3);
+ check_buf(&buf, "binary");
+ fail_if(buf.data == NULL || buf.len != 6, "binary");
+ s = buf.data;
+ fail_if(s[0] != 'a' || s[1] != 0 || s[2] != 'b', "binary");
+ fail_if(s[3] != 'a' || s[4] != 0 || s[5] != 'b', "binary");
+ k5_buf_free(&buf);
+}
+
+static void
+test_fmt()
+{
+ struct k5buf buf;
+ char storage[10], data[1024];
+ size_t i;
+
+ for (i = 0; i < sizeof(data) - 1; i++)
+ data[i] = 'a';
+ data[i] = '\0';
+
+ /* Format some text into a non-empty fixed buffer. */
+ k5_buf_init_fixed(&buf, storage, sizeof(storage));
+ k5_buf_add(&buf, "foo");
+ k5_buf_add_fmt(&buf, " %d ", 3);
+ check_buf(&buf, "fmt 1");
+ fail_if(buf.data == NULL || buf.len != 6, "fmt 1");
+ fail_if(strcmp(buf.data, "foo 3 ") != 0, "fmt 1");
+
+ /* Overflow the same buffer with formatted text. */
+ k5_buf_add_fmt(&buf, "%d%d%d%d", 1, 2, 3, 4);
+ check_buf(&buf, "fmt 2");
+ fail_if(buf.buftype != K5BUF_ERROR, "fmt 2");
+
+ /* Format some text into a non-empty dynamic buffer. */
+ k5_buf_init_dynamic(&buf);
+ k5_buf_add(&buf, "foo");
+ k5_buf_add_fmt(&buf, " %d ", 3);
+ check_buf(&buf, "fmt 3");
+ fail_if(buf.data == NULL || buf.len != 6, "fmt 3");
+ fail_if(strcmp(buf.data, "foo 3 ") != 0, "fmt 3");
+
+ /* Format more text into the same buffer, causing a big resize. */
+ k5_buf_add_fmt(&buf, "%s", data);
+ check_buf(&buf, "fmt 4");
+ fail_if(buf.space != 2048, "fmt 4");
+ fail_if(buf.data == NULL || buf.len != 1029, "fmt 4");
+ fail_if(strcmp((char *)buf.data + 6, data) != 0, "fmt 4");
+ k5_buf_free(&buf);
+}
+
+int
+main()
+{
+ test_basic();
+ test_realloc();
+ test_overflow();
+ test_error();
+ test_truncate();
+ test_binary();
+ test_fmt();
+ return 0;
+}
diff --git a/src/util/support/t_path.c b/src/util/support/t_path.c
new file mode 100644
index 000000000000..2ac91d8ec340
--- /dev/null
+++ b/src/util/support/t_path.c
@@ -0,0 +1,191 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/t_path.c - Path manipulation tests */
+/*
+ * Copyright (C) 2011 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <k5-platform.h>
+
+/* For testing purposes, use a different symbol for Windows path semantics. */
+#ifdef _WIN32
+#define WINDOWS_PATHS
+#endif
+
+/*
+ * The ultimate arbiter of these tests is the dirname, basename, and isabs
+ * methods of the Python posixpath and ntpath modules.
+ */
+
+struct {
+ const char *path;
+ const char *posix_dirname;
+ const char *posix_basename;
+ const char *win_dirname;
+ const char *win_basename;
+} split_tests[] = {
+ { "", "", "", "", "" },
+ { "a/b/c", "a/b", "c", "a/b", "c" },
+ { "a/b/", "a/b", "", "a/b", "" },
+ { "a\\b\\c", "", "a\\b\\c", "a\\b", "c" },
+ { "a\\b\\", "", "a\\b\\", "a\\b", "" },
+ { "a/b\\c", "a", "b\\c", "a/b", "c" },
+ { "a//b", "a", "b", "a", "b" },
+ { "a/\\/b", "a/\\", "b", "a", "b" },
+ { "a//b/c", "a//b", "c", "a//b", "c" },
+
+ { "/", "/", "", "/", "" },
+ { "\\", "", "\\", "\\", "" },
+ { "/a/b/c", "/a/b", "c", "/a/b", "c" },
+ { "\\a/b/c", "\\a/b", "c", "\\a/b", "c" },
+ { "/a", "/", "a", "/", "a" },
+ { "//a", "//", "a", "//", "a" },
+ { "\\//\\a", "\\", "\\a", "\\//\\", "a" },
+
+ { "/:", "/", ":", "/:", "" },
+ { "c:\\", "", "c:\\", "c:\\", "" },
+ { "c:/", "c:", "", "c:/", "" },
+ { "c:/\\a", "c:", "\\a", "c:/\\", "a" },
+ { "c:a", "", "c:a", "c:", "a" },
+};
+
+struct {
+ const char *path1;
+ const char *path2;
+ const char *posix_result;
+ const char *win_result;
+} join_tests[] = {
+ { "", "", "", "" },
+ { "", "a", "a", "a" },
+ { "", "/a", "/a", "/a" },
+ { "", "c:", "c:", "c:" },
+
+ { "a", "", "a/", "a\\" },
+ { "a/", "", "a/", "a/" },
+ { "a\\", "", "a\\/", "a\\" },
+ { "a/\\", "", "a/\\/", "a/\\" },
+
+ { "a", "b", "a/b", "a\\b" },
+ { "a", "/b", "/b", "/b" },
+ { "a", "c:", "a/c:", "a\\c:" },
+ { "a", "c:/", "a/c:/", "c:/" },
+ { "a", "c:/a", "a/c:/a", "c:/a" },
+ { "a", "/:", "/:", "a/:" },
+ { "a/", "b", "a/b", "a/b" },
+ { "a/", "", "a/", "a/" },
+ { "a\\", "b", "a\\/b", "a\\b" },
+
+ { "a//", "b", "a//b", "a//b" },
+ { "a/\\", "b", "a/\\/b", "a/\\b" },
+};
+
+struct {
+ const char *path;
+ int posix_result;
+ int win_result;
+} isabs_tests[] = {
+ { "", 0, 0 },
+ { "/", 1, 1 },
+ { "/a", 1, 1 },
+ { "a/b", 0, 0 },
+ { "\\", 0, 1 },
+ { "\\a", 0, 1 },
+ { "c:", 0, 0 },
+ { "/:", 1, 0 },
+ { "\\:", 0, 0 },
+ { "c:/a", 0, 1 },
+ { "c:\\a", 0, 1 },
+ { "c:a", 0, 0 },
+ { "c:a/b", 0, 0 },
+ { "/:a/b", 1, 0 },
+};
+
+int
+main(void)
+{
+ char *dirname, *basename, *joined;
+ const char *edirname, *ebasename, *ejoined, *ipath, *path1, *path2;
+ int result, eresult, status = 0;
+ size_t i;
+
+ for (i = 0; i < sizeof(split_tests) / sizeof(*split_tests); i++) {
+ ipath = split_tests[i].path;
+#ifdef WINDOWS_PATHS
+ edirname = split_tests[i].win_dirname;
+ ebasename = split_tests[i].win_basename;
+#else
+ edirname = split_tests[i].posix_dirname;
+ ebasename = split_tests[i].posix_basename;
+#endif
+ assert(k5_path_split(ipath, NULL, NULL) == 0);
+ assert(k5_path_split(ipath, &dirname, NULL) == 0);
+ free(dirname);
+ assert(k5_path_split(ipath, NULL, &basename) == 0);
+ free(basename);
+ assert(k5_path_split(ipath, &dirname, &basename) == 0);
+ if (strcmp(dirname, edirname) != 0) {
+ fprintf(stderr, "Split test %d: dirname %s != expected %s\n",
+ (int)i, dirname, edirname);
+ status = 1;
+ }
+ if (strcmp(basename, ebasename) != 0) {
+ fprintf(stderr, "Split test %d: basename %s != expected %s\n",
+ (int)i, basename, ebasename);
+ status = 1;
+ }
+ free(dirname);
+ free(basename);
+ }
+
+ for (i = 0; i < sizeof(join_tests) / sizeof(*join_tests); i++) {
+ path1 = join_tests[i].path1;
+ path2 = join_tests[i].path2;
+#ifdef WINDOWS_PATHS
+ ejoined = join_tests[i].win_result;
+#else
+ ejoined = join_tests[i].posix_result;
+#endif
+ assert(k5_path_join(path1, path2, &joined) == 0);
+ if (strcmp(joined, ejoined) != 0) {
+ fprintf(stderr, "Join test %d: %s != expected %s\n",
+ (int)i, joined, ejoined);
+ status = 1;
+ }
+ free(joined);
+ }
+
+ for (i = 0; i < sizeof(isabs_tests) / sizeof(*isabs_tests); i++) {
+#ifdef WINDOWS_PATHS
+ eresult = isabs_tests[i].win_result;
+#else
+ eresult = isabs_tests[i].posix_result;
+#endif
+ result = k5_path_isabs(isabs_tests[i].path);
+ if (result != eresult) {
+ fprintf(stderr, "isabs test %d: %d != expected %d\n",
+ (int)i, result, eresult);
+ status = 1;
+ }
+ }
+
+ return status;
+}
diff --git a/src/util/support/t_unal.c b/src/util/support/t_unal.c
new file mode 100644
index 000000000000..f67cd31edf5f
--- /dev/null
+++ b/src/util/support/t_unal.c
@@ -0,0 +1,46 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#undef NDEBUG
+#include "k5-platform.h"
+
+int main ()
+{
+ /* Test some low-level assumptions the Kerberos code depends
+ on. */
+
+ union {
+ uint64_t n64;
+ uint32_t n32;
+ uint16_t n16;
+ unsigned char b[9];
+ } u;
+ static unsigned char buf[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+
+ assert(load_64_be(buf+1) == 0x0102030405060708LL);
+ assert(load_64_le(buf+1) == 0x0807060504030201LL);
+ assert(load_32_le(buf+2) == 0x05040302);
+ assert(load_32_be(buf+2) == 0x02030405);
+ assert(load_16_be(buf+3) == 0x0304);
+ assert(load_16_le(buf+3) == 0x0403);
+ u.b[0] = 0;
+ assert((store_64_be(0x0102030405060708LL, u.b+1), !memcmp(buf, u.b, 9)));
+ u.b[1] = 9;
+ assert((store_64_le(0x0807060504030201LL, u.b+1), !memcmp(buf, u.b, 9)));
+ u.b[2] = 10;
+ assert((store_32_be(0x02030405, u.b+2), !memcmp(buf, u.b, 9)));
+ u.b[3] = 11;
+ assert((store_32_le(0x05040302, u.b+2), !memcmp(buf, u.b, 9)));
+ u.b[4] = 12;
+ assert((store_16_be(0x0304, u.b+3), !memcmp(buf, u.b, 9)));
+ u.b[4] = 13;
+ assert((store_16_le(0x0403, u.b+3), !memcmp(buf, u.b, 9)));
+ /* Verify that load_*_n properly does native format. Assume
+ the unaligned thing is okay. */
+ u.n64 = 0x090a0b0c0d0e0f00LL;
+ assert(load_64_n((unsigned char *) &u.n64) == 0x090a0b0c0d0e0f00LL);
+ u.n32 = 0x06070809;
+ assert(load_32_n((unsigned char *) &u.n32) == 0x06070809);
+ u.n16 = 0x0a0b;
+ assert(load_16_n((unsigned char *) &u.n16) == 0x0a0b);
+
+ return 0;
+}
diff --git a/src/util/support/t_utf8.c b/src/util/support/t_utf8.c
new file mode 100644
index 000000000000..583270165a60
--- /dev/null
+++ b/src/util/support/t_utf8.c
@@ -0,0 +1,209 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/t_utf8.c - test UTF-8 boundary conditions */
+/*
+ * Copyright (C) 2015 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 <stdio.h>
+#include <string.h>
+
+#include "k5-platform.h"
+#include "k5-utf8.h"
+
+/*
+ * Convenience macro to allow testing of old encodings.
+ *
+ * "Old" means ISO/IEC 10646 prior to 2011, when the highest valid code point
+ * was U+7FFFFFFF instead of U+10FFFF.
+ */
+#ifdef OLDENCODINGS
+#define L(x) (x)
+#else
+#define L(x) 0
+#endif
+
+/*
+ * len is 0 for invalid encoding prefixes (krb5int_utf8_charlen2() partially
+ * enforces the validity of the first two bytes, based on masking the second
+ * byte. It doesn't check whether bit 6 is 0, though, and doesn't catch the
+ * range between U+110000 and U+13FFFF).
+ *
+ * ucs is 0 for invalid encodings (including ones with valid prefixes according
+ * to krb5int_utf8_charlen2(); krb5int_utf8_to_ucs4() will still fail on them
+ * because it checks more things.) Code points above U+10FFFF are excluded by
+ * the actual test code and remain in the table for possibly testing the old
+ * implementation that didn't exclude them.
+ *
+ * Neither krb5int_ucs4_to_utf8() nor krb5int_utf8_to_ucs4() excludes the
+ * surrogate pair range.
+ */
+struct testcase {
+ const char *p;
+ krb5_ucs4 ucs;
+ int len;
+} testcases[] = {
+ { "\x7f", 0x0000007f, 1 }, /* Lowest 1-byte encoding */
+ { "\xc0\x80", 0x00000000, 0 }, /* Invalid 2-byte encoding */
+ { "\xc2\x80", 0x00000080, 2 }, /* Lowest valid 2-byte encoding */
+ { "\xdf\xbf", 0x000007ff, 2 }, /* Highest valid 2-byte encoding*/
+ { "\xdf\xff", 0x00000000, 2 }, /* Invalid 2-byte encoding*/
+ { "\xe0\x80\x80", 0x00000000, 0 }, /* Invalid 3-byte encoding */
+ { "\xe0\xa0\x80", 0x00000800, 3 }, /* Lowest valid 3-byte encoding */
+ { "\xef\xbf\xbf", 0x0000ffff, 3 }, /* Highest valid 3-byte encoding */
+ { "\xef\xff\xff", 0x00000000, 3 }, /* Invalid 3-byte encoding */
+ { "\xf0\x80\x80\x80", 0x00000000, 0 }, /* Invalid 4-byte encoding */
+ { "\xf0\x90\x80\x80", 0x00010000, 4 }, /* Lowest valid 4-byte encoding */
+ { "\xf4\x8f\xbf\xbf", 0x0010ffff, 4 }, /* Highest valid 4-byte encoding */
+ /* Next higher 4-byte encoding (old) */
+ { "\xf4\x90\x80\x80", 0x00110000, 4 },
+ /* Highest 4-byte encoding starting with 0xf4 (old) */
+ { "\xf4\xbf\xbf\xbf", 0x0013ffff, 4 },
+ /* Next higher 4-byte prefix byte (old) */
+ { "\xf5\x80\x80\x80", 0x00140000, L(4) },
+ /* Highest valid 4-byte encoding (old) */
+ { "\xf7\xbf\xbf\xbf", 0x001fffff, L(4) },
+ /* Invalid 4-byte encoding */
+ { "\xf7\xff\xff\xff", 0x00000000, L(4) },
+ /* Invalid 5-byte encoding */
+ { "\xf8\x80\x80\x80\x80", 0x00000000, 0 },
+ /* Lowest valid 5-byte encoding (old) */
+ { "\xf8\x88\x80\x80\x80", 0x00200000, L(5) },
+ /* Highest valid 5-byte encoding (old) */
+ { "\xfb\xbf\xbf\xbf\xbf", 0x03ffffff, L(5) },
+ /* Invalid 5-byte encoding */
+ { "\xfb\xff\xff\xff\xff", 0x00000000, L(5) },
+ /* Invalid 6-byte encoding */
+ { "\xfc\x80\x80\x80\x80\x80", 0x00000000, 0 },
+ /* Lowest valid 6-byte encoding (old) */
+ { "\xfc\x84\x80\x80\x80\x80", 0x04000000, L(6) },
+ /* Highest valid 6-byte encoding (old) */
+ { "\xfd\xbf\xbf\xbf\xbf\xbf", 0x7fffffff, L(6) },
+ /* Invalid 6-byte encoding */
+ { "\xfd\xff\xff\xff\xff\xff", 0x00000000, L(6) },
+};
+
+static void
+printhex(const char *p)
+{
+ for (; *p != '\0'; p++) {
+ printf("%02x ", (unsigned char)*p);
+ }
+}
+
+static void
+printtest(struct testcase *t)
+{
+ printhex(t->p);
+ printf("0x%08lx, %d\n", (unsigned long)t->ucs, t->len);
+}
+
+static int
+test_decode(struct testcase *t, int high4)
+{
+ int len, status = 0;
+ krb5_ucs4 u = 0;
+
+ len = krb5int_utf8_charlen2(t->p);
+ if (len != t->len) {
+ printf("expected len=%d, got len=%d\n", t->len, len);
+ status = 1;
+ }
+ if ((t->len == 0 || high4) && krb5int_utf8_to_ucs4(t->p, &u) != -1) {
+ printf("unexpected success in utf8_to_ucs4\n");
+ status = 1;
+ }
+ if (krb5int_utf8_to_ucs4(t->p, &u) != 0 && t->ucs != 0 && !high4) {
+ printf("unexpected failure in utf8_to_ucs4\n");
+ status = 1;
+ }
+ if (t->ucs != u && !high4) {
+ printf("expected 0x%08lx, got 0x%08lx\n", (unsigned long)t->ucs,
+ (unsigned long)u);
+ status = 1;
+ }
+ return status;
+}
+
+static int
+test_encode(struct testcase *t, int high4)
+{
+ size_t size;
+ char buf[7];
+
+ memset(buf, 0, sizeof(buf));
+ size = krb5int_ucs4_to_utf8(t->ucs, buf);
+ if (high4 && size != 0) {
+ printf("unexpected success beyond U+10FFFF\n");
+ return 1;
+ }
+ if (!high4 && size == 0) {
+ printf("unexpected zero size on encode\n");
+ return 1;
+ }
+ if (size != 0 && strcmp(t->p, buf) != 0) {
+ printf("expected ");
+ printhex(t->p);
+ printf("got ");
+ printhex(buf);
+ printf("\n");
+ return 1;
+ }
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ size_t ncases = sizeof(testcases) / sizeof(testcases[0]);
+ size_t i;
+ struct testcase *t;
+ int status = 0, verbose = 0;
+ /* Is this a "high" 4-byte encoding above U+10FFFF? */
+ int high4;
+
+ if (argc == 2 && strcmp(argv[1], "-v") == 0)
+ verbose = 1;
+ for (i = 0; i < ncases; i++) {
+ t = &testcases[i];
+ if (verbose)
+ printtest(t);
+#ifndef OLDENCODINGS
+ high4 = t->ucs > 0x10ffff;
+#else
+ high4 = 0;
+#endif
+ if (test_decode(t, high4) != 0)
+ status = 1;
+ if (t->ucs == 0)
+ continue;
+ if (test_encode(t, high4) != 0)
+ status = 1;
+ }
+ return status;
+}
diff --git a/src/util/support/threads.c b/src/util/support/threads.c
new file mode 100644
index 000000000000..bb8e287ecf75
--- /dev/null
+++ b/src/util/support/threads.c
@@ -0,0 +1,614 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/threads.c - Portable thread support */
+/*
+ * Copyright 2004,2005,2006,2007,2008 by the Massachusetts Institute of
+ * Technology. All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#define THREAD_SUPPORT_IMPL
+#include "k5-platform.h"
+#include "k5-thread.h"
+#include "supp-int.h"
+
+MAKE_INIT_FUNCTION(krb5int_thread_support_init);
+MAKE_FINI_FUNCTION(krb5int_thread_support_fini);
+
+/* This function used to be referenced from elsewhere in the tree, but is now
+ * only used internally. Keep it linker-visible for now. */
+int krb5int_pthread_loaded(void);
+
+#ifndef ENABLE_THREADS /* no thread support */
+
+static void (*destructors[K5_KEY_MAX])(void *);
+struct tsd_block { void *values[K5_KEY_MAX]; };
+static struct tsd_block tsd_no_threads;
+static unsigned char destructors_set[K5_KEY_MAX];
+
+int krb5int_pthread_loaded (void)
+{
+ return 0;
+}
+
+#elif defined(_WIN32)
+
+static DWORD tls_idx;
+static CRITICAL_SECTION key_lock;
+struct tsd_block {
+ void *values[K5_KEY_MAX];
+};
+static void (*destructors[K5_KEY_MAX])(void *);
+static unsigned char destructors_set[K5_KEY_MAX];
+
+void krb5int_thread_detach_hook (void)
+{
+ /* XXX Memory leak here!
+ Need to destroy all TLS objects we know about for this thread. */
+ struct tsd_block *t;
+ int i, err;
+
+ err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
+ if (err)
+ return;
+
+ t = TlsGetValue(tls_idx);
+ if (t == NULL)
+ return;
+ for (i = 0; i < K5_KEY_MAX; i++) {
+ if (destructors_set[i] && destructors[i] && t->values[i]) {
+ void *v = t->values[i];
+ t->values[i] = 0;
+ (*destructors[i])(v);
+ }
+ }
+}
+
+/* Stub function not used on Windows. */
+int krb5int_pthread_loaded (void)
+{
+ return 0;
+}
+#else /* POSIX threads */
+
+/* Must support register/delete/register sequence, e.g., if krb5 is
+ loaded so this support code stays in the process, and gssapi is
+ loaded, unloaded, and loaded again. */
+
+static k5_mutex_t key_lock = K5_MUTEX_PARTIAL_INITIALIZER;
+static void (*destructors[K5_KEY_MAX])(void *);
+static unsigned char destructors_set[K5_KEY_MAX];
+
+/* This is not safe yet!
+
+ Thread termination concurrent with key deletion can cause two
+ threads to interfere. It's a bit tricky, since one of the threads
+ will want to remove this structure from the list being walked by
+ the other.
+
+ Other cases, like looking up data while the library owning the key
+ is in the process of being unloaded, we don't worry about. */
+
+struct tsd_block {
+ struct tsd_block *next;
+ void *values[K5_KEY_MAX];
+};
+
+#ifdef HAVE_PRAGMA_WEAK_REF
+# pragma weak pthread_once
+# pragma weak pthread_mutex_lock
+# pragma weak pthread_mutex_unlock
+# pragma weak pthread_mutex_destroy
+# pragma weak pthread_mutex_init
+# pragma weak pthread_self
+# pragma weak pthread_equal
+# pragma weak pthread_getspecific
+# pragma weak pthread_setspecific
+# pragma weak pthread_key_create
+# pragma weak pthread_key_delete
+# pragma weak pthread_create
+# pragma weak pthread_join
+# define K5_PTHREADS_LOADED (krb5int_pthread_loaded())
+static volatile int flag_pthread_loaded = -1;
+static void loaded_test_aux(void)
+{
+ if (flag_pthread_loaded == -1)
+ flag_pthread_loaded = 1;
+ else
+ /* Could we have been called twice? */
+ flag_pthread_loaded = 0;
+}
+static pthread_once_t loaded_test_once = PTHREAD_ONCE_INIT;
+int krb5int_pthread_loaded (void)
+{
+ int x = flag_pthread_loaded;
+ if (x != -1)
+ return x;
+ if (&pthread_getspecific == 0
+ || &pthread_setspecific == 0
+ || &pthread_key_create == 0
+ || &pthread_key_delete == 0
+ || &pthread_once == 0
+ || &pthread_mutex_lock == 0
+ || &pthread_mutex_unlock == 0
+ || &pthread_mutex_destroy == 0
+ || &pthread_mutex_init == 0
+ || &pthread_self == 0
+ || &pthread_equal == 0
+ /* Any program that's really multithreaded will have to be
+ able to create threads. */
+ || &pthread_create == 0
+ || &pthread_join == 0
+ /* Okay, all the interesting functions -- or stubs for them --
+ seem to be present. If we call pthread_once, does it
+ actually seem to cause the indicated function to get called
+ exactly one time? */
+ || pthread_once(&loaded_test_once, loaded_test_aux) != 0
+ || pthread_once(&loaded_test_once, loaded_test_aux) != 0
+ /* This catches cases where pthread_once does nothing, and
+ never causes the function to get called. That's a pretty
+ clear violation of the POSIX spec, but hey, it happens. */
+ || flag_pthread_loaded < 0) {
+ flag_pthread_loaded = 0;
+ return 0;
+ }
+ /* If we wanted to be super-paranoid, we could try testing whether
+ pthread_get/setspecific work, too. I don't know -- so far --
+ of any system with non-functional stubs for those. */
+ return flag_pthread_loaded;
+}
+
+static struct tsd_block tsd_if_single;
+# define GET_NO_PTHREAD_TSD() (&tsd_if_single)
+#else
+# define K5_PTHREADS_LOADED (1)
+int krb5int_pthread_loaded (void)
+{
+ return 1;
+}
+
+# define GET_NO_PTHREAD_TSD() (abort(),(struct tsd_block *)0)
+#endif
+
+static pthread_key_t key;
+static void thread_termination(void *);
+
+static void thread_termination (void *tptr)
+{
+ int i, pass, none_found;
+ struct tsd_block *t = tptr;
+
+ k5_mutex_lock(&key_lock);
+
+ /*
+ * Make multiple passes in case, for example, a libkrb5 cleanup
+ * function wants to print out an error message, which causes
+ * com_err to allocate a thread-specific buffer, after we just
+ * freed up the old one.
+ *
+ * Shouldn't actually happen, if we're careful, but check just in
+ * case.
+ */
+
+ pass = 0;
+ none_found = 0;
+ while (pass < 4 && !none_found) {
+ none_found = 1;
+ for (i = 0; i < K5_KEY_MAX; i++) {
+ if (destructors_set[i] && destructors[i] && t->values[i]) {
+ void *v = t->values[i];
+ t->values[i] = 0;
+ (*destructors[i])(v);
+ none_found = 0;
+ }
+ }
+ }
+ free (t);
+ k5_mutex_unlock(&key_lock);
+
+ /* remove thread from global linked list */
+}
+
+#endif /* no threads vs Win32 vs POSIX */
+
+void *k5_getspecific (k5_key_t keynum)
+{
+ struct tsd_block *t;
+ int err;
+
+ err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
+ if (err)
+ return NULL;
+
+ assert(keynum >= 0 && keynum < K5_KEY_MAX);
+ assert(destructors_set[keynum] == 1);
+
+#ifndef ENABLE_THREADS
+
+ t = &tsd_no_threads;
+
+#elif defined(_WIN32)
+
+ t = TlsGetValue(tls_idx);
+
+#else /* POSIX */
+
+ if (K5_PTHREADS_LOADED)
+ t = pthread_getspecific(key);
+ else
+ t = GET_NO_PTHREAD_TSD();
+
+#endif
+
+ if (t == NULL)
+ return NULL;
+ return t->values[keynum];
+}
+
+int k5_setspecific (k5_key_t keynum, void *value)
+{
+ struct tsd_block *t;
+ int err;
+
+ err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
+ if (err)
+ return err;
+
+ assert(keynum >= 0 && keynum < K5_KEY_MAX);
+ assert(destructors_set[keynum] == 1);
+
+#ifndef ENABLE_THREADS
+
+ t = &tsd_no_threads;
+
+#elif defined(_WIN32)
+
+ t = TlsGetValue(tls_idx);
+ if (t == NULL) {
+ int i;
+ t = malloc(sizeof(*t));
+ if (t == NULL)
+ return ENOMEM;
+ for (i = 0; i < K5_KEY_MAX; i++)
+ t->values[i] = 0;
+ /* add to global linked list */
+ /* t->next = 0; */
+ err = TlsSetValue(tls_idx, t);
+ if (!err) {
+ free(t);
+ return GetLastError();
+ }
+ }
+
+#else /* POSIX */
+
+ if (K5_PTHREADS_LOADED) {
+ t = pthread_getspecific(key);
+ if (t == NULL) {
+ int i;
+ t = malloc(sizeof(*t));
+ if (t == NULL)
+ return ENOMEM;
+ for (i = 0; i < K5_KEY_MAX; i++)
+ t->values[i] = 0;
+ /* add to global linked list */
+ t->next = 0;
+ err = pthread_setspecific(key, t);
+ if (err) {
+ free(t);
+ return err;
+ }
+ }
+ } else {
+ t = GET_NO_PTHREAD_TSD();
+ }
+
+#endif
+
+ t->values[keynum] = value;
+ return 0;
+}
+
+int k5_key_register (k5_key_t keynum, void (*destructor)(void *))
+{
+ int err;
+
+ err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
+ if (err)
+ return err;
+
+ assert(keynum >= 0 && keynum < K5_KEY_MAX);
+
+#ifndef ENABLE_THREADS
+
+ assert(destructors_set[keynum] == 0);
+ destructors[keynum] = destructor;
+ destructors_set[keynum] = 1;
+
+#elif defined(_WIN32)
+
+ /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */
+ EnterCriticalSection(&key_lock);
+ assert(destructors_set[keynum] == 0);
+ destructors_set[keynum] = 1;
+ destructors[keynum] = destructor;
+ LeaveCriticalSection(&key_lock);
+
+#else /* POSIX */
+
+ k5_mutex_lock(&key_lock);
+ assert(destructors_set[keynum] == 0);
+ destructors_set[keynum] = 1;
+ destructors[keynum] = destructor;
+ k5_mutex_unlock(&key_lock);
+
+#endif
+ return 0;
+}
+
+int k5_key_delete (k5_key_t keynum)
+{
+ assert(keynum >= 0 && keynum < K5_KEY_MAX);
+
+#ifndef ENABLE_THREADS
+
+ assert(destructors_set[keynum] == 1);
+ if (destructors[keynum] && tsd_no_threads.values[keynum])
+ (*destructors[keynum])(tsd_no_threads.values[keynum]);
+ destructors[keynum] = 0;
+ tsd_no_threads.values[keynum] = 0;
+ destructors_set[keynum] = 0;
+
+#elif defined(_WIN32)
+
+ /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */
+ EnterCriticalSection(&key_lock);
+ /* XXX Memory leak here!
+ Need to destroy the associated data for all threads.
+ But watch for race conditions in case threads are going away too. */
+ assert(destructors_set[keynum] == 1);
+ destructors_set[keynum] = 0;
+ destructors[keynum] = 0;
+ LeaveCriticalSection(&key_lock);
+
+#else /* POSIX */
+
+ /* XXX RESOURCE LEAK: Need to destroy the allocated objects first! */
+ k5_mutex_lock(&key_lock);
+ assert(destructors_set[keynum] == 1);
+ destructors_set[keynum] = 0;
+ destructors[keynum] = NULL;
+ k5_mutex_unlock(&key_lock);
+
+#endif
+
+ return 0;
+}
+
+int krb5int_call_thread_support_init (void)
+{
+ return CALL_INIT_FUNCTION(krb5int_thread_support_init);
+}
+
+#include "cache-addrinfo.h"
+
+int krb5int_thread_support_init (void)
+{
+ int err;
+
+#ifdef SHOW_INITFINI_FUNCS
+ printf("krb5int_thread_support_init\n");
+#endif
+
+#ifndef ENABLE_THREADS
+
+ /* Nothing to do for TLS initialization. */
+
+#elif defined(_WIN32)
+
+ tls_idx = TlsAlloc();
+ /* XXX This can raise an exception if memory is low! */
+ InitializeCriticalSection(&key_lock);
+
+#else /* POSIX */
+
+ err = k5_mutex_finish_init(&key_lock);
+ if (err)
+ return err;
+ if (K5_PTHREADS_LOADED) {
+ err = pthread_key_create(&key, thread_termination);
+ if (err)
+ return err;
+ }
+
+#endif
+
+ err = krb5int_init_fac();
+ if (err)
+ return err;
+
+ err = krb5int_err_init();
+ if (err)
+ return err;
+
+ return 0;
+}
+
+void krb5int_thread_support_fini (void)
+{
+ if (! INITIALIZER_RAN (krb5int_thread_support_init))
+ return;
+
+#ifdef SHOW_INITFINI_FUNCS
+ printf("krb5int_thread_support_fini\n");
+#endif
+
+#ifndef ENABLE_THREADS
+
+ /* Do nothing. */
+
+#elif defined(_WIN32)
+
+ /* ... free stuff ... */
+ TlsFree(tls_idx);
+ DeleteCriticalSection(&key_lock);
+
+#else /* POSIX */
+
+ if (! INITIALIZER_RAN(krb5int_thread_support_init))
+ return;
+ if (K5_PTHREADS_LOADED)
+ pthread_key_delete(key);
+ /* ... delete stuff ... */
+ k5_mutex_destroy(&key_lock);
+
+#endif
+
+ krb5int_fini_fac();
+}
+
+/* Mutex allocation functions, for use in plugins that may not know
+ what options a given set of libraries was compiled with. */
+int KRB5_CALLCONV
+krb5int_mutex_alloc (k5_mutex_t **m)
+{
+ k5_mutex_t *ptr;
+ int err;
+
+ ptr = malloc (sizeof (k5_mutex_t));
+ if (ptr == NULL)
+ return ENOMEM;
+ err = k5_mutex_init (ptr);
+ if (err) {
+ free (ptr);
+ return err;
+ }
+ *m = ptr;
+ return 0;
+}
+
+void KRB5_CALLCONV
+krb5int_mutex_free (k5_mutex_t *m)
+{
+ (void) k5_mutex_destroy (m);
+ free (m);
+}
+
+/* Callable versions of the various macros. */
+void KRB5_CALLCONV
+krb5int_mutex_lock (k5_mutex_t *m)
+{
+ k5_mutex_lock (m);
+}
+void KRB5_CALLCONV
+krb5int_mutex_unlock (k5_mutex_t *m)
+{
+ k5_mutex_unlock (m);
+}
+
+#ifdef USE_CONDITIONAL_PTHREADS
+
+int
+k5_os_mutex_init(k5_os_mutex *m)
+{
+ if (krb5int_pthread_loaded())
+ return pthread_mutex_init(m, 0);
+ else
+ return 0;
+}
+
+int
+k5_os_mutex_destroy(k5_os_mutex *m)
+{
+ if (krb5int_pthread_loaded())
+ return pthread_mutex_destroy(m);
+ else
+ return 0;
+}
+
+int
+k5_os_mutex_lock(k5_os_mutex *m)
+{
+ if (krb5int_pthread_loaded())
+ return pthread_mutex_lock(m);
+ else
+ return 0;
+}
+
+int
+k5_os_mutex_unlock(k5_os_mutex *m)
+{
+ if (krb5int_pthread_loaded())
+ return pthread_mutex_unlock(m);
+ else
+ return 0;
+}
+
+int
+k5_once(k5_once_t *once, void (*fn)(void))
+{
+ if (krb5int_pthread_loaded())
+ return pthread_once(&once->o, fn);
+ else
+ return k5_os_nothread_once(&once->n, fn);
+}
+
+#else /* USE_CONDITIONAL_PTHREADS */
+
+#undef k5_os_mutex_init
+#undef k5_os_mutex_destroy
+#undef k5_os_mutex_lock
+#undef k5_os_mutex_unlock
+#undef k5_once
+
+int k5_os_mutex_init(k5_os_mutex *m);
+int k5_os_mutex_destroy(k5_os_mutex *m);
+int k5_os_mutex_lock(k5_os_mutex *m);
+int k5_os_mutex_unlock(k5_os_mutex *m);
+int k5_once(k5_once_t *once, void (*fn)(void));
+
+/* Stub functions */
+int
+k5_os_mutex_init(k5_os_mutex *m)
+{
+ return 0;
+}
+int
+k5_os_mutex_destroy(k5_os_mutex *m)
+{
+ return 0;
+}
+int
+k5_os_mutex_lock(k5_os_mutex *m)
+{
+ return 0;
+}
+int
+k5_os_mutex_unlock(k5_os_mutex *m)
+{
+ return 0;
+}
+int
+k5_once(k5_once_t *once, void (*fn)(void))
+{
+ return 0;
+}
+
+#endif /* not USE_CONDITIONAL_PTHREADS */
diff --git a/src/util/support/utf8.c b/src/util/support/utf8.c
new file mode 100644
index 000000000000..e42c0c7dc82b
--- /dev/null
+++ b/src/util/support/utf8.c
@@ -0,0 +1,533 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/utf8.c */
+/*
+ * Copyright 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/*
+ * Copyright 1998-2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>. */
+
+/* Basic UTF-8 routines
+ *
+ * These routines are "dumb". Though they understand UTF-8,
+ * they don't grok Unicode. That is, they can push bits,
+ * but don't have a clue what the bits represent. That's
+ * good enough for use with the KRB5 Client SDK.
+ *
+ * These routines are not optimized.
+ */
+
+#include "k5-platform.h"
+#include "k5-utf8.h"
+#include "supp-int.h"
+
+/*
+ * return the number of bytes required to hold the
+ * NULL-terminated UTF-8 string NOT INCLUDING the
+ * termination.
+ */
+size_t krb5int_utf8_bytes(const char *p)
+{
+ size_t bytes;
+
+ for (bytes = 0; p[bytes]; bytes++)
+ ;
+
+ return bytes;
+}
+
+size_t krb5int_utf8_chars(const char *p)
+{
+ /* could be optimized and could check for invalid sequences */
+ size_t chars = 0;
+
+ for ( ; *p ; KRB5_UTF8_INCR(p))
+ chars++;
+
+ return chars;
+}
+
+size_t krb5int_utf8c_chars(const char *p, size_t length)
+{
+ /* could be optimized and could check for invalid sequences */
+ size_t chars = 0;
+ const char *end = p + length;
+
+ for ( ; p < end; KRB5_UTF8_INCR(p))
+ chars++;
+
+ return chars;
+}
+
+/* return offset to next character */
+int krb5int_utf8_offset(const char *p)
+{
+ return KRB5_UTF8_NEXT(p) - p;
+}
+
+/*
+ * Returns length indicated by first byte.
+ */
+const char krb5int_utf8_lentab[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+int krb5int_utf8_charlen(const char *p)
+{
+ if (!(*p & 0x80))
+ return 1;
+
+ return krb5int_utf8_lentab[*(const unsigned char *)p ^ 0x80];
+}
+
+/*
+ * Make sure the UTF-8 char used the shortest possible encoding
+ * returns charlen if valid, 0 if not.
+ *
+ * Here are the valid UTF-8 encodings, taken from RFC 3629 page 4.
+ * The table is slightly modified from that of the RFC.
+ *
+ * UCS-4 range (hex) UTF-8 sequence (binary)
+ * 0000 0000-0000 007F 0.......
+ * 0000 0080-0000 07FF 110++++. 10......
+ * 0000 0800-0000 FFFF 1110++++ 10+..... 10......
+ * 0001 0000-0010 FFFF 11110+++ 10++.... 10...... 10......
+ *
+ * The '.' bits are "don't cares". When validating a UTF-8 sequence,
+ * at least one of the '+' bits must be set, otherwise the character
+ * should have been encoded in fewer octets. Note that in the two-octet
+ * case, only the first octet needs to be validated, and this is done
+ * in the krb5int_utf8_lentab[] above.
+ */
+
+/* mask of required bits in second octet */
+#undef c
+#define c const char
+c krb5int_utf8_mintab[] = {
+ (c)0x20, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80,
+ (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80,
+ (c)0x30, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x00, (c)0x00, (c)0x00,
+ (c)0x00, (c)0x00, (c)0x00, (c)0x00, (c)0x00, (c)0x00, (c)0x00, (c)0x00 };
+#undef c
+
+int krb5int_utf8_charlen2(const char *p)
+{
+ int i = KRB5_UTF8_CHARLEN(p);
+
+ if (i > 2) {
+ if (!(krb5int_utf8_mintab[*p & 0x1f] & p[1]))
+ i = 0;
+ }
+
+ return i;
+}
+
+/*
+ * Convert a UTF8 character to a UCS4 character. Return 0 on success,
+ * -1 on failure.
+ */
+int krb5int_utf8_to_ucs4(const char *p, krb5_ucs4 *out)
+{
+ const unsigned char *c = (const unsigned char *) p;
+ krb5_ucs4 ch;
+ int len, i;
+ static unsigned char mask[] = {
+ 0, 0x7f, 0x1f, 0x0f, 0x07 };
+
+ *out = 0;
+ len = KRB5_UTF8_CHARLEN2(p, len);
+
+ if (len == 0)
+ return -1;
+
+ ch = c[0] & mask[len];
+
+ for (i = 1; i < len; i++) {
+ if ((c[i] & 0xc0) != 0x80)
+ return -1;
+
+ ch <<= 6;
+ ch |= c[i] & 0x3f;
+ }
+
+ if (ch > 0x10ffff)
+ return -1;
+
+ *out = ch;
+ return 0;
+}
+
+int krb5int_utf8_to_ucs2(const char *p, krb5_ucs2 *out)
+{
+ krb5_ucs4 ch;
+
+ *out = 0;
+ if (krb5int_utf8_to_ucs4(p, &ch) == -1 || ch > 0xFFFF)
+ return -1;
+ *out = (krb5_ucs2) ch;
+ return 0;
+}
+
+/* conv UCS-2 to UTF-8, not used */
+size_t krb5int_ucs4_to_utf8(krb5_ucs4 c, char *buf)
+{
+ size_t len = 0;
+ unsigned char *p = (unsigned char *) buf;
+
+ /* not a valid Unicode character */
+ if (c > 0x10ffff)
+ return 0;
+
+ /* Just return length, don't convert */
+ if (buf == NULL) {
+ if (c < 0x80) return 1;
+ else if (c < 0x800) return 2;
+ else if (c < 0x10000) return 3;
+ else return 4;
+ }
+
+ if (c < 0x80) {
+ p[len++] = c;
+ } else if (c < 0x800) {
+ p[len++] = 0xc0 | ( c >> 6 );
+ p[len++] = 0x80 | ( c & 0x3f );
+ } else if (c < 0x10000) {
+ p[len++] = 0xe0 | ( c >> 12 );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+ } else /* if (c < 0x110000) */ {
+ p[len++] = 0xf0 | ( c >> 18 );
+ p[len++] = 0x80 | ( (c >> 12) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+ }
+
+ return len;
+}
+
+size_t krb5int_ucs2_to_utf8(krb5_ucs2 c, char *buf)
+{
+ return krb5int_ucs4_to_utf8((krb5_ucs4)c, buf);
+}
+
+/*
+ * Advance to the next UTF-8 character
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+char *krb5int_utf8_next(const char *p)
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) p;
+
+ if (KRB5_UTF8_ISASCII(u)) {
+ return (char *) &p[1];
+ }
+
+ for (i = 1; i < 6; i++) {
+ if ((u[i] & 0xc0) != 0x80) {
+ return (char *) &p[i];
+ }
+ }
+
+ return (char *) &p[i];
+}
+
+/*
+ * Advance to the previous UTF-8 character
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+char *krb5int_utf8_prev(const char *p)
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) p;
+
+ for (i = -1; i>-6 ; i--) {
+ if ((u[i] & 0xc0 ) != 0x80) {
+ return (char *) &p[i];
+ }
+ }
+
+ return (char *) &p[i];
+}
+
+/*
+ * Copy one UTF-8 character from src to dst returning
+ * number of bytes copied.
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+int krb5int_utf8_copy(char* dst, const char *src)
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) src;
+
+ dst[0] = src[0];
+
+ if (KRB5_UTF8_ISASCII(u)) {
+ return 1;
+ }
+
+ for (i=1; i<6; i++) {
+ if ((u[i] & 0xc0) != 0x80) {
+ return i;
+ }
+ dst[i] = src[i];
+ }
+
+ return i;
+}
+
+#ifndef UTF8_ALPHA_CTYPE
+/*
+ * UTF-8 ctype routines
+ * Only deals with characters < 0x80 (ie: US-ASCII)
+ */
+
+int krb5int_utf8_isascii(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ return KRB5_ASCII(c);
+}
+
+int krb5int_utf8_isdigit(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_DIGIT( c );
+}
+
+int krb5int_utf8_isxdigit(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_HEX(c);
+}
+
+int krb5int_utf8_isspace(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ switch(c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\v':
+ case '\f':
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * These are not needed by the C SDK and are
+ * not "good enough" for general use.
+ */
+int krb5int_utf8_isalpha(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_ALPHA(c);
+}
+
+int krb5int_utf8_isalnum(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_ALNUM(c);
+}
+
+#if 0
+int krb5int_utf8_islower(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_LOWER(c);
+}
+
+int krb5int_utf8_isupper(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_UPPER(c);
+}
+#endif
+#endif
+
+
+/*
+ * UTF-8 string routines
+ */
+
+/* like strchr() */
+char *krb5int_utf8_strchr(const char *str, const char *chr)
+{
+ krb5_ucs4 chs, ch;
+
+ if (krb5int_utf8_to_ucs4(chr, &ch) == -1)
+ return NULL;
+ for ( ; *str != '\0'; KRB5_UTF8_INCR(str)) {
+ if (krb5int_utf8_to_ucs4(str, &chs) == 0 && chs == ch)
+ return (char *)str;
+ }
+
+ return NULL;
+}
+
+/* like strcspn() but returns number of bytes, not characters */
+size_t krb5int_utf8_strcspn(const char *str, const char *set)
+{
+ const char *cstr, *cset;
+ krb5_ucs4 chstr, chset;
+
+ for (cstr = str; *cstr != '\0'; KRB5_UTF8_INCR(cstr)) {
+ for (cset = set; *cset != '\0'; KRB5_UTF8_INCR(cset)) {
+ if (krb5int_utf8_to_ucs4(cstr, &chstr) == 0
+ && krb5int_utf8_to_ucs4(cset, &chset) == 0 && chstr == chset)
+ return cstr - str;
+ }
+ }
+
+ return cstr - str;
+}
+
+/* like strspn() but returns number of bytes, not characters */
+size_t krb5int_utf8_strspn(const char *str, const char *set)
+{
+ const char *cstr, *cset;
+ krb5_ucs4 chstr, chset;
+
+ for (cstr = str; *cstr != '\0'; KRB5_UTF8_INCR(cstr)) {
+ for (cset = set; ; KRB5_UTF8_INCR(cset)) {
+ if (*cset == '\0')
+ return cstr - str;
+ if (krb5int_utf8_to_ucs4(cstr, &chstr) == 0
+ && krb5int_utf8_to_ucs4(cset, &chset) == 0 && chstr == chset)
+ break;
+ }
+ }
+
+ return cstr - str;
+}
+
+/* like strpbrk(), replaces strchr() as well */
+char *krb5int_utf8_strpbrk(const char *str, const char *set)
+{
+ const char *cset;
+ krb5_ucs4 chstr, chset;
+
+ for ( ; *str != '\0'; KRB5_UTF8_INCR(str)) {
+ for (cset = set; *cset != '\0'; KRB5_UTF8_INCR(cset)) {
+ if (krb5int_utf8_to_ucs4(str, &chstr) == 0
+ && krb5int_utf8_to_ucs4(cset, &chset) == 0 && chstr == chset)
+ return (char *)str;
+ }
+ }
+
+ return NULL;
+}
+
+/* like strtok_r(), not strtok() */
+char *krb5int_utf8_strtok(char *str, const char *sep, char **last)
+{
+ char *begin;
+ char *end;
+
+ if (last == NULL)
+ return NULL;
+
+ begin = str ? str : *last;
+
+ begin += krb5int_utf8_strspn(begin, sep);
+
+ if (*begin == '\0') {
+ *last = NULL;
+ return NULL;
+ }
+
+ end = &begin[krb5int_utf8_strcspn(begin, sep)];
+
+ if (*end != '\0') {
+ char *next = KRB5_UTF8_NEXT(end);
+ *end = '\0';
+ end = next;
+ }
+
+ *last = end;
+
+ return begin;
+}
diff --git a/src/util/support/utf8_conv.c b/src/util/support/utf8_conv.c
new file mode 100644
index 000000000000..80ca90b139e7
--- /dev/null
+++ b/src/util/support/utf8_conv.c
@@ -0,0 +1,457 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/utf8_conv.c */
+/*
+ * Copyright 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/*
+ * Copyright 1998-2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
+ * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
+ * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
+ * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
+ * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
+ * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
+ * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
+ * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ */
+
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>. */
+
+/*
+ * UTF-8 Conversion Routines
+ *
+ * These routines convert between Wide Character and UTF-8,
+ * or between MultiByte and UTF-8 encodings.
+ *
+ * Both single character and string versions of the functions are provided.
+ * All functions return -1 if the character or string cannot be converted.
+ */
+
+#include "k5-platform.h"
+#include "k5-utf8.h"
+#include "supp-int.h"
+
+static unsigned char mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
+
+static ssize_t
+k5_utf8s_to_ucs2s(krb5_ucs2 *ucs2str,
+ const char *utf8str,
+ size_t count,
+ int little_endian)
+{
+ size_t ucs2len = 0;
+ size_t utflen, i;
+ krb5_ucs2 ch;
+
+ /* If input ptr is NULL or empty... */
+ if (utf8str == NULL || *utf8str == '\0') {
+ if (ucs2str != NULL)
+ *ucs2str = 0;
+
+ return 0;
+ }
+
+ /* Examine next UTF-8 character. */
+ while (ucs2len < count && *utf8str != '\0') {
+ /* Get UTF-8 sequence length from 1st byte */
+ utflen = KRB5_UTF8_CHARLEN2(utf8str, utflen);
+
+ if (utflen == 0 || utflen > KRB5_MAX_UTF8_LEN)
+ return -1;
+
+ /* First byte minus length tag */
+ ch = (krb5_ucs2)(utf8str[0] & mask[utflen]);
+
+ for (i = 1; i < utflen; i++) {
+ /* Subsequent bytes must start with 10 */
+ if ((utf8str[i] & 0xc0) != 0x80)
+ return -1;
+
+ ch <<= 6; /* 6 bits of data in each subsequent byte */
+ ch |= (krb5_ucs2)(utf8str[i] & 0x3f);
+ }
+
+ if (ucs2str != NULL) {
+#ifdef K5_BE
+#ifndef SWAP16
+#define SWAP16(X) ((((X) << 8) | ((X) >> 8)) & 0xFFFF)
+#endif
+ if (little_endian)
+ ucs2str[ucs2len] = SWAP16(ch);
+ else
+#endif
+ ucs2str[ucs2len] = ch;
+ }
+
+ utf8str += utflen; /* Move to next UTF-8 character */
+ ucs2len++; /* Count number of wide chars stored/required */
+ }
+
+ if (ucs2str != NULL && ucs2len < count) {
+ /* Add null terminator if there's room in the buffer. */
+ ucs2str[ucs2len] = 0;
+ }
+
+ return ucs2len;
+}
+
+int
+krb5int_utf8s_to_ucs2s(const char *utf8s,
+ krb5_ucs2 **ucs2s,
+ size_t *ucs2chars)
+{
+ ssize_t len;
+ size_t chars;
+
+ chars = krb5int_utf8_chars(utf8s);
+ *ucs2s = (krb5_ucs2 *)malloc((chars + 1) * sizeof(krb5_ucs2));
+ if (*ucs2s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_utf8s_to_ucs2s(*ucs2s, utf8s, chars + 1, 0);
+ if (len < 0) {
+ free(*ucs2s);
+ *ucs2s = NULL;
+ return EINVAL;
+ }
+
+ if (ucs2chars != NULL) {
+ *ucs2chars = chars;
+ }
+
+ return 0;
+}
+
+int
+krb5int_utf8cs_to_ucs2s(const char *utf8s,
+ size_t utf8slen,
+ krb5_ucs2 **ucs2s,
+ size_t *ucs2chars)
+{
+ ssize_t len;
+ size_t chars;
+
+ chars = krb5int_utf8c_chars(utf8s, utf8slen);
+ *ucs2s = (krb5_ucs2 *)malloc((chars + 1) * sizeof(krb5_ucs2));
+ if (*ucs2s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_utf8s_to_ucs2s(*ucs2s, utf8s, chars, 0);
+ if (len < 0) {
+ free(*ucs2s);
+ *ucs2s = NULL;
+ return EINVAL;
+ }
+ (*ucs2s)[chars] = 0;
+
+ if (ucs2chars != NULL) {
+ *ucs2chars = chars;
+ }
+
+ return 0;
+}
+
+int
+krb5int_utf8s_to_ucs2les(const char *utf8s,
+ unsigned char **ucs2les,
+ size_t *ucs2leslen)
+{
+ ssize_t len;
+ size_t chars;
+
+ chars = krb5int_utf8_chars(utf8s);
+
+ *ucs2les = (unsigned char *)malloc((chars + 1) * sizeof(krb5_ucs2));
+ if (*ucs2les == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_utf8s_to_ucs2s((krb5_ucs2 *)*ucs2les, utf8s, chars + 1, 1);
+ if (len < 0) {
+ free(*ucs2les);
+ *ucs2les = NULL;
+ return EINVAL;
+ }
+
+ if (ucs2leslen != NULL) {
+ *ucs2leslen = chars * sizeof(krb5_ucs2);
+ }
+
+ return 0;
+}
+
+int
+krb5int_utf8cs_to_ucs2les(const char *utf8s,
+ size_t utf8slen,
+ unsigned char **ucs2les,
+ size_t *ucs2leslen)
+{
+ ssize_t len;
+ size_t chars;
+ krb5_ucs2 *ucs2s;
+
+ *ucs2les = NULL;
+
+ chars = krb5int_utf8c_chars(utf8s, utf8slen);
+ ucs2s = malloc((chars + 1) * sizeof(krb5_ucs2));
+ if (ucs2s == NULL)
+ return ENOMEM;
+
+ len = k5_utf8s_to_ucs2s(ucs2s, utf8s, chars, 1);
+ if (len < 0) {
+ free(ucs2s);
+ return EINVAL;
+ }
+ ucs2s[chars] = 0;
+
+ *ucs2les = (unsigned char *)ucs2s;
+ if (ucs2leslen != NULL) {
+ *ucs2leslen = chars * sizeof(krb5_ucs2);
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ Convert a wide char string to a UTF-8 string.
+ No more than 'count' bytes will be written to the output buffer.
+ Return the # of bytes written to the output buffer, excl null terminator.
+
+ ucs2len is -1 if the UCS-2 string is NUL terminated, otherwise it is the
+ length of the UCS-2 string in characters
+*/
+static ssize_t
+k5_ucs2s_to_utf8s(char *utf8str, const krb5_ucs2 *ucs2str,
+ size_t count, ssize_t ucs2len, int little_endian)
+{
+ int len = 0;
+ int n;
+ char *p = utf8str;
+ krb5_ucs2 empty = 0, ch;
+
+ if (ucs2str == NULL) /* Treat input ptr NULL as an empty string */
+ ucs2str = &empty;
+
+ if (utf8str == NULL) /* Just compute size of output, excl null */
+ {
+ while (ucs2len == -1 ? *ucs2str : --ucs2len >= 0) {
+ /* Get UTF-8 size of next wide char */
+ ch = *ucs2str++;
+#ifdef K5_BE
+ if (little_endian)
+ ch = SWAP16(ch);
+#endif
+
+ n = krb5int_ucs2_to_utf8(ch, NULL);
+ if (n < 1 || n > INT_MAX - len)
+ return -1;
+ len += n;
+ }
+
+ return len;
+ }
+
+ /* Do the actual conversion. */
+
+ n = 1; /* In case of empty ucs2str */
+ while (ucs2len == -1 ? *ucs2str != 0 : --ucs2len >= 0) {
+ ch = *ucs2str++;
+#ifdef K5_BE
+ if (little_endian)
+ ch = SWAP16(ch);
+#endif
+
+ n = krb5int_ucs2_to_utf8(ch, p);
+
+ if (n < 1)
+ break;
+
+ p += n;
+ count -= n; /* Space left in output buffer */
+ }
+
+ /* If not enough room for last character, pad remainder with null
+ so that return value = original count, indicating buffer full. */
+ if (n == 0) {
+ while (count--)
+ *p++ = 0;
+ }
+ /* Add a null terminator if there's room. */
+ else if (count)
+ *p = 0;
+
+ if (n == -1) /* Conversion encountered invalid wide char. */
+ return -1;
+
+ /* Return the number of bytes written to output buffer, excl null. */
+ return (p - utf8str);
+}
+
+int
+krb5int_ucs2s_to_utf8s(const krb5_ucs2 *ucs2s,
+ char **utf8s,
+ size_t *utf8slen)
+{
+ ssize_t len;
+
+ len = k5_ucs2s_to_utf8s(NULL, ucs2s, 0, -1, 0);
+ if (len < 0) {
+ return EINVAL;
+ }
+
+ *utf8s = (char *)malloc((size_t)len + 1);
+ if (*utf8s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_ucs2s_to_utf8s(*utf8s, ucs2s, (size_t)len + 1, -1, 0);
+ if (len < 0) {
+ free(*utf8s);
+ *utf8s = NULL;
+ return EINVAL;
+ }
+
+ if (utf8slen != NULL) {
+ *utf8slen = len;
+ }
+
+ return 0;
+}
+
+int
+krb5int_ucs2les_to_utf8s(const unsigned char *ucs2les,
+ char **utf8s,
+ size_t *utf8slen)
+{
+ ssize_t len;
+
+ len = k5_ucs2s_to_utf8s(NULL, (krb5_ucs2 *)ucs2les, 0, -1, 1);
+ if (len < 0)
+ return EINVAL;
+
+ *utf8s = (char *)malloc((size_t)len + 1);
+ if (*utf8s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2les, (size_t)len + 1, -1, 1);
+ if (len < 0) {
+ free(*utf8s);
+ *utf8s = NULL;
+ return EINVAL;
+ }
+
+ if (utf8slen != NULL) {
+ *utf8slen = len;
+ }
+
+ return 0;
+}
+
+int
+krb5int_ucs2cs_to_utf8s(const krb5_ucs2 *ucs2s,
+ size_t ucs2slen,
+ char **utf8s,
+ size_t *utf8slen)
+{
+ ssize_t len;
+
+ if (ucs2slen > SSIZE_MAX)
+ return ERANGE;
+
+ len = k5_ucs2s_to_utf8s(NULL, (krb5_ucs2 *)ucs2s, 0,
+ (ssize_t)ucs2slen, 0);
+ if (len < 0)
+ return EINVAL;
+
+ *utf8s = (char *)malloc((size_t)len + 1);
+ if (*utf8s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2s, (size_t)len,
+ (ssize_t)ucs2slen, 0);
+ if (len < 0) {
+ free(*utf8s);
+ *utf8s = NULL;
+ return EINVAL;
+ }
+ (*utf8s)[len] = '\0';
+
+ if (utf8slen != NULL) {
+ *utf8slen = len;
+ }
+
+ return 0;
+}
+
+int
+krb5int_ucs2lecs_to_utf8s(const unsigned char *ucs2les,
+ size_t ucs2leslen,
+ char **utf8s,
+ size_t *utf8slen)
+{
+ ssize_t len;
+
+ if (ucs2leslen > SSIZE_MAX)
+ return ERANGE;
+
+ len = k5_ucs2s_to_utf8s(NULL, (krb5_ucs2 *)ucs2les, 0,
+ (ssize_t)ucs2leslen, 1);
+ if (len < 0)
+ return EINVAL;
+
+ *utf8s = (char *)malloc((size_t)len + 1);
+ if (*utf8s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2les, (size_t)len,
+ (ssize_t)ucs2leslen, 1);
+ if (len < 0) {
+ free(*utf8s);
+ *utf8s = NULL;
+ return EINVAL;
+ }
+ (*utf8s)[len] = '\0';
+
+ if (utf8slen != NULL) {
+ *utf8slen = len;
+ }
+
+ return 0;
+}
diff --git a/src/util/support/zap.c b/src/util/support/zap.c
new file mode 100644
index 000000000000..ed31630db1b9
--- /dev/null
+++ b/src/util/support/zap.c
@@ -0,0 +1,41 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/zap.c */
+/*
+ * Copyright 2009 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * krb5int_zap() is used by zap() (a static inline function defined in
+ * k5-int.h) on non-Windows, non-gcc compilers, in order to prevent the
+ * compiler from inlining and optimizing out the memset() call.
+ */
+
+#include <k5-platform.h>
+
+void krb5int_zap(void *ptr, size_t len)
+{
+ volatile char *p = ptr;
+
+ while (len--)
+ *p++ = '\0';
+}