diff options
Diffstat (limited to 'src/util/support')
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 = ∅ + + 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'; +} |
