diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 11:11:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 11:11:40 +0000 |
commit | 7731832751ab9f3c6ddeb66f186d3d7fa1934a6d (patch) | |
tree | e91015872543a59be2aad26c2fea02e41b57005d /libraries/liblber | |
parent | Initial commit. (diff) | |
download | openldap-7731832751ab9f3c6ddeb66f186d3d7fa1934a6d.tar.xz openldap-7731832751ab9f3c6ddeb66f186d3d7fa1934a6d.zip |
Adding upstream version 2.4.57+dfsg.upstream/2.4.57+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | libraries/liblber/Makefile.in | 53 | ||||
-rw-r--r-- | libraries/liblber/assert.c | 40 | ||||
-rw-r--r-- | libraries/liblber/bprint.c | 296 | ||||
-rw-r--r-- | libraries/liblber/debug.c | 105 | ||||
-rw-r--r-- | libraries/liblber/decode.c | 1000 | ||||
-rw-r--r-- | libraries/liblber/dtest.c | 121 | ||||
-rw-r--r-- | libraries/liblber/encode.c | 651 | ||||
-rw-r--r-- | libraries/liblber/etest.c | 181 | ||||
-rw-r--r-- | libraries/liblber/idtest.c | 87 | ||||
-rw-r--r-- | libraries/liblber/io.c | 725 | ||||
-rw-r--r-- | libraries/liblber/lber-int.h | 224 | ||||
-rw-r--r-- | libraries/liblber/memory.c | 825 | ||||
-rw-r--r-- | libraries/liblber/nt_err.c | 96 | ||||
-rw-r--r-- | libraries/liblber/options.c | 233 | ||||
-rw-r--r-- | libraries/liblber/sockbuf.c | 988 | ||||
-rw-r--r-- | libraries/liblber/stdio.c | 243 |
16 files changed, 5868 insertions, 0 deletions
diff --git a/libraries/liblber/Makefile.in b/libraries/liblber/Makefile.in new file mode 100644 index 0000000..2247868 --- /dev/null +++ b/libraries/liblber/Makefile.in @@ -0,0 +1,53 @@ +# LIBLBER +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 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>. + +LIBRARY = liblber.la + +NT_SRCS = nt_err.c +NT_OBJS = nt_err.lo + +UNIX_SRCS = stdio.c +UNIX_OBJS = stdio.lo + +LIB_DEFS = -DLBER_LIBRARY + +SRCS= assert.c decode.c encode.c io.c bprint.c debug.c \ + memory.c options.c sockbuf.c $(@PLAT@_SRCS) +OBJS= assert.lo decode.lo encode.lo io.lo bprint.lo debug.lo \ + memory.lo options.lo sockbuf.lo $(@PLAT@_OBJS) +XSRCS= version.c + +PROGRAMS= dtest etest idtest + +LDAP_INCDIR= ../../include +LDAP_LIBDIR= ../../libraries + +XLIBS = $(LIBRARY) $(LDAP_LIBLUTIL_A) +XXLIBS = +NT_LINK_LIBS = $(AC_LIBS) +UNIX_LINK_LIBS = $(AC_LIBS) + +dtest: $(XLIBS) dtest.o + $(LTLINK) -o $@ dtest.o $(LIBS) +etest: $(XLIBS) etest.o + $(LTLINK) -o $@ etest.o $(LIBS) +idtest: $(XLIBS) idtest.o + $(LTLINK) -o $@ idtest.o $(LIBS) + +install-local: FORCE + -$(MKDIR) $(DESTDIR)$(libdir) + $(LTINSTALL) $(INSTALLFLAGS) -m 644 $(LIBRARY) $(DESTDIR)$(libdir) + $(LTFINISH) $(DESTDIR)$(libdir) + diff --git a/libraries/liblber/assert.c b/libraries/liblber/assert.c new file mode 100644 index 0000000..5e87dfb --- /dev/null +++ b/libraries/liblber/assert.c @@ -0,0 +1,40 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ + +#include "portable.h" + +#ifdef LDAP_NEED_ASSERT + +#include <stdio.h> + +/* + * helper for our private assert() macro + * + * note: if assert() doesn't exist, like abort() or raise() won't either. + * could use kill() but that might be problematic. I'll just ignore this + * issue for now. + */ + +void +ber_pvt_assert( const char *file, int line, const char *test ) +{ + fprintf(stderr, + _("Assertion failed: %s, file %s, line %d\n"), + test, file, line); + + abort(); +} + +#endif diff --git a/libraries/liblber/bprint.c b/libraries/liblber/bprint.c new file mode 100644 index 0000000..c080b62 --- /dev/null +++ b/libraries/liblber/bprint.c @@ -0,0 +1,296 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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) 1991 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/ctype.h> +#include <ac/stdarg.h> +#include <ac/string.h> + +#include "lber-int.h" + +#define ber_log_check(errlvl, loglvl) ((errlvl) & (loglvl)) + +BER_LOG_FN ber_int_log_proc = NULL; + +/* + * We don't just set ber_pvt_err_file to stderr here, because in NT, + * stderr is a symbol imported from a DLL. As such, the compiler + * doesn't recognize the symbol as having a constant address. Thus + * we set ber_pvt_err_file to stderr later, when it first gets + * referenced. + */ +FILE *ber_pvt_err_file = NULL; + +/* + * ber errno + */ +BER_ERRNO_FN ber_int_errno_fn = NULL; + +int * ber_errno_addr(void) +{ + static int ber_int_errno = LBER_ERROR_NONE; + + if( ber_int_errno_fn ) { + return (*ber_int_errno_fn)(); + } + + return &ber_int_errno; +} + +/* + * Print stuff + */ +void ber_error_print( LDAP_CONST char *data ) +{ + assert( data != NULL ); + + if (!ber_pvt_err_file) ber_pvt_err_file = stderr; + + fputs( data, ber_pvt_err_file ); + + /* Print to both streams */ + if (ber_pvt_err_file != stderr) { + fputs( data, stderr ); + fflush( stderr ); + } + + fflush( ber_pvt_err_file ); +} + +BER_LOG_PRINT_FN ber_pvt_log_print = ber_error_print; + +/* + * lber log + */ + +int ber_pvt_log_output( + const char *subsystem, + int level, + const char *fmt, + ... ) +{ + char buf[1024]; + va_list vl; + va_start( vl, fmt ); + + if ( ber_int_log_proc != NULL ) { + ber_int_log_proc( ber_pvt_err_file, subsystem, level, fmt, vl ); + + } else { + int level; + ber_get_option( NULL, LBER_OPT_BER_DEBUG, &level ); + buf[sizeof(buf) - 1] = '\0'; + vsnprintf( buf, sizeof(buf)-1, fmt, vl ); + if ( ber_log_check( LDAP_DEBUG_BER, level ) ) { + (*ber_pvt_log_print)( buf ); + } + } + + va_end(vl); + return 1; +} + +int ber_pvt_log_printf( int errlvl, int loglvl, const char *fmt, ... ) +{ + char buf[1024]; + va_list ap; + + assert( fmt != NULL ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + va_start( ap, fmt ); + + buf[sizeof(buf) - 1] = '\0'; + vsnprintf( buf, sizeof(buf)-1, fmt, ap ); + + va_end(ap); + + (*ber_pvt_log_print)( buf ); + return 1; +} + +#if 0 +static int ber_log_puts(int errlvl, int loglvl, char *buf) +{ + assert( buf != NULL ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + (*ber_pvt_log_print)( buf ); + return 1; +} +#endif + +/* + * Print arbitrary stuff, for debugging. + */ + +int +ber_log_bprint(int errlvl, + int loglvl, + const char *data, + ber_len_t len ) +{ + assert( data != NULL ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + ber_bprint(data, len); + return 1; +} + +void +ber_bprint( + LDAP_CONST char *data, + ber_len_t len ) +{ + static const char hexdig[] = "0123456789abcdef"; +#define BP_OFFSET 9 +#define BP_GRAPH 60 +#define BP_LEN 80 + char line[BP_LEN]; + ber_len_t i; + + assert( data != NULL ); + + /* in case len is zero */ + line[0] = '\n'; + line[1] = '\0'; + + for ( i = 0 ; i < len ; i++ ) { + int n = i % 16; + unsigned off; + + if( !n ) { + if( i ) (*ber_pvt_log_print)( line ); + memset( line, ' ', sizeof(line)-2 ); + line[sizeof(line)-2] = '\n'; + line[sizeof(line)-1] = '\0'; + + off = i % 0x0ffffU; + + line[2] = hexdig[0x0f & (off >> 12)]; + line[3] = hexdig[0x0f & (off >> 8)]; + line[4] = hexdig[0x0f & (off >> 4)]; + line[5] = hexdig[0x0f & off]; + line[6] = ':'; + } + + off = BP_OFFSET + n*3 + ((n >= 8)?1:0); + line[off] = hexdig[0x0f & ( data[i] >> 4 )]; + line[off+1] = hexdig[0x0f & data[i]]; + + off = BP_GRAPH + n + ((n >= 8)?1:0); + + if ( isprint( (unsigned char) data[i] )) { + line[BP_GRAPH + n] = data[i]; + } else { + line[BP_GRAPH + n] = '.'; + } + } + + (*ber_pvt_log_print)( line ); +} + + +int +ber_log_dump( + int errlvl, + int loglvl, + BerElement *ber, + int inout ) +{ + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + ber_dump(ber, inout); + return 1; +} + +void +ber_dump( + BerElement *ber, + int inout ) +{ + char buf[132]; + ber_len_t len; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( inout == 1 ) { + len = ber_pvt_ber_remaining(ber); + } else { + len = ber_pvt_ber_write(ber); + } + + sprintf( buf, "ber_dump: buf=%p ptr=%p end=%p len=%ld\n", + ber->ber_buf, + ber->ber_ptr, + ber->ber_end, + (long) len ); + + (void) (*ber_pvt_log_print)( buf ); + + ber_bprint( ber->ber_ptr, len ); +} + +typedef struct seqorset Seqorset; + +/* Exists for binary compatibility with OpenLDAP 2.4.17-- */ +int +ber_log_sos_dump( + int errlvl, + int loglvl, + Seqorset *sos ) +{ + return 0; +} + +/* Exists for binary compatibility with OpenLDAP 2.4.17-- */ +void +ber_sos_dump( + Seqorset *sos ) +{ +} diff --git a/libraries/liblber/debug.c b/libraries/liblber/debug.c new file mode 100644 index 0000000..7d1f772 --- /dev/null +++ b/libraries/liblber/debug.c @@ -0,0 +1,105 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdarg.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/ctype.h> + +#ifdef LDAP_SYSLOG +#include <ac/syslog.h> +#endif + +#include "ldap_log.h" +#include "ldap_defaults.h" +#include "lber.h" +#include "ldap_pvt.h" + +static FILE *log_file = NULL; +static int debug_lastc = '\n'; + +int lutil_debug_file( FILE *file ) +{ + log_file = file; + ber_set_option( NULL, LBER_OPT_LOG_PRINT_FILE, file ); + + return 0; +} + +void (lutil_debug)( int debug, int level, const char *fmt, ... ) +{ + char buffer[4096]; + va_list vl; + int len, off; + + if ( !(level & debug ) ) return; + +#ifdef HAVE_WINSOCK + if( log_file == NULL ) { + log_file = fopen( LDAP_RUNDIR LDAP_DIRSEP "openldap.log", "w" ); + + if ( log_file == NULL ) { + log_file = fopen( "openldap.log", "w" ); + if ( log_file == NULL ) return; + } + + ber_set_option( NULL, LBER_OPT_LOG_PRINT_FILE, log_file ); + } +#endif + + if (debug_lastc == '\n') { + sprintf(buffer, "%08x ", (unsigned) time(0L)); + off = 9; + } else { + off = 0; + } + va_start( vl, fmt ); + len = vsnprintf( buffer+off, sizeof(buffer)-off, fmt, vl ); + if (len > sizeof(buffer)-off) + len = sizeof(buffer)-off; + debug_lastc = buffer[len+off-1]; + buffer[sizeof(buffer)-1] = '\0'; + if( log_file != NULL ) { + fputs( buffer, log_file ); + fflush( log_file ); + } + fputs( buffer, stderr ); + va_end( vl ); +} + +#if defined(HAVE_EBCDIC) && defined(LDAP_SYSLOG) +#undef syslog +void eb_syslog( int pri, const char *fmt, ... ) +{ + char buffer[4096]; + va_list vl; + + va_start( vl, fmt ); + vsnprintf( buffer, sizeof(buffer), fmt, vl ); + buffer[sizeof(buffer)-1] = '\0'; + + /* The syslog function appears to only work with pure EBCDIC */ + __atoe(buffer); +#pragma convlit(suspend) + syslog( pri, "%s", buffer ); +#pragma convlit(resume) + va_end( vl ); +} +#endif diff --git a/libraries/liblber/decode.c b/libraries/liblber/decode.c new file mode 100644 index 0000000..a357ec9 --- /dev/null +++ b/libraries/liblber/decode.c @@ -0,0 +1,1000 @@ +/* decode.c - ber input decoding routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/stdarg.h> +#include <ac/string.h> +#include <ac/socket.h> + +#include "lber-int.h" + + +/* out->bv_len should be the buffer size on input */ +int +ber_decode_oid( BerValue *in, BerValue *out ) +{ + const unsigned char *der; + unsigned long val; + unsigned val1; + ber_len_t i; + char *ptr; + + assert( in != NULL ); + assert( out != NULL ); + + /* need 4 chars/inbyte + \0 for input={7f 7f 7f...} */ + if ( !out->bv_val || (out->bv_len+3)/4 <= in->bv_len ) + return -1; + + ptr = NULL; + der = (unsigned char *) in->bv_val; + val = 0; + for ( i=0; i < in->bv_len; i++ ) { + val |= der[i] & 0x7f; + if ( !( der[i] & 0x80 )) { + if ( ptr == NULL ) { + /* Initial "x.y": val=x*40+y, x<=2, y<40 if x<2 */ + ptr = out->bv_val; + val1 = (val < 80 ? val/40 : 2); + val -= val1*40; + ptr += sprintf( ptr, "%u", val1 ); + } + ptr += sprintf( ptr, ".%lu", val ); + val = 0; + } else if ( val - 1UL < LBER_OID_COMPONENT_MAX >> 7 ) { + val <<= 7; + } else { + /* val would overflow, or is 0 from invalid initial 0x80 octet */ + return -1; + } + } + if ( ptr == NULL || val != 0 ) + return -1; + + out->bv_len = ptr - out->bv_val; + return 0; +} + +/* Return tag, with *bv = rest of element (starting at length octets) */ +static ber_tag_t +ber_tag_and_rest( const BerElement *ber, struct berval *bv ) +{ + ber_tag_t tag; + ptrdiff_t rest; + unsigned char *ptr; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + ptr = (unsigned char *) ber->ber_ptr; + rest = (unsigned char *) ber->ber_end - ptr; + if ( rest <= 0 ) { + goto fail; + } + + tag = ber->ber_tag; + if ( (char *) ptr == ber->ber_buf ) { + tag = *ptr; + } + ptr++; + rest--; + if ( (tag & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK ) { + goto done; + } + + do { + if ( rest <= 0 ) { + break; + } + tag <<= 8; + tag |= *ptr++ & 0xffU; + rest--; + + if ( ! (tag & LBER_MORE_TAG_MASK) ) { + goto done; + } + } while ( tag <= (ber_tag_t)-1 / 256 ); + + fail: + /* Error or unsupported tag size */ + tag = LBER_DEFAULT; + + done: + bv->bv_len = rest; + bv->bv_val = (char *) ptr; + return tag; +} + +/* Return the tag - LBER_DEFAULT returned means trouble */ +ber_tag_t +ber_get_tag( BerElement *ber ) +{ + struct berval bv; + ber_tag_t tag = ber_tag_and_rest( ber, &bv ); + + ber->ber_ptr = bv.bv_val; + return tag; +} + +/* Return next element's tag and point *bv at its contents in-place */ +ber_tag_t +ber_peek_element( const BerElement *ber, struct berval *bv ) +{ + ber_tag_t tag; + ber_len_t len, rest; + unsigned i; + unsigned char *ptr; + + assert( bv != NULL ); + + /* + * Any ber element looks like this: tag length contents. + * Assuming everything's ok, we return the tag, and point + * bv at the contents. + * + * Assumptions: + * 1) definite lengths + * 2) primitive encodings used whenever possible + */ + + len = 0; + + /* + * First, we read the tag. + */ + tag = ber_tag_and_rest( ber, bv ); + + rest = bv->bv_len; + ptr = (unsigned char *) bv->bv_val; + if ( tag == LBER_DEFAULT || rest == 0 ) { + goto fail; + } + + /* + * Next, read the length. The first octet determines the length + * of the length. If bit 8 is 0, the length is the short form, + * otherwise if the octet != 0x80 it's the long form, otherwise + * the ber element has the unsupported indefinite-length format. + * Lengths that do not fit in a ber_len_t are not accepted. + */ + + len = *ptr++; + rest--; + + if ( len & 0x80U ) { + len &= 0x7fU; + if ( len - 1U > sizeof(ber_len_t) - 1U || rest < len ) { + /* Indefinite-length/too long length/not enough data */ + goto fail; + } + + rest -= len; + i = len; + for( len = *ptr++ & 0xffU; --i; len |= *ptr++ & 0xffU ) { + len <<= 8; + } + } + + /* BER element should have enough data left */ + if( len > rest ) { + fail: + tag = LBER_DEFAULT; + } + + bv->bv_len = len; + bv->bv_val = (char *) ptr; + return tag; +} + +/* Move past next element, point *bv at it in-place, and return its tag. + * The caller may \0-terminate *bv, as next octet is saved in ber->ber_tag. + * Similar to ber_get_stringbv(ber, bv, LBER_BV_NOTERM) except on error. + */ +ber_tag_t +ber_skip_element( BerElement *ber, struct berval *bv ) +{ + ber_tag_t tag = ber_peek_element( ber, bv ); + + if ( tag != LBER_DEFAULT ) { + ber->ber_ptr = bv->bv_val + bv->bv_len; + ber->ber_tag = *(unsigned char *) ber->ber_ptr; + } + + return tag; +} + +ber_tag_t +ber_peek_tag( + BerElement *ber, + ber_len_t *len ) +{ + struct berval bv; + ber_tag_t tag = ber_peek_element( ber, &bv ); + + *len = bv.bv_len; + return tag; +} + +ber_tag_t +ber_skip_tag( BerElement *ber, ber_len_t *lenp ) +{ + struct berval bv; + ber_tag_t tag = ber_peek_element( ber, &bv ); + + ber->ber_ptr = bv.bv_val; + ber->ber_tag = *(unsigned char *) ber->ber_ptr; + + *lenp = bv.bv_len; + return tag; +} + +ber_tag_t +ber_get_int( + BerElement *ber, + ber_int_t *num ) +{ + ber_tag_t tag; + ber_len_t len; + struct berval bv; + + assert( num != NULL ); + + tag = ber_skip_element( ber, &bv ); + len = bv.bv_len; + if ( tag == LBER_DEFAULT || len > sizeof(ber_int_t) ) { + return LBER_DEFAULT; + } + + /* parse two's complement integer */ + if( len ) { + unsigned char *buf = (unsigned char *) bv.bv_val; + ber_len_t i; + ber_int_t netnum = buf[0] & 0xff; + + /* sign extend */ + netnum = (netnum ^ 0x80) - 0x80; + + /* shift in the bytes */ + for( i = 1; i < len; i++ ) { + netnum = (netnum << 8 ) | buf[i]; + } + + *num = netnum; + + } else { + *num = 0; + } + + return tag; +} + +ber_tag_t +ber_get_enum( + BerElement *ber, + ber_int_t *num ) +{ + return ber_get_int( ber, num ); +} + +ber_tag_t +ber_get_stringb( + BerElement *ber, + char *buf, + ber_len_t *len ) +{ + struct berval bv; + ber_tag_t tag; + + if ( (tag = ber_skip_element( ber, &bv )) == LBER_DEFAULT ) { + return LBER_DEFAULT; + } + + /* must fit within allocated space with termination */ + if ( bv.bv_len >= *len ) { + return LBER_DEFAULT; + } + + memcpy( buf, bv.bv_val, bv.bv_len ); + buf[bv.bv_len] = '\0'; + + *len = bv.bv_len; + return tag; +} + +/* Definitions for get_string vector + * + * ChArray, BvArray, and BvVec are self-explanatory. + * BvOff is a struct berval embedded in an array of larger structures + * of siz bytes at off bytes from the beginning of the struct. + */ +enum bgbvc { ChArray, BvArray, BvVec, BvOff }; + +/* Use this single cookie for state, to keep actual + * stack use to the absolute minimum. + */ +typedef struct bgbvr { + const enum bgbvc choice; + const int option; /* (ALLOC unless BvOff) | (STRING if ChArray) */ + ber_len_t siz; /* input array element size, output count */ + ber_len_t off; /* BvOff offset to the struct berval */ + void *result; +} bgbvr; + +static ber_tag_t +ber_get_stringbvl( BerElement *ber, bgbvr *b ) +{ + int i = 0, n; + ber_tag_t tag; + ber_len_t tot_size = 0, siz = b->siz; + char *last, *orig; + struct berval bv, *bvp = NULL; + union stringbvl_u { + char **ca; /* ChArray */ + BerVarray ba; /* BvArray */ + struct berval **bv; /* BvVec */ + char *bo; /* BvOff */ + } res; + + tag = ber_skip_tag( ber, &bv.bv_len ); + + if ( tag != LBER_DEFAULT ) { + tag = 0; + orig = ber->ber_ptr; + last = orig + bv.bv_len; + + for ( ; ber->ber_ptr < last; i++, tot_size += siz ) { + if ( ber_skip_element( ber, &bv ) == LBER_DEFAULT ) + break; + } + if ( ber->ber_ptr != last ) { + i = 0; + tag = LBER_DEFAULT; + } + + ber->ber_ptr = orig; + ber->ber_tag = *(unsigned char *) orig; + } + + b->siz = i; + if ( i == 0 ) { + return tag; + } + + /* Allocate and NULL-terminate the result vector */ + b->result = ber_memalloc_x( tot_size + siz, ber->ber_memctx ); + if ( b->result == NULL ) { + return LBER_DEFAULT; + } + switch (b->choice) { + case ChArray: + res.ca = b->result; + res.ca[i] = NULL; + break; + case BvArray: + res.ba = b->result; + res.ba[i].bv_val = NULL; + break; + case BvVec: + res.bv = b->result; + res.bv[i] = NULL; + break; + case BvOff: + res.bo = (char *) b->result + b->off; + ((struct berval *) (res.bo + tot_size))->bv_val = NULL; + tot_size = 0; + break; + } + + n = 0; + do { + tag = ber_get_stringbv( ber, &bv, b->option ); + if ( tag == LBER_DEFAULT ) { + goto failed; + } + + /* store my result */ + switch (b->choice) { + case ChArray: + res.ca[n] = bv.bv_val; + break; + case BvArray: + res.ba[n] = bv; + break; + case BvVec: + bvp = ber_memalloc_x( sizeof( struct berval ), + ber->ber_memctx ); + if ( !bvp ) { + ber_memfree_x( bv.bv_val, ber->ber_memctx ); + goto failed; + } + res.bv[n] = bvp; + *bvp = bv; + break; + case BvOff: + *(struct berval *)(res.bo + tot_size) = bv; + tot_size += siz; + break; + } + } while (++n < i); + return tag; + +failed: + if (b->choice != BvOff) { /* BvOff does not have LBER_BV_ALLOC set */ + while (--n >= 0) { + switch(b->choice) { + case ChArray: + ber_memfree_x(res.ca[n], ber->ber_memctx); + break; + case BvArray: + ber_memfree_x(res.ba[n].bv_val, ber->ber_memctx); + break; + case BvVec: + ber_memfree_x(res.bv[n]->bv_val, ber->ber_memctx); + ber_memfree_x(res.bv[n], ber->ber_memctx); + break; + default: + break; + } + } + } + ber_memfree_x(b->result, ber->ber_memctx); + b->result = NULL; + return LBER_DEFAULT; +} + +ber_tag_t +ber_get_stringbv( BerElement *ber, struct berval *bv, int option ) +{ + ber_tag_t tag; + char *data; + + tag = ber_skip_element( ber, bv ); + if ( tag == LBER_DEFAULT || + (( option & LBER_BV_STRING ) && + bv->bv_len && memchr( bv->bv_val, 0, bv->bv_len - 1 ))) + { + bv->bv_val = NULL; + return LBER_DEFAULT; + } + + data = bv->bv_val; + if ( option & LBER_BV_ALLOC ) { + bv->bv_val = (char *) ber_memalloc_x( bv->bv_len + 1, + ber->ber_memctx ); + if ( bv->bv_val == NULL ) { + return LBER_DEFAULT; + } + + if ( bv->bv_len != 0 ) { + memcpy( bv->bv_val, data, bv->bv_len ); + } + data = bv->bv_val; + } + if ( !( option & LBER_BV_NOTERM )) + data[bv->bv_len] = '\0'; + + return tag; +} + +ber_tag_t +ber_get_stringbv_null( BerElement *ber, struct berval *bv, int option ) +{ + ber_tag_t tag; + char *data; + + tag = ber_skip_element( ber, bv ); + if ( tag == LBER_DEFAULT || bv->bv_len == 0 ) { + bv->bv_val = NULL; + return tag; + } + + if (( option & LBER_BV_STRING ) && + memchr( bv->bv_val, 0, bv->bv_len - 1 )) + { + bv->bv_val = NULL; + return LBER_DEFAULT; + } + + data = bv->bv_val; + if ( option & LBER_BV_ALLOC ) { + bv->bv_val = (char *) ber_memalloc_x( bv->bv_len + 1, + ber->ber_memctx ); + if ( bv->bv_val == NULL ) { + return LBER_DEFAULT; + } + + memcpy( bv->bv_val, data, bv->bv_len ); + data = bv->bv_val; + } + if ( !( option & LBER_BV_NOTERM )) + data[bv->bv_len] = '\0'; + + return tag; +} + +ber_tag_t +ber_get_stringa( BerElement *ber, char **buf ) +{ + BerValue bv; + ber_tag_t tag; + + assert( buf != NULL ); + + tag = ber_get_stringbv( ber, &bv, LBER_BV_ALLOC | LBER_BV_STRING ); + *buf = bv.bv_val; + + return tag; +} + +ber_tag_t +ber_get_stringa_null( BerElement *ber, char **buf ) +{ + BerValue bv; + ber_tag_t tag; + + assert( buf != NULL ); + + tag = ber_get_stringbv_null( ber, &bv, LBER_BV_ALLOC | LBER_BV_STRING ); + *buf = bv.bv_val; + + return tag; +} + +ber_tag_t +ber_get_stringal( BerElement *ber, struct berval **bv ) +{ + ber_tag_t tag; + + assert( ber != NULL ); + assert( bv != NULL ); + + *bv = (struct berval *) ber_memalloc_x( sizeof(struct berval), + ber->ber_memctx ); + if ( *bv == NULL ) { + return LBER_DEFAULT; + } + + tag = ber_get_stringbv( ber, *bv, LBER_BV_ALLOC ); + if ( tag == LBER_DEFAULT ) { + ber_memfree_x( *bv, ber->ber_memctx ); + *bv = NULL; + } + return tag; +} + +ber_tag_t +ber_get_bitstringa( + BerElement *ber, + char **buf, + ber_len_t *blen ) +{ + ber_tag_t tag; + struct berval data; + unsigned char unusedbits; + + assert( buf != NULL ); + assert( blen != NULL ); + + if ( (tag = ber_skip_element( ber, &data )) == LBER_DEFAULT ) { + goto fail; + } + + if ( --data.bv_len > (ber_len_t)-1 / 8 ) { + goto fail; + } + unusedbits = *(unsigned char *) data.bv_val++; + if ( unusedbits > 7 ) { + goto fail; + } + + if ( memchr( data.bv_val, 0, data.bv_len )) { + goto fail; + } + + *buf = (char *) ber_memalloc_x( data.bv_len, ber->ber_memctx ); + if ( *buf == NULL ) { + return LBER_DEFAULT; + } + memcpy( *buf, data.bv_val, data.bv_len ); + + *blen = data.bv_len * 8 - unusedbits; + return tag; + + fail: + *buf = NULL; + return LBER_DEFAULT; +} + +ber_tag_t +ber_get_null( BerElement *ber ) +{ + ber_len_t len; + ber_tag_t tag = ber_skip_tag( ber, &len ); + + return( len == 0 ? tag : LBER_DEFAULT ); +} + +ber_tag_t +ber_get_boolean( + BerElement *ber, + ber_int_t *boolval ) +{ + return ber_get_int( ber, boolval ); +} + +ber_tag_t +ber_first_element( + BerElement *ber, + ber_len_t *len, + char **last ) +{ + assert( last != NULL ); + + /* skip the sequence header, use the len to mark where to stop */ + if ( ber_skip_tag( ber, len ) == LBER_DEFAULT ) { + *last = NULL; + return LBER_DEFAULT; + } + + *last = ber->ber_ptr + *len; + + if ( *len == 0 ) { + return LBER_DEFAULT; + } + + return ber_peek_tag( ber, len ); +} + +ber_tag_t +ber_next_element( + BerElement *ber, + ber_len_t *len, + LDAP_CONST char *last ) +{ + assert( ber != NULL ); + assert( last != NULL ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_ptr >= last ) { + return LBER_DEFAULT; + } + + return ber_peek_tag( ber, len ); +} + +/* VARARGS */ +ber_tag_t +ber_scanf ( BerElement *ber, + LDAP_CONST char *fmt, + ... ) +{ + va_list ap; + LDAP_CONST char *fmt_reset; + char *s, **ss, ***sss; + struct berval data, *bval, **bvp, ***bvpp; + ber_int_t *i; + ber_len_t *l; + ber_tag_t *t; + ber_tag_t rc; + ber_len_t len; + + va_start( ap, fmt ); + + assert( ber != NULL ); + assert( fmt != NULL ); + assert( LBER_VALID( ber ) ); + + fmt_reset = fmt; + + if ( ber->ber_debug & (LDAP_DEBUG_TRACE|LDAP_DEBUG_BER)) { + ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug, + "ber_scanf fmt (%s) ber:\n", fmt ); + ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 ); + } + + for ( rc = 0; *fmt && rc != LBER_DEFAULT; fmt++ ) { + /* When this is modified, remember to update + * the error-cleanup code below accordingly. */ + switch ( *fmt ) { + case '!': { /* Hook */ + BERDecodeCallback *f; + void *p; + + f = va_arg( ap, BERDecodeCallback * ); + p = va_arg( ap, void * ); + + rc = (*f)( ber, p, 0 ); + } break; + + case 'a': /* octet string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + rc = ber_get_stringa( ber, ss ); + break; + + case 'A': /* octet string - allocate storage as needed, + * but return NULL if len == 0 */ + ss = va_arg( ap, char ** ); + rc = ber_get_stringa_null( ber, ss ); + break; + + case 'b': /* boolean */ + i = va_arg( ap, ber_int_t * ); + rc = ber_get_boolean( ber, i ); + break; + + case 'B': /* bit string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + l = va_arg( ap, ber_len_t * ); /* for length, in bits */ + rc = ber_get_bitstringa( ber, ss, l ); + break; + + case 'e': /* enumerated */ + case 'i': /* integer */ + i = va_arg( ap, ber_int_t * ); + rc = ber_get_int( ber, i ); + break; + + case 'l': /* length of next item */ + l = va_arg( ap, ber_len_t * ); + rc = ber_peek_tag( ber, l ); + break; + + case 'm': /* octet string in berval, in-place */ + bval = va_arg( ap, struct berval * ); + rc = ber_get_stringbv( ber, bval, 0 ); + break; + + case 'M': /* bvoffarray - must include address of + * a record len, and record offset. + * number of records will be returned thru + * len ptr on finish. parsed in-place. + */ + { + bgbvr cookie = { BvOff, 0 }; + bvp = va_arg( ap, struct berval ** ); + l = va_arg( ap, ber_len_t * ); + cookie.siz = *l; + cookie.off = va_arg( ap, ber_len_t ); + rc = ber_get_stringbvl( ber, &cookie ); + *bvp = cookie.result; + *l = cookie.siz; + break; + } + + case 'n': /* null */ + rc = ber_get_null( ber ); + break; + + case 'o': /* octet string in a supplied berval */ + bval = va_arg( ap, struct berval * ); + rc = ber_get_stringbv( ber, bval, LBER_BV_ALLOC ); + break; + + case 'O': /* octet string - allocate & include length */ + bvp = va_arg( ap, struct berval ** ); + rc = ber_get_stringal( ber, bvp ); + break; + + case 's': /* octet string - in a buffer */ + s = va_arg( ap, char * ); + l = va_arg( ap, ber_len_t * ); + rc = ber_get_stringb( ber, s, l ); + break; + + case 't': /* tag of next item */ + t = va_arg( ap, ber_tag_t * ); + *t = rc = ber_peek_tag( ber, &len ); + break; + + case 'T': /* skip tag of next item */ + t = va_arg( ap, ber_tag_t * ); + *t = rc = ber_skip_tag( ber, &len ); + break; + + case 'v': /* sequence of strings */ + { + bgbvr cookie = { + ChArray, LBER_BV_ALLOC | LBER_BV_STRING, sizeof( char * ) + }; + rc = ber_get_stringbvl( ber, &cookie ); + *(va_arg( ap, char *** )) = cookie.result; + break; + } + + case 'V': /* sequence of strings + lengths */ + { + bgbvr cookie = { + BvVec, LBER_BV_ALLOC, sizeof( struct berval * ) + }; + rc = ber_get_stringbvl( ber, &cookie ); + *(va_arg( ap, struct berval *** )) = cookie.result; + break; + } + + case 'W': /* bvarray */ + { + bgbvr cookie = { + BvArray, LBER_BV_ALLOC, sizeof( struct berval ) + }; + rc = ber_get_stringbvl( ber, &cookie ); + *(va_arg( ap, struct berval ** )) = cookie.result; + break; + } + + case 'x': /* skip the next element - whatever it is */ + rc = ber_skip_element( ber, &data ); + break; + + case '{': /* begin sequence */ + case '[': /* begin set */ + switch ( fmt[1] ) { + case 'v': case 'V': case 'W': case 'M': + break; + default: + rc = ber_skip_tag( ber, &len ); + break; + } + break; + + case '}': /* end sequence */ + case ']': /* end set */ + break; + + default: + if( ber->ber_debug ) { + ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, + "ber_scanf: unknown fmt %c\n", *fmt ); + } + rc = LBER_DEFAULT; + break; + } + } + + va_end( ap ); + + if ( rc == LBER_DEFAULT ) { + /* + * Error. Reclaim malloced memory that was given to the caller. + * Set allocated pointers to NULL, "data length" outvalues to 0. + */ + va_start( ap, fmt ); + + for ( ; fmt_reset < fmt; fmt_reset++ ) { + switch ( *fmt_reset ) { + case '!': { /* Hook */ + BERDecodeCallback *f; + void *p; + + f = va_arg( ap, BERDecodeCallback * ); + p = va_arg( ap, void * ); + + (void) (*f)( ber, p, 1 ); + } break; + + case 'a': /* octet string - allocate storage as needed */ + case 'A': + ss = va_arg( ap, char ** ); + ber_memfree_x( *ss, ber->ber_memctx ); + *ss = NULL; + break; + + case 'b': /* boolean */ + case 'e': /* enumerated */ + case 'i': /* integer */ + (void) va_arg( ap, ber_int_t * ); + break; + + case 'l': /* length of next item */ + *(va_arg( ap, ber_len_t * )) = 0; + break; + + case 'm': /* berval in-place */ + bval = va_arg( ap, struct berval * ); + BER_BVZERO( bval ); + break; + + case 'M': /* BVoff array in-place */ + bvp = va_arg( ap, struct berval ** ); + ber_memfree_x( *bvp, ber->ber_memctx ); + *bvp = NULL; + *(va_arg( ap, ber_len_t * )) = 0; + (void) va_arg( ap, ber_len_t ); + break; + + case 'o': /* octet string in a supplied berval */ + bval = va_arg( ap, struct berval * ); + ber_memfree_x( bval->bv_val, ber->ber_memctx ); + BER_BVZERO( bval ); + break; + + case 'O': /* octet string - allocate & include length */ + bvp = va_arg( ap, struct berval ** ); + ber_bvfree_x( *bvp, ber->ber_memctx ); + *bvp = NULL; + break; + + case 's': /* octet string - in a buffer */ + (void) va_arg( ap, char * ); + *(va_arg( ap, ber_len_t * )) = 0; + break; + + case 't': /* tag of next item */ + case 'T': /* skip tag of next item */ + (void) va_arg( ap, ber_tag_t * ); + break; + + case 'B': /* bit string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + ber_memfree_x( *ss, ber->ber_memctx ); + *ss = NULL; + *(va_arg( ap, ber_len_t * )) = 0; /* for length, in bits */ + break; + + case 'v': /* sequence of strings */ + sss = va_arg( ap, char *** ); + ber_memvfree_x( (void **) *sss, ber->ber_memctx ); + *sss = NULL; + break; + + case 'V': /* sequence of strings + lengths */ + bvpp = va_arg( ap, struct berval *** ); + ber_bvecfree_x( *bvpp, ber->ber_memctx ); + *bvpp = NULL; + break; + + case 'W': /* BerVarray */ + bvp = va_arg( ap, struct berval ** ); + ber_bvarray_free_x( *bvp, ber->ber_memctx ); + *bvp = NULL; + break; + + case 'n': /* null */ + case 'x': /* skip the next element - whatever it is */ + case '{': /* begin sequence */ + case '[': /* begin set */ + case '}': /* end sequence */ + case ']': /* end set */ + break; + + default: + /* format should be good */ + assert( 0 ); + } + } + + va_end( ap ); + } + + return rc; +} diff --git a/libraries/liblber/dtest.c b/libraries/liblber/dtest.c new file mode 100644 index 0000000..2595252 --- /dev/null +++ b/libraries/liblber/dtest.c @@ -0,0 +1,121 @@ +/* dtest.c - lber decoding test program */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/socket.h> +#include <ac/unistd.h> +#include <ac/errno.h> + +#ifdef HAVE_CONSOLE_H +#include <console.h> +#endif + +#include <lber.h> + +static void usage( const char *name ) +{ + fprintf( stderr, "usage: %s fmt\n", name ); +} + +int +main( int argc, char **argv ) +{ + char *s; + + ber_tag_t tag; + ber_len_t len; + + BerElement *ber; + Sockbuf *sb; + int fd; + + /* enable debugging */ + int ival = -1; + ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ival ); + + if ( argc < 2 ) { + usage( argv[0] ); + return( EXIT_FAILURE ); + } + +#ifdef HAVE_CONSOLE_H + ccommand( &argv ); + cshow( stdout ); +#endif + + sb = ber_sockbuf_alloc(); + fd = fileno( stdin ); + ber_sockbuf_add_io( sb, &ber_sockbuf_io_fd, LBER_SBIOD_LEVEL_PROVIDER, + (void *)&fd ); + + ber = ber_alloc_t(LBER_USE_DER); + if( ber == NULL ) { + perror( "ber_alloc_t" ); + return( EXIT_FAILURE ); + } + + for (;;) { + tag = ber_get_next( sb, &len, ber); + if( tag != LBER_ERROR ) break; + + if( errno == EWOULDBLOCK ) continue; + if( errno == EAGAIN ) continue; + + perror( "ber_get_next" ); + return( EXIT_FAILURE ); + } + + printf("decode: message tag 0x%lx and length %ld\n", + (unsigned long) tag, (long) len ); + + for( s = argv[1]; *s; s++ ) { + char buf[128]; + char fmt[2]; + fmt[0] = *s; + fmt[1] = '\0'; + + printf("decode: format %s\n", fmt ); + len = sizeof(buf); + tag = ber_scanf( ber, fmt, &buf[0], &len ); + + if( tag == LBER_ERROR ) { + perror( "ber_scanf" ); + return( EXIT_FAILURE ); + } + } + + ber_sockbuf_free( sb ); + return( EXIT_SUCCESS ); +} diff --git a/libraries/liblber/encode.c b/libraries/liblber/encode.c new file mode 100644 index 0000000..7ce5901 --- /dev/null +++ b/libraries/liblber/encode.c @@ -0,0 +1,651 @@ +/* encode.c - ber output encoding routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <ctype.h> +#include <limits.h> +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/stdarg.h> +#include <ac/socket.h> +#include <ac/string.h> + +#include "lber-int.h" + + +#define OCTET_SIZE(type) ((ber_len_t) (sizeof(type)*CHAR_BIT + 7) / 8) +#define TAGBUF_SIZE OCTET_SIZE(ber_tag_t) +#define LENBUF_SIZE (1 + OCTET_SIZE(ber_len_t)) +#define HEADER_SIZE (TAGBUF_SIZE + LENBUF_SIZE) + +/* + * BER element size constrains: + * + * - We traditionally support a length of max 0xffffffff. However + * some functions return an int length so that is their max. + * MAXINT_BERSIZE is the max for those functions. + * + * - MAXINT_BERSIZE must fit in MAXINT_BERSIZE_OCTETS octets. + * + * - sizeof(ber_elem_size_t) is normally MAXINT_BERSIZE_OCTETS: + * Big enough for MAXINT_BERSIZE, but not more. (Larger wastes + * space in the working encoding and DER encoding of a sequence + * or set. Smaller further limits sizes near a sequence/set.) + * + * ber_len_t is mostly unrelated to this. Which may be for the best, + * since it is also used for lengths of data that are never encoded. + */ +#define MAXINT_BERSIZE \ + (INT_MAX>0xffffffffUL ? (ber_len_t) 0xffffffffUL : INT_MAX-HEADER_SIZE) +#define MAXINT_BERSIZE_OCTETS 4 +typedef ber_uint_t ber_elem_size_t; /* normally 32 bits */ + + +/* Prepend tag to ptr, which points to the end of a tag buffer */ +static unsigned char * +ber_prepend_tag( unsigned char *ptr, ber_tag_t tag ) +{ + do { + *--ptr = (unsigned char) tag & 0xffU; + } while ( (tag >>= 8) != 0 ); + + return ptr; +} + +/* Prepend ber length to ptr, which points to the end of a length buffer */ +static unsigned char * +ber_prepend_len( unsigned char *ptr, ber_len_t len ) +{ + /* + * short len if it's less than 128 - one byte giving the len, + * with bit 8 0. + * long len otherwise - one byte with bit 8 set, giving the + * length of the length, followed by the length itself. + */ + + *--ptr = (unsigned char) len & 0xffU; + + if ( len >= 0x80 ) { + unsigned char *endptr = ptr--; + + while ( (len >>= 8) != 0 ) { + *ptr-- = (unsigned char) len & 0xffU; + } + *ptr = (unsigned char) (endptr - ptr) + 0x80U; + } + + return ptr; +} + +/* out->bv_len should be the buffer size on input */ +int +ber_encode_oid( BerValue *in, BerValue *out ) +{ + unsigned char *der; + unsigned long val1, val; + int i, j, len; + char *ptr, *end, *inend; + + assert( in != NULL ); + assert( out != NULL ); + + if ( !out->bv_val || out->bv_len < in->bv_len/2 ) + return -1; + + der = (unsigned char *) out->bv_val; + ptr = in->bv_val; + inend = ptr + in->bv_len; + + /* OIDs start with <0-1>.<0-39> or 2.<any>, DER-encoded 40*val1+val2 */ + if ( !isdigit( (unsigned char) *ptr )) return -1; + val1 = strtoul( ptr, &end, 10 ); + if ( end == ptr || val1 > 2 ) return -1; + if ( *end++ != '.' || !isdigit( (unsigned char) *end )) return -1; + val = strtoul( end, &ptr, 10 ); + if ( ptr == end ) return -1; + if ( val > (val1 < 2 ? 39 : LBER_OID_COMPONENT_MAX - 80) ) return -1; + val += val1 * 40; + + for (;;) { + if ( ptr > inend ) return -1; + + /* Write the OID component little-endian, then reverse it */ + len = 0; + do { + der[len++] = (val & 0xff) | 0x80; + } while ( (val >>= 7) != 0 ); + der[0] &= 0x7f; + for ( i = 0, j = len; i < --j; i++ ) { + unsigned char tmp = der[i]; + der[i] = der[j]; + der[j] = tmp; + } + der += len; + + if ( ptr == inend ) + break; + + if ( *ptr++ != '.' ) return -1; + if ( !isdigit( (unsigned char) *ptr )) return -1; + val = strtoul( ptr, &end, 10 ); + if ( end == ptr || val > LBER_OID_COMPONENT_MAX ) return -1; + ptr = end; + } + + out->bv_len = (char *)der - out->bv_val; + return 0; +} + +static int +ber_put_int_or_enum( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) +{ + ber_uint_t unum; + unsigned char sign, data[TAGBUF_SIZE+1 + OCTET_SIZE(ber_int_t)], *ptr; + + sign = 0; + unum = num; /* Bit fiddling should be done with unsigned values */ + if ( num < 0 ) { + sign = 0xffU; + unum = ~unum; + } + for ( ptr = &data[sizeof(data) - 1] ;; unum >>= 8 ) { + *ptr-- = (sign ^ (unsigned char) unum) & 0xffU; + if ( unum < 0x80 ) /* top bit at *ptr is sign bit */ + break; + } + + *ptr = (unsigned char) (&data[sizeof(data) - 1] - ptr); /* length */ + ptr = ber_prepend_tag( ptr, tag ); + + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); +} + +int +ber_put_enum( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_ENUMERATED; + } + + return ber_put_int_or_enum( ber, num, tag ); +} + +int +ber_put_int( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_INTEGER; + } + + return ber_put_int_or_enum( ber, num, tag ); +} + +int +ber_put_ostring( + BerElement *ber, + LDAP_CONST char *str, + ber_len_t len, + ber_tag_t tag ) +{ + int rc; + unsigned char header[HEADER_SIZE], *ptr; + + if ( tag == LBER_DEFAULT ) { + tag = LBER_OCTETSTRING; + } + + if ( len > MAXINT_BERSIZE ) { + return -1; + } + + ptr = ber_prepend_len( &header[sizeof(header)], len ); + ptr = ber_prepend_tag( ptr, tag ); + + rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 ); + if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) { + /* length(tag + length + contents) */ + return rc + (int) len; + } + + return -1; +} + +int +ber_put_berval( + BerElement *ber, + struct berval *bv, + ber_tag_t tag ) +{ + if( bv == NULL || bv->bv_len == 0 ) { + return ber_put_ostring( ber, "", (ber_len_t) 0, tag ); + } + + return ber_put_ostring( ber, bv->bv_val, bv->bv_len, tag ); +} + +int +ber_put_string( + BerElement *ber, + LDAP_CONST char *str, + ber_tag_t tag ) +{ + assert( str != NULL ); + + return ber_put_ostring( ber, str, strlen( str ), tag ); +} + +int +ber_put_bitstring( + BerElement *ber, + LDAP_CONST char *str, + ber_len_t blen /* in bits */, + ber_tag_t tag ) +{ + int rc; + ber_len_t len; + unsigned char unusedbits, header[HEADER_SIZE + 1], *ptr; + + if ( tag == LBER_DEFAULT ) { + tag = LBER_BITSTRING; + } + + unusedbits = (unsigned char) -blen & 7; + len = blen / 8 + (unusedbits != 0); /* (blen+7)/8 without overflow */ + if ( len >= MAXINT_BERSIZE ) { + return -1; + } + + header[sizeof(header) - 1] = unusedbits; + ptr = ber_prepend_len( &header[sizeof(header) - 1], len + 1 ); + ptr = ber_prepend_tag( ptr, tag ); + + rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 ); + if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) { + /* length(tag + length + unused bit count + bitstring) */ + return rc + (int) len; + } + + return -1; +} + +int +ber_put_null( BerElement *ber, ber_tag_t tag ) +{ + unsigned char data[TAGBUF_SIZE + 1], *ptr; + + if ( tag == LBER_DEFAULT ) { + tag = LBER_NULL; + } + + data[sizeof(data) - 1] = 0; /* length */ + ptr = ber_prepend_tag( &data[sizeof(data) - 1], tag ); + + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); +} + +int +ber_put_boolean( + BerElement *ber, + ber_int_t boolval, + ber_tag_t tag ) +{ + unsigned char data[TAGBUF_SIZE + 2], *ptr; + + if ( tag == LBER_DEFAULT ) + tag = LBER_BOOLEAN; + + data[sizeof(data) - 1] = boolval ? 0xff : 0; + data[sizeof(data) - 2] = 1; /* length */ + ptr = ber_prepend_tag( &data[sizeof(data) - 2], tag ); + + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); +} + + +/* Max number of length octets in a sequence or set, normally 5 */ +#define SOS_LENLEN (1 + (sizeof(ber_elem_size_t) > MAXINT_BERSIZE_OCTETS ? \ + (ber_len_t) sizeof(ber_elem_size_t) : MAXINT_BERSIZE_OCTETS)) + +/* Header of incomplete sequence or set */ +typedef struct seqorset_header { + char xtagbuf[TAGBUF_SIZE + 1]; /* room for tag + len(tag or len) */ + union { + ber_elem_size_t offset; /* enclosing seqence/set */ + char padding[SOS_LENLEN-1]; /* for final length encoding */ + } next_sos; +# define SOS_TAG_END(header) ((unsigned char *) &(header).next_sos - 1) +} Seqorset_header; + +/* Start a sequence or set */ +static int +ber_start_seqorset( + BerElement *ber, + ber_tag_t tag ) +{ + /* + * Write the tag and SOS_LENLEN octets reserved for length, to ber. + * For now, length octets = (tag length, previous ber_sos_inner). + * + * Update ber_sos_inner and the write-cursor ber_sos_ptr. ber_ptr + * will not move until the outermost sequence or set is complete. + */ + + Seqorset_header header; + unsigned char *headptr; + ber_len_t taglen, headlen; + char *dest, **p; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_sos_ptr == NULL ) { /* outermost sequence/set? */ + header.next_sos.offset = 0; + p = &ber->ber_ptr; + } else { + if ( (ber_len_t) -1 > (ber_elem_size_t) -1 ) { + if ( ber->ber_sos_inner > (ber_elem_size_t) -1 ) + return -1; + } + header.next_sos.offset = ber->ber_sos_inner; + p = &ber->ber_sos_ptr; + } + headptr = ber_prepend_tag( SOS_TAG_END(header), tag ); + *SOS_TAG_END(header) = taglen = SOS_TAG_END(header) - headptr; + headlen = taglen + SOS_LENLEN; + + /* As ber_write(,headptr,headlen,) except update ber_sos_ptr, not *p */ + if ( headlen > (ber_len_t) (ber->ber_end - *p) ) { + if ( ber_realloc( ber, headlen ) != 0 ) + return -1; + } + dest = *p; + AC_MEMCPY( dest, headptr, headlen ); + ber->ber_sos_ptr = dest + headlen; + + ber->ber_sos_inner = dest + taglen - ber->ber_buf; + + /* + * Do not return taglen + SOS_LENLEN here - then ber_put_seqorset() + * should return lenlen - SOS_LENLEN + len, which can be < 0. + */ + return 0; +} + +int +ber_start_seq( BerElement *ber, ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_SEQUENCE; + } + + return ber_start_seqorset( ber, tag ); +} + +int +ber_start_set( BerElement *ber, ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_SET; + } + + return ber_start_seqorset( ber, tag ); +} + +/* End a sequence or set */ +static int +ber_put_seqorset( BerElement *ber ) +{ + Seqorset_header header; + unsigned char *lenptr; /* length octets in the sequence/set */ + ber_len_t len; /* length(contents) */ + ber_len_t xlen; /* len + length(length) */ + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_sos_ptr == NULL ) return -1; + + lenptr = (unsigned char *) ber->ber_buf + ber->ber_sos_inner; + xlen = ber->ber_sos_ptr - (char *) lenptr; + if ( xlen > MAXINT_BERSIZE + SOS_LENLEN ) { + return -1; + } + + /* Extract sequence/set information from length octets */ + memcpy( SOS_TAG_END(header), lenptr, SOS_LENLEN ); + + /* Store length, and close gap of leftover reserved length octets */ + len = xlen - SOS_LENLEN; + if ( !(ber->ber_options & LBER_USE_DER) ) { + int i; + lenptr[0] = SOS_LENLEN - 1 + 0x80; /* length(length)-1 */ + for( i = SOS_LENLEN; --i > 0; len >>= 8 ) { + lenptr[i] = len & 0xffU; + } + } else { + unsigned char *p = ber_prepend_len( lenptr + SOS_LENLEN, len ); + ber_len_t unused = p - lenptr; + if ( unused != 0 ) { + /* length(length) < the reserved SOS_LENLEN bytes */ + xlen -= unused; + AC_MEMCPY( lenptr, p, xlen ); + ber->ber_sos_ptr = (char *) lenptr + xlen; + } + } + + ber->ber_sos_inner = header.next_sos.offset; + if ( header.next_sos.offset == 0 ) { /* outermost sequence/set? */ + /* The ber_ptr is at the set/seq start - move it to the end */ + ber->ber_ptr = ber->ber_sos_ptr; + ber->ber_sos_ptr = NULL; + } + + return xlen + *SOS_TAG_END(header); /* lenlen + len + taglen */ +} + +int +ber_put_seq( BerElement *ber ) +{ + return ber_put_seqorset( ber ); +} + +int +ber_put_set( BerElement *ber ) +{ + return ber_put_seqorset( ber ); +} + +/* N tag */ +static ber_tag_t lber_int_null = 0; + +/* VARARGS */ +int +ber_printf( BerElement *ber, LDAP_CONST char *fmt, ... ) +{ + va_list ap; + char *s, **ss; + struct berval *bv, **bvp; + int rc; + ber_int_t i; + ber_len_t len; + + assert( ber != NULL ); + assert( fmt != NULL ); + assert( LBER_VALID( ber ) ); + + va_start( ap, fmt ); + + for ( rc = 0; *fmt && rc != -1; fmt++ ) { + switch ( *fmt ) { + case '!': { /* hook */ + BEREncodeCallback *f; + void *p; + + ber->ber_usertag = 0; + + f = va_arg( ap, BEREncodeCallback * ); + p = va_arg( ap, void * ); + rc = (*f)( ber, p ); + + if ( ber->ber_usertag ) { + goto next; + } + } break; + + case 'b': /* boolean */ + i = va_arg( ap, ber_int_t ); + rc = ber_put_boolean( ber, i, ber->ber_tag ); + break; + + case 'i': /* int */ + i = va_arg( ap, ber_int_t ); + rc = ber_put_int( ber, i, ber->ber_tag ); + break; + + case 'e': /* enumeration */ + i = va_arg( ap, ber_int_t ); + rc = ber_put_enum( ber, i, ber->ber_tag ); + break; + + case 'n': /* null */ + rc = ber_put_null( ber, ber->ber_tag ); + break; + + case 'N': /* Debug NULL */ + rc = 0; + if( lber_int_null != 0 ) { + /* Insert NULL to ensure peer ignores unknown tags */ + rc = ber_put_null( ber, lber_int_null ); + } + break; + + case 'o': /* octet string (non-null terminated) */ + s = va_arg( ap, char * ); + len = va_arg( ap, ber_len_t ); + rc = ber_put_ostring( ber, s, len, ber->ber_tag ); + break; + + case 'O': /* berval octet string */ + bv = va_arg( ap, struct berval * ); + if( bv == NULL ) break; + rc = ber_put_berval( ber, bv, ber->ber_tag ); + break; + + case 's': /* string */ + s = va_arg( ap, char * ); + rc = ber_put_string( ber, s, ber->ber_tag ); + break; + + case 'B': /* bit string */ + case 'X': /* bit string (deprecated) */ + s = va_arg( ap, char * ); + len = va_arg( ap, ber_len_t ); /* in bits */ + rc = ber_put_bitstring( ber, s, len, ber->ber_tag ); + break; + + case 't': /* tag for the next element */ + ber->ber_tag = va_arg( ap, ber_tag_t ); + goto next; + + case 'v': /* vector of strings */ + if ( (ss = va_arg( ap, char ** )) == NULL ) + break; + for ( i = 0; ss[i] != NULL; i++ ) { + if ( (rc = ber_put_string( ber, ss[i], + ber->ber_tag )) == -1 ) + break; + } + break; + + case 'V': /* sequences of strings + lengths */ + if ( (bvp = va_arg( ap, struct berval ** )) == NULL ) + break; + for ( i = 0; bvp[i] != NULL; i++ ) { + if ( (rc = ber_put_berval( ber, bvp[i], + ber->ber_tag )) == -1 ) + break; + } + break; + + case 'W': /* BerVarray */ + if ( (bv = va_arg( ap, BerVarray )) == NULL ) + break; + for ( i = 0; bv[i].bv_val != NULL; i++ ) { + if ( (rc = ber_put_berval( ber, &bv[i], + ber->ber_tag )) == -1 ) + break; + } + break; + + case '{': /* begin sequence */ + rc = ber_start_seq( ber, ber->ber_tag ); + break; + + case '}': /* end sequence */ + rc = ber_put_seqorset( ber ); + break; + + case '[': /* begin set */ + rc = ber_start_set( ber, ber->ber_tag ); + break; + + case ']': /* end set */ + rc = ber_put_seqorset( ber ); + break; + + default: + if( ber->ber_debug ) { + ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, + "ber_printf: unknown fmt %c\n", *fmt ); + } + rc = -1; + break; + } + + ber->ber_tag = LBER_DEFAULT; + next:; + } + + va_end( ap ); + + return rc; +} diff --git a/libraries/liblber/etest.c b/libraries/liblber/etest.c new file mode 100644 index 0000000..e34cb92 --- /dev/null +++ b/libraries/liblber/etest.c @@ -0,0 +1,181 @@ +/* etest.c - lber encoding test program */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_CONSOLE_H +#include <console.h> +#endif /* HAVE_CONSOLE_H */ + +#include "lber.h" + +static void usage( const char *name ) +{ + fprintf( stderr, "usage: %s fmtstring\n", name ); +} + +static char* getbuf( void ) { + char *p; + static char buf[1024]; + + if ( fgets( buf, sizeof(buf), stdin ) == NULL ) return NULL; + + if ( (p = strchr( buf, '\n' )) != NULL ) *p = '\0'; + + return buf; +} + +int +main( int argc, char **argv ) +{ + char *s; + int tag; + + int fd, rc; + BerElement *ber; + Sockbuf *sb; + + /* enable debugging */ + int ival = -1; + ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ival ); + + if ( argc < 2 ) { + usage( argv[0] ); + return( EXIT_FAILURE ); + } + +#ifdef HAVE_CONSOLE_H + ccommand( &argv ); + cshow( stdout ); + + if (( fd = open( "lber-test", O_WRONLY|O_CREAT|O_TRUNC|O_BINARY )) + < 0 ) { + perror( "open" ); + return( EXIT_FAILURE ); + } + +#else + fd = fileno(stdout); +#endif + + sb = ber_sockbuf_alloc(); + ber_sockbuf_add_io( sb, &ber_sockbuf_io_fd, LBER_SBIOD_LEVEL_PROVIDER, + (void *)&fd ); + + if( sb == NULL ) { + perror( "ber_sockbuf_alloc_fd" ); + return( EXIT_FAILURE ); + } + + if ( (ber = ber_alloc_t( LBER_USE_DER )) == NULL ) { + perror( "ber_alloc" ); + return( EXIT_FAILURE ); + } + + fprintf(stderr, "encode: start\n" ); + if( ber_printf( ber, "{" /*}*/ ) ) { + perror( "ber_printf {" /*}*/ ); + return( EXIT_FAILURE ); + } + + for ( s = argv[1]; *s; s++ ) { + char *buf; + char fmt[2]; + + fmt[0] = *s; + fmt[1] = '\0'; + + fprintf(stderr, "encode: %s\n", fmt ); + switch ( *s ) { + case 'i': /* int */ + case 'b': /* boolean */ + case 'e': /* enumeration */ + buf = getbuf(); + rc = ber_printf( ber, fmt, atoi(buf) ); + break; + + case 'n': /* null */ + case '{': /* begin sequence */ + case '}': /* end sequence */ + case '[': /* begin set */ + case ']': /* end set */ + rc = ber_printf( ber, fmt ); + break; + + case 'o': /* octet string (non-null terminated) */ + case 'B': /* bit string */ + buf = getbuf(); + rc = ber_printf( ber, fmt, buf, strlen(buf) ); + break; + + case 's': /* string */ + buf = getbuf(); + rc = ber_printf( ber, fmt, buf ); + break; + case 't': /* tag for the next element */ + buf = getbuf(); + tag = atoi(buf); + rc = ber_printf( ber, fmt, tag ); + break; + + default: + fprintf( stderr, "encode: unknown fmt %c\n", *fmt ); + rc = -1; + break; + } + + if( rc == -1 ) { + perror( "ber_printf" ); + return( EXIT_FAILURE ); + } + } + + fprintf(stderr, "encode: end\n" ); + if( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + perror( /*{*/ "ber_printf }" ); + return( EXIT_FAILURE ); + } + + if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) == -1 ) { + perror( "ber_flush2" ); + return( EXIT_FAILURE ); + } + + ber_sockbuf_free( sb ); + return( EXIT_SUCCESS ); +} diff --git a/libraries/liblber/idtest.c b/libraries/liblber/idtest.c new file mode 100644 index 0000000..9f0522d --- /dev/null +++ b/libraries/liblber/idtest.c @@ -0,0 +1,87 @@ +/* idtest.c - ber decoding test program using isode libraries */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#ifdef HAVE_PSAP_H +#include <psap.h> +#include <quipu/attr.h> +#endif + +int +main( int argc, char **argv ) +{ +#ifdef HAVE_PSAP_H + PE pe; + PS psin, psout, pserr; + + /* read the pe from standard in */ + if ( (psin = ps_alloc( std_open )) == NULLPS ) { + perror( "ps_alloc" ); + exit( EXIT_FAILURE ); + } + if ( std_setup( psin, stdin ) == NOTOK ) { + perror( "std_setup" ); + exit( EXIT_FAILURE ); + } + /* write the pe to standard out */ + if ( (psout = ps_alloc( std_open )) == NULLPS ) { + perror( "ps_alloc" ); + exit( EXIT_FAILURE ); + } + if ( std_setup( psout, stdout ) == NOTOK ) { + perror( "std_setup" ); + exit( EXIT_FAILURE ); + } + /* pretty print it to standard error */ + if ( (pserr = ps_alloc( std_open )) == NULLPS ) { + perror( "ps_alloc" ); + exit( EXIT_FAILURE ); + } + if ( std_setup( pserr, stderr ) == NOTOK ) { + perror( "std_setup" ); + exit( EXIT_FAILURE ); + } + + while ( (pe = ps2pe( psin )) != NULLPE ) { + pe2pl( pserr, pe ); + pe2ps( psout, pe ); + } + + exit( EXIT_SUCCESS ); +#else + fprintf(stderr, "requires ISODE X.500 distribution.\n"); + return( EXIT_FAILURE ); +#endif +} diff --git a/libraries/liblber/io.c b/libraries/liblber/io.c new file mode 100644 index 0000000..65f3cba --- /dev/null +++ b/libraries/liblber/io.c @@ -0,0 +1,725 @@ +/* io.c - ber general i/o routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#include "lber-int.h" +#include "ldap_log.h" + +ber_slen_t +ber_skip_data( + BerElement *ber, + ber_len_t len ) +{ + ber_len_t actuallen, nleft; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + nleft = ber_pvt_ber_remaining( ber ); + actuallen = nleft < len ? nleft : len; + ber->ber_ptr += actuallen; + ber->ber_tag = *(unsigned char *)ber->ber_ptr; + + return( (ber_slen_t) actuallen ); +} + +/* + * Read from the ber buffer. The caller must maintain ber->ber_tag. + * Do not use to read whole tags. See ber_get_tag() and ber_skip_data(). + */ +ber_slen_t +ber_read( + BerElement *ber, + char *buf, + ber_len_t len ) +{ + ber_len_t actuallen, nleft; + + assert( ber != NULL ); + assert( buf != NULL ); + assert( LBER_VALID( ber ) ); + + nleft = ber_pvt_ber_remaining( ber ); + actuallen = nleft < len ? nleft : len; + + AC_MEMCPY( buf, ber->ber_ptr, actuallen ); + + ber->ber_ptr += actuallen; + + return( (ber_slen_t) actuallen ); +} + +/* + * Write to the ber buffer. + * Note that ber_start_seqorset/ber_put_seqorset() bypass ber_write(). + */ +ber_slen_t +ber_write( + BerElement *ber, + LDAP_CONST char *buf, + ber_len_t len, + int zero ) /* nonzero is unsupported from OpenLDAP 2.4.18 */ +{ + char **p; + + assert( ber != NULL ); + assert( buf != NULL ); + assert( LBER_VALID( ber ) ); + + if ( zero != 0 ) { + ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, "%s", + "ber_write: nonzero 4th argument not supported\n" ); + return( -1 ); + } + + p = ber->ber_sos_ptr == NULL ? &ber->ber_ptr : &ber->ber_sos_ptr; + if ( len > (ber_len_t) (ber->ber_end - *p) ) { + if ( ber_realloc( ber, len ) != 0 ) return( -1 ); + } + AC_MEMCPY( *p, buf, len ); + *p += len; + + return( (ber_slen_t) len ); +} + +/* Resize the ber buffer */ +int +ber_realloc( BerElement *ber, ber_len_t len ) +{ + ber_len_t total, offset, sos_offset, rw_offset; + char *buf; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + /* leave room for ber_flatten() to \0-terminate ber_buf */ + if ( ++len == 0 ) { + return( -1 ); + } + + total = ber_pvt_ber_total( ber ); + +#define LBER_EXBUFSIZ 4060 /* a few words less than 2^N for binary buddy */ +#if defined( LBER_EXBUFSIZ ) && LBER_EXBUFSIZ > 0 +# ifndef notdef + /* don't realloc by small amounts */ + total += len < LBER_EXBUFSIZ ? LBER_EXBUFSIZ : len; +# else + { /* not sure what value this adds. reduce fragmentation? */ + ber_len_t have = (total + (LBER_EXBUFSIZE - 1)) / LBER_EXBUFSIZ; + ber_len_t need = (len + (LBER_EXBUFSIZ - 1)) / LBER_EXBUFSIZ; + total = ( have + need ) * LBER_EXBUFSIZ; + } +# endif +#else + total += len; /* realloc just what's needed */ +#endif + + if ( total < len || total > (ber_len_t)-1 / 2 /* max ber_slen_t */ ) { + return( -1 ); + } + + buf = ber->ber_buf; + offset = ber->ber_ptr - buf; + sos_offset = ber->ber_sos_ptr ? ber->ber_sos_ptr - buf : 0; + /* if ber_sos_ptr != NULL, it is > ber_buf so that sos_offset > 0 */ + rw_offset = ber->ber_rwptr ? ber->ber_rwptr - buf : 0; + + buf = (char *) ber_memrealloc_x( buf, total, ber->ber_memctx ); + if ( buf == NULL ) { + return( -1 ); + } + + ber->ber_buf = buf; + ber->ber_end = buf + total; + ber->ber_ptr = buf + offset; + if ( sos_offset ) + ber->ber_sos_ptr = buf + sos_offset; + if ( ber->ber_rwptr ) + ber->ber_rwptr = buf + rw_offset; + + return( 0 ); +} + +void +ber_free_buf( BerElement *ber ) +{ + assert( LBER_VALID( ber ) ); + + if ( ber->ber_buf) ber_memfree_x( ber->ber_buf, ber->ber_memctx ); + + ber->ber_buf = NULL; + ber->ber_sos_ptr = NULL; + ber->ber_valid = LBER_UNINITIALIZED; +} + +void +ber_free( BerElement *ber, int freebuf ) +{ + if( ber == NULL ) { + LDAP_MEMORY_DEBUG_ASSERT( ber != NULL ); + return; + } + + if( freebuf ) ber_free_buf( ber ); + + ber_memfree_x( (char *) ber, ber->ber_memctx ); +} + +int +ber_flush( Sockbuf *sb, BerElement *ber, int freeit ) +{ + return ber_flush2( sb, ber, + freeit ? LBER_FLUSH_FREE_ON_SUCCESS + : LBER_FLUSH_FREE_NEVER ); +} + +int +ber_flush2( Sockbuf *sb, BerElement *ber, int freeit ) +{ + ber_len_t towrite; + ber_slen_t rc; + + assert( sb != NULL ); + assert( ber != NULL ); + assert( SOCKBUF_VALID( sb ) ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_rwptr == NULL ) { + ber->ber_rwptr = ber->ber_buf; + } + towrite = ber->ber_ptr - ber->ber_rwptr; + + if ( sb->sb_debug ) { + ber_log_printf( LDAP_DEBUG_TRACE, sb->sb_debug, + "ber_flush2: %ld bytes to sd %ld%s\n", + towrite, (long) sb->sb_fd, + ber->ber_rwptr != ber->ber_buf ? " (re-flush)" : "" ); + ber_log_bprint( LDAP_DEBUG_BER, sb->sb_debug, + ber->ber_rwptr, towrite ); + } + + while ( towrite > 0 ) { +#ifdef LBER_TRICKLE + sleep(1); + rc = ber_int_sb_write( sb, ber->ber_rwptr, 1 ); +#else + rc = ber_int_sb_write( sb, ber->ber_rwptr, towrite ); +#endif + if ( rc <= 0 ) { + if ( freeit & LBER_FLUSH_FREE_ON_ERROR ) ber_free( ber, 1 ); + return -1; + } + towrite -= rc; + ber->ber_rwptr += rc; + } + + if ( freeit & LBER_FLUSH_FREE_ON_SUCCESS ) ber_free( ber, 1 ); + + return 0; +} + +BerElement * +ber_alloc_t( int options ) +{ + BerElement *ber; + + ber = (BerElement *) LBER_CALLOC( 1, sizeof(BerElement) ); + + if ( ber == NULL ) { + return NULL; + } + + ber->ber_valid = LBER_VALID_BERELEMENT; + ber->ber_tag = LBER_DEFAULT; + ber->ber_options = options; + ber->ber_debug = ber_int_debug; + + assert( LBER_VALID( ber ) ); + return ber; +} + +BerElement * +ber_alloc( void ) /* deprecated */ +{ + return ber_alloc_t( 0 ); +} + +BerElement * +der_alloc( void ) /* deprecated */ +{ + return ber_alloc_t( LBER_USE_DER ); +} + +BerElement * +ber_dup( BerElement *ber ) +{ + BerElement *new; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( (new = ber_alloc_t( ber->ber_options )) == NULL ) { + return NULL; + } + + *new = *ber; + + assert( LBER_VALID( new ) ); + return( new ); +} + + +void +ber_init2( BerElement *ber, struct berval *bv, int options ) +{ + assert( ber != NULL ); + + (void) memset( (char *)ber, '\0', sizeof( BerElement )); + ber->ber_valid = LBER_VALID_BERELEMENT; + ber->ber_tag = LBER_DEFAULT; + ber->ber_options = (char) options; + ber->ber_debug = ber_int_debug; + + if ( bv != NULL ) { + ber->ber_buf = bv->bv_val; + ber->ber_ptr = ber->ber_buf; + ber->ber_end = ber->ber_buf + bv->bv_len; + } + + assert( LBER_VALID( ber ) ); +} + +/* OLD U-Mich ber_init() */ +void +ber_init_w_nullc( BerElement *ber, int options ) +{ + ber_init2( ber, NULL, options ); +} + +/* New C-API ber_init() */ +/* This function constructs a BerElement containing a copy +** of the data in the bv argument. +*/ +BerElement * +ber_init( struct berval *bv ) +{ + BerElement *ber; + + assert( bv != NULL ); + + if ( bv == NULL ) { + return NULL; + } + + ber = ber_alloc_t( 0 ); + + if( ber == NULL ) { + /* allocation failed */ + return NULL; + } + + /* copy the data */ + if ( ((ber_len_t) ber_write ( ber, bv->bv_val, bv->bv_len, 0 )) + != bv->bv_len ) + { + /* write failed, so free and return NULL */ + ber_free( ber, 1 ); + return NULL; + } + + ber_reset( ber, 1 ); /* reset the pointer to the start of the buffer */ + return ber; +} + +/* New C-API ber_flatten routine */ +/* This routine allocates a struct berval whose contents are a BER +** encoding taken from the ber argument. The bvPtr pointer points to +** the returned berval. +** +** ber_flatten2 is the same, but uses a struct berval passed by +** the caller. If alloc is 0 the returned bv uses the ber buf directly. +*/ +int ber_flatten2( + BerElement *ber, + struct berval *bv, + int alloc ) +{ + assert( bv != NULL ); + + if ( bv == NULL ) { + return -1; + } + + if ( ber == NULL ) { + /* ber is null, create an empty berval */ + bv->bv_val = NULL; + bv->bv_len = 0; + + } else if ( ber->ber_sos_ptr != NULL ) { + /* unmatched "{" and "}" */ + return -1; + + } else { + /* copy the berval */ + ber_len_t len = ber_pvt_ber_write( ber ); + + if ( alloc ) { + bv->bv_val = (char *) ber_memalloc_x( len + 1, ber->ber_memctx ); + if ( bv->bv_val == NULL ) { + return -1; + } + AC_MEMCPY( bv->bv_val, ber->ber_buf, len ); + bv->bv_val[len] = '\0'; + } else if ( ber->ber_buf != NULL ) { + bv->bv_val = ber->ber_buf; + bv->bv_val[len] = '\0'; + } else { + bv->bv_val = ""; + } + bv->bv_len = len; + } + return 0; +} + +int ber_flatten( + BerElement *ber, + struct berval **bvPtr) +{ + struct berval *bv; + int rc; + + assert( bvPtr != NULL ); + + if(bvPtr == NULL) { + return -1; + } + + bv = ber_memalloc_x( sizeof(struct berval), ber->ber_memctx ); + if ( bv == NULL ) { + return -1; + } + rc = ber_flatten2(ber, bv, 1); + if (rc == -1) { + ber_memfree_x(bv, ber->ber_memctx); + } else { + *bvPtr = bv; + } + return rc; +} + +void +ber_reset( BerElement *ber, int was_writing ) +{ + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( was_writing ) { + ber->ber_end = ber->ber_ptr; + ber->ber_ptr = ber->ber_buf; + + } else { + ber->ber_ptr = ber->ber_end; + } + + ber->ber_rwptr = NULL; +} + +/* + * A rewrite of ber_get_next that can safely be called multiple times + * for the same packet. It will simply continue where it stopped until + * a full packet is read. + */ + +#define LENSIZE 4 + +ber_tag_t +ber_get_next( + Sockbuf *sb, + ber_len_t *len, + BerElement *ber ) +{ + assert( sb != NULL ); + assert( len != NULL ); + assert( ber != NULL ); + assert( SOCKBUF_VALID( sb ) ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_debug & LDAP_DEBUG_TRACE ) { + ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug, + "ber_get_next\n" ); + } + + /* + * Any ber element looks like this: tag length contents. + * Assuming everything's ok, we return the tag byte (we + * can assume a single byte), return the length in len, + * and the rest of the undecoded element in buf. + * + * Assumptions: + * 1) small tags (less than 128) + * 2) definite lengths + * 3) primitive encodings used whenever possible + * + * The code also handles multi-byte tags. The first few bytes + * of the message are read to check for multi-byte tags and + * lengths. These bytes are temporarily stored in the ber_tag, + * ber_len, and ber_usertag fields of the berelement until + * tag/len parsing is complete. After this parsing, any leftover + * bytes and the rest of the message are copied into the ber_buf. + * + * We expect tag and len to be at most 32 bits wide. + */ + + if (ber->ber_rwptr == NULL) { + assert( ber->ber_buf == NULL ); + ber->ber_rwptr = (char *) &ber->ber_len-1; + ber->ber_ptr = ber->ber_rwptr; + ber->ber_tag = 0; + } + + while (ber->ber_rwptr > (char *)&ber->ber_tag && ber->ber_rwptr < + (char *)&ber->ber_len + LENSIZE*2) { + ber_slen_t sblen; + char buf[sizeof(ber->ber_len)-1]; + ber_len_t tlen = 0; + + /* The tag & len can be at most 9 bytes; we try to read up to 8 here */ + sock_errset(0); + sblen=((char *)&ber->ber_len + LENSIZE*2 - 1)-ber->ber_rwptr; + /* Trying to read the last len byte of a 9 byte tag+len */ + if (sblen<1) + sblen = 1; + sblen=ber_int_sb_read( sb, ber->ber_rwptr, sblen ); + if (sblen<=0) return LBER_DEFAULT; + ber->ber_rwptr += sblen; + + /* We got at least one byte, try to parse the tag. */ + if (ber->ber_ptr == (char *)&ber->ber_len-1) { + ber_tag_t tag; + unsigned char *p = (unsigned char *)ber->ber_ptr; + tag = *p++; + if ((tag & LBER_BIG_TAG_MASK) == LBER_BIG_TAG_MASK) { + ber_len_t i; + for (i=1; (char *)p<ber->ber_rwptr; i++) { + tag <<= 8; + tag |= *p++; + if (!(tag & LBER_MORE_TAG_MASK)) + break; + /* Is the tag too big? */ + if (i == sizeof(ber_tag_t)-1) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + } + /* Did we run out of bytes? */ + if ((char *)p == ber->ber_rwptr) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } + } + ber->ber_tag = tag; + ber->ber_ptr = (char *)p; + } + + if ( ber->ber_ptr == ber->ber_rwptr ) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } + + /* Now look for the length */ + if (*ber->ber_ptr & 0x80) { /* multi-byte */ + int i; + unsigned char *p = (unsigned char *)ber->ber_ptr; + int llen = *p++ & 0x7f; + if (llen > LENSIZE) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + /* Not enough bytes? */ + if (ber->ber_rwptr - (char *)p < llen) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } + for (i=0; i<llen; i++) { + tlen <<=8; + tlen |= *p++; + } + ber->ber_ptr = (char *)p; + } else { + tlen = *(unsigned char *)ber->ber_ptr++; + } + + /* Are there leftover data bytes inside ber->ber_len? */ + if (ber->ber_ptr < (char *)&ber->ber_usertag) { + if (ber->ber_rwptr < (char *)&ber->ber_usertag) { + sblen = ber->ber_rwptr - ber->ber_ptr; + } else { + sblen = (char *)&ber->ber_usertag - ber->ber_ptr; + } + AC_MEMCPY(buf, ber->ber_ptr, sblen); + ber->ber_ptr += sblen; + } else { + sblen = 0; + } + ber->ber_len = tlen; + + /* now fill the buffer. */ + + /* make sure length is reasonable */ + if ( ber->ber_len == 0 ) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + + if ( sb->sb_max_incoming && ber->ber_len > sb->sb_max_incoming ) { + ber_log_printf( LDAP_DEBUG_CONNS, ber->ber_debug, + "ber_get_next: sockbuf_max_incoming exceeded " + "(%ld > %ld)\n", ber->ber_len, sb->sb_max_incoming ); + sock_errset(ERANGE); + return LBER_DEFAULT; + } + + if (ber->ber_buf==NULL) { + ber_len_t l = ber->ber_rwptr - ber->ber_ptr; + /* ber->ber_ptr is always <= ber->ber->ber_rwptr. + * make sure ber->ber_len agrees with what we've + * already read. + */ + if ( ber->ber_len < sblen + l ) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + ber->ber_buf = (char *) ber_memalloc_x( ber->ber_len + 1, ber->ber_memctx ); + if (ber->ber_buf==NULL) { + return LBER_DEFAULT; + } + ber->ber_end = ber->ber_buf + ber->ber_len; + if (sblen) { + AC_MEMCPY(ber->ber_buf, buf, sblen); + } + if (l > 0) { + AC_MEMCPY(ber->ber_buf + sblen, ber->ber_ptr, l); + sblen += l; + } + *ber->ber_end = '\0'; + ber->ber_ptr = ber->ber_buf; + ber->ber_usertag = 0; + if ((ber_len_t)sblen == ber->ber_len) { + goto done; + } + ber->ber_rwptr = ber->ber_buf + sblen; + } + } + + if ((ber->ber_rwptr>=ber->ber_buf) && (ber->ber_rwptr<ber->ber_end)) { + ber_slen_t res; + ber_slen_t to_go; + + to_go = ber->ber_end - ber->ber_rwptr; + /* unsigned/signed overflow */ + if (to_go<0) return LBER_DEFAULT; + + sock_errset(0); + res = ber_int_sb_read( sb, ber->ber_rwptr, to_go ); + if (res<=0) return LBER_DEFAULT; + ber->ber_rwptr+=res; + + if (res<to_go) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } +done: + ber->ber_rwptr = NULL; + *len = ber->ber_len; + if ( ber->ber_debug ) { + ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug, + "ber_get_next: tag 0x%lx len %ld contents:\n", + ber->ber_tag, ber->ber_len ); + ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 ); + } + return (ber->ber_tag); + } + + /* invalid input */ + return LBER_DEFAULT; +} + +char * +ber_start( BerElement* ber ) +{ + return ber->ber_buf; +} + +int +ber_len( BerElement* ber ) +{ + return ( ber->ber_end - ber->ber_buf ); +} + +int +ber_ptrlen( BerElement* ber ) +{ + return ( ber->ber_ptr - ber->ber_buf ); +} + +void +ber_rewind ( BerElement * ber ) +{ + ber->ber_rwptr = NULL; + ber->ber_sos_ptr = NULL; + ber->ber_end = ber->ber_ptr; + ber->ber_ptr = ber->ber_buf; +#if 0 /* TODO: Should we add this? */ + ber->ber_tag = LBER_DEFAULT; + ber->ber_usertag = 0; +#endif +} + +int +ber_remaining( BerElement * ber ) +{ + return ber_pvt_ber_remaining( ber ); +} diff --git a/libraries/liblber/lber-int.h b/libraries/liblber/lber-int.h new file mode 100644 index 0000000..ed88efd --- /dev/null +++ b/libraries/liblber/lber-int.h @@ -0,0 +1,224 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#ifndef _LBER_INT_H +#define _LBER_INT_H + +#include "lber.h" +#include "ldap_log.h" +#include "lber_pvt.h" +#include "ldap_queue.h" + +LDAP_BEGIN_DECL + +typedef void (*BER_LOG_FN)(FILE *file, + const char *subsys, int level, const char *fmt, ... ); + +LBER_V (BER_ERRNO_FN) ber_int_errno_fn; + +#ifdef LDAP_MEMORY_TRACE +# ifndef LDAP_MEMORY_DEBUG +# define LDAP_MEMORY_DEBUG 1 +# endif +#endif + +#ifdef LDAP_MEMORY_DEBUG +LBER_V (long) ber_int_meminuse; +#endif +#if defined(LDAP_MEMORY_DEBUG) && ((LDAP_MEMORY_DEBUG +0) & 2) +# define LDAP_MEMORY_DEBUG_ASSERT assert +#else +# define LDAP_MEMORY_DEBUG_ASSERT(expr) ((void) 0) +#endif + +struct lber_options { + short lbo_valid; + unsigned short lbo_options; + int lbo_debug; +}; + +LBER_F( int ) ber_pvt_log_output( + const char *subsystem, + int level, + const char *fmt, ... ); + +#define LBER_UNINITIALIZED 0x0 +#define LBER_INITIALIZED 0x1 +#define LBER_VALID_BERELEMENT 0x2 +#define LBER_VALID_SOCKBUF 0x3 + +LBER_V (struct lber_options) ber_int_options; +#define ber_int_debug ber_int_options.lbo_debug + +/* Data encoded in ASN.1 BER format */ +struct berelement { + struct lber_options ber_opts; +#define ber_valid ber_opts.lbo_valid +#define ber_options ber_opts.lbo_options +#define ber_debug ber_opts.lbo_debug + + /* + * The members below, when not NULL/LBER_DEFAULT/etc, are: + * ber_buf Data buffer. Other pointers normally point into it. + * ber_rwptr Read/write cursor for Sockbuf I/O. + * ber_memctx Context passed to ber_memalloc() & co. + * When decoding data (reading it from the BerElement): + * ber_end End of BER data. + * ber_ptr Read cursor, except for 1st octet of tags. + * ber_tag 1st octet of next tag, saved from *ber_ptr when + * ber_ptr may be pointing at a tag and is >ber_buf. + * The octet *ber_ptr itself may get overwritten with + * a \0, to terminate the preceding element. + * When encoding data (writing it to the BerElement): + * ber_end End of allocated buffer - 1 (allowing a final \0). + * ber_ptr Last complete BER element (normally write cursor). + * ber_sos_ptr NULL or write cursor for incomplete sequence or set. + * ber_sos_inner offset(seq/set length octets) if ber_sos_ptr!=NULL. + * ber_tag Default tag for next ber_printf() element. + * ber_usertag Boolean set by ber_printf "!" if it sets ber_tag. + * ber_len Reused for ber_sos_inner. + * When output to a Sockbuf: + * ber_ptr End of encoded data to write. + * When input from a Sockbuf: + * See ber_get_next(). + */ + + /* Do not change the order of these 3 fields! see ber_get_next */ + ber_tag_t ber_tag; + ber_len_t ber_len; + ber_tag_t ber_usertag; + + char *ber_buf; + char *ber_ptr; + char *ber_end; + + char *ber_sos_ptr; +# define ber_sos_inner ber_len /* reused for binary compat */ + + char *ber_rwptr; + void *ber_memctx; +}; +#define LBER_VALID(ber) ((ber)->ber_valid==LBER_VALID_BERELEMENT) + +#define ber_pvt_ber_remaining(ber) ((ber)->ber_end - (ber)->ber_ptr) +#define ber_pvt_ber_total(ber) ((ber)->ber_end - (ber)->ber_buf) +#define ber_pvt_ber_write(ber) ((ber)->ber_ptr - (ber)->ber_buf) + +struct sockbuf { + struct lber_options sb_opts; + Sockbuf_IO_Desc *sb_iod; /* I/O functions */ +#define sb_valid sb_opts.lbo_valid +#define sb_options sb_opts.lbo_options +#define sb_debug sb_opts.lbo_debug + ber_socket_t sb_fd; + ber_len_t sb_max_incoming; + unsigned int sb_trans_needs_read:1; + unsigned int sb_trans_needs_write:1; +#ifdef LDAP_PF_LOCAL_SENDMSG + char sb_ungetlen; + char sb_ungetbuf[8]; +#endif +}; + +#define SOCKBUF_VALID( sb ) ( (sb)->sb_valid == LBER_VALID_SOCKBUF ) + + +/* + * decode.c, encode.c + */ + +/* Simplest OID max-DER-component to implement in both decode and encode */ +#define LBER_OID_COMPONENT_MAX ((unsigned long)-1 - 128) + + +/* + * io.c + */ +LBER_F( int ) +ber_realloc LDAP_P(( + BerElement *ber, + ber_len_t len )); + +LBER_F (char *) ber_start LDAP_P(( BerElement * )); +LBER_F (int) ber_len LDAP_P(( BerElement * )); +LBER_F (int) ber_ptrlen LDAP_P(( BerElement * )); +LBER_F (void) ber_rewind LDAP_P(( BerElement * )); + +/* + * bprint.c + */ +#define ber_log_printf ber_pvt_log_printf + +LBER_F( int ) +ber_log_bprint LDAP_P(( + int errlvl, + int loglvl, + const char *data, + ber_len_t len )); + +LBER_F( int ) +ber_log_dump LDAP_P(( + int errlvl, + int loglvl, + BerElement *ber, + int inout )); + +LBER_V (BER_LOG_FN) ber_int_log_proc; +LBER_V (FILE *) ber_pvt_err_file; + +/* memory.c */ + /* simple macros to realloc for now */ +LBER_V (BerMemoryFunctions *) ber_int_memory_fns; +LBER_F (char *) ber_strndup( LDAP_CONST char *, ber_len_t ); +LBER_F (char *) ber_strndup_x( LDAP_CONST char *, ber_len_t, void *ctx ); + +#define LBER_MALLOC(s) ber_memalloc((s)) +#define LBER_CALLOC(n,s) ber_memcalloc((n),(s)) +#define LBER_REALLOC(p,s) ber_memrealloc((p),(s)) +#define LBER_FREE(p) ber_memfree((p)) +#define LBER_VFREE(v) ber_memvfree((void**)(v)) +#define LBER_STRDUP(s) ber_strdup((s)) +#define LBER_STRNDUP(s,l) ber_strndup((s),(l)) + +/* sockbuf.c */ + +LBER_F( int ) +ber_int_sb_init LDAP_P(( Sockbuf *sb )); + +LBER_F( int ) +ber_int_sb_close LDAP_P(( Sockbuf *sb )); + +LBER_F( int ) +ber_int_sb_destroy LDAP_P(( Sockbuf *sb )); + +LBER_F( ber_slen_t ) +ber_int_sb_read LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len )); + +LBER_F( ber_slen_t ) +ber_int_sb_write LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len )); + +LDAP_END_DECL + +#endif /* _LBER_INT_H */ diff --git a/libraries/liblber/memory.c b/libraries/liblber/memory.c new file mode 100644 index 0000000..d93104d --- /dev/null +++ b/libraries/liblber/memory.c @@ -0,0 +1,825 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ + +#include "portable.h" + +#include <ac/stdlib.h> +#include <ac/string.h> + +#include "lber-int.h" + +#ifdef LDAP_MEMORY_TRACE +#include <stdio.h> +#endif + +#ifdef LDAP_MEMORY_DEBUG +/* + * LDAP_MEMORY_DEBUG should only be enabled for the purposes of + * debugging memory management within OpenLDAP libraries and slapd. + * + * It should only be enabled by an experienced developer as it causes + * the inclusion of numerous assert()'s, many of which may be triggered + * by a prefectly valid program. If LDAP_MEMORY_DEBUG & 2 is true, + * that includes asserts known to break both slapd and current clients. + * + * The code behind this macro is subject to change as needed to + * support this testing. + */ + +struct ber_mem_hdr { + ber_int_t bm_top; /* Pattern to detect buf overrun from prev buffer */ + ber_int_t bm_length; /* Length of user allocated area */ +#ifdef LDAP_MEMORY_TRACE + ber_int_t bm_sequence; /* Allocation sequence number */ +#endif + union bmu_align_u { /* Force alignment, pattern to detect back clobber */ + ber_len_t bmu_len_t; + ber_tag_t bmu_tag_t; + ber_int_t bmu_int_t; + + size_t bmu_size_t; + void * bmu_voidp; + double bmu_double; + long bmu_long; + long (*bmu_funcp)( double ); + unsigned char bmu_char[4]; + } ber_align; +#define bm_junk ber_align.bmu_len_t +#define bm_data ber_align.bmu_char[1] +#define bm_char ber_align.bmu_char +}; + +/* Pattern at top of allocated space */ +#define LBER_MEM_JUNK ((ber_int_t) 0xdeaddada) + +static const struct ber_mem_hdr ber_int_mem_hdr = { LBER_MEM_JUNK }; + +/* Note sequence and ber_int_meminuse are counters, but are not + * thread safe. If you want to use these values for multithreaded applications, + * you must put mutexes around them, otherwise they will have incorrect values. + * When debugging, if you sort the debug output, the sequence number will + * put allocations/frees together. It is then a simple matter to write a script + * to find any allocations that don't have a buffer free function. + */ +long ber_int_meminuse = 0; +#ifdef LDAP_MEMORY_TRACE +static ber_int_t sequence = 0; +#endif + +/* Pattern placed just before user data */ +static unsigned char toppattern[4] = { 0xde, 0xad, 0xba, 0xde }; +/* Pattern placed just after user data */ +static unsigned char endpattern[4] = { 0xd1, 0xed, 0xde, 0xca }; + +#define mbu_len sizeof(ber_int_mem_hdr.ber_align) + +/* Test if pattern placed just before user data is good */ +#define testdatatop(val) ( \ + *(val->bm_char+mbu_len-4)==toppattern[0] && \ + *(val->bm_char+mbu_len-3)==toppattern[1] && \ + *(val->bm_char+mbu_len-2)==toppattern[2] && \ + *(val->bm_char+mbu_len-1)==toppattern[3] ) + +/* Place pattern just before user data */ +#define setdatatop(val) *(val->bm_char+mbu_len-4)=toppattern[0]; \ + *(val->bm_char+mbu_len-3)=toppattern[1]; \ + *(val->bm_char+mbu_len-2)=toppattern[2]; \ + *(val->bm_char+mbu_len-1)=toppattern[3]; + +/* Test if pattern placed just after user data is good */ +#define testend(val) ( *((unsigned char *)val+0)==endpattern[0] && \ + *((unsigned char *)val+1)==endpattern[1] && \ + *((unsigned char *)val+2)==endpattern[2] && \ + *((unsigned char *)val+3)==endpattern[3] ) + +/* Place pattern just after user data */ +#define setend(val) *((unsigned char *)val+0)=endpattern[0]; \ + *((unsigned char *)val+1)=endpattern[1]; \ + *((unsigned char *)val+2)=endpattern[2]; \ + *((unsigned char *)val+3)=endpattern[3]; + +#define BER_MEM_BADADDR ((void *) &ber_int_mem_hdr.bm_data) +#define BER_MEM_VALID(p) do { \ + assert( (p) != BER_MEM_BADADDR ); \ + assert( (p) != (void *) &ber_int_mem_hdr ); \ + } while(0) + +#else +#define BER_MEM_VALID(p) /* no-op */ +#endif + +BerMemoryFunctions *ber_int_memory_fns = NULL; + +void +ber_memfree_x( void *p, void *ctx ) +{ + if( p == NULL ) { + return; + } + + BER_MEM_VALID( p ); + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + struct ber_mem_hdr *mh = (struct ber_mem_hdr *) + ((char *)p - sizeof(struct ber_mem_hdr)); + assert( mh->bm_top == LBER_MEM_JUNK); + assert( testdatatop( mh)); + assert( testend( (char *)&mh[1] + mh->bm_length) ); + ber_int_meminuse -= mh->bm_length; + +#ifdef LDAP_MEMORY_TRACE + fprintf(stderr, "0x%08lx 0x%08lx -f- %ld ber_memfree %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + /* Fill the free space with poison */ + memset( mh, 0xff, mh->bm_length + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t)); + free( mh ); +#else + free( p ); +#endif + return; + } + + assert( ber_int_memory_fns->bmf_free != 0 ); + + (*ber_int_memory_fns->bmf_free)( p, ctx ); +} + +void +ber_memfree( void *p ) +{ + ber_memfree_x(p, NULL); +} + +void +ber_memvfree_x( void **vec, void *ctx ) +{ + int i; + + if( vec == NULL ) { + return; + } + + BER_MEM_VALID( vec ); + + for ( i = 0; vec[i] != NULL; i++ ) { + ber_memfree_x( vec[i], ctx ); + } + + ber_memfree_x( vec, ctx ); +} + +void +ber_memvfree( void **vec ) +{ + ber_memvfree_x( vec, NULL ); +} + +void * +ber_memalloc_x( ber_len_t s, void *ctx ) +{ + void *new; + + if( s == 0 ) { + LDAP_MEMORY_DEBUG_ASSERT( s != 0 ); + return NULL; + } + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + new = malloc(s + sizeof(struct ber_mem_hdr) + sizeof( ber_int_t)); + if( new ) + { + struct ber_mem_hdr *mh = new; + mh->bm_top = LBER_MEM_JUNK; + mh->bm_length = s; + setdatatop( mh); + setend( (char *)&mh[1] + mh->bm_length ); + + ber_int_meminuse += mh->bm_length; /* Count mem inuse */ + +#ifdef LDAP_MEMORY_TRACE + mh->bm_sequence = sequence++; + fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memalloc %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + /* poison new memory */ + memset( (char *)&mh[1], 0xff, s); + + BER_MEM_VALID( &mh[1] ); + new = &mh[1]; + } +#else + new = malloc( s ); +#endif + } else { + new = (*ber_int_memory_fns->bmf_malloc)( s, ctx ); + } + + if( new == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + } + + return new; +} + +void * +ber_memalloc( ber_len_t s ) +{ + return ber_memalloc_x( s, NULL ); +} + +void * +ber_memcalloc_x( ber_len_t n, ber_len_t s, void *ctx ) +{ + void *new; + + if( n == 0 || s == 0 ) { + LDAP_MEMORY_DEBUG_ASSERT( n != 0 && s != 0); + return NULL; + } + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + new = n < (-sizeof(struct ber_mem_hdr) - sizeof(ber_int_t)) / s + ? calloc(1, n*s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t)) + : NULL; + if( new ) + { + struct ber_mem_hdr *mh = new; + + mh->bm_top = LBER_MEM_JUNK; + mh->bm_length = n*s; + setdatatop( mh); + setend( (char *)&mh[1] + mh->bm_length ); + + ber_int_meminuse += mh->bm_length; + +#ifdef LDAP_MEMORY_TRACE + mh->bm_sequence = sequence++; + fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memcalloc %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + BER_MEM_VALID( &mh[1] ); + new = &mh[1]; + } +#else + new = calloc( n, s ); +#endif + + } else { + new = (*ber_int_memory_fns->bmf_calloc)( n, s, ctx ); + } + + if( new == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + } + + return new; +} + +void * +ber_memcalloc( ber_len_t n, ber_len_t s ) +{ + return ber_memcalloc_x( n, s, NULL ); +} + +void * +ber_memrealloc_x( void* p, ber_len_t s, void *ctx ) +{ + void *new = NULL; + + /* realloc(NULL,s) -> malloc(s) */ + if( p == NULL ) { + return ber_memalloc_x( s, ctx ); + } + + /* realloc(p,0) -> free(p) */ + if( s == 0 ) { + ber_memfree_x( p, ctx ); + return NULL; + } + + BER_MEM_VALID( p ); + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + ber_int_t oldlen; + struct ber_mem_hdr *mh = (struct ber_mem_hdr *) + ((char *)p - sizeof(struct ber_mem_hdr)); + assert( mh->bm_top == LBER_MEM_JUNK); + assert( testdatatop( mh)); + assert( testend( (char *)&mh[1] + mh->bm_length) ); + oldlen = mh->bm_length; + + p = realloc( mh, s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t) ); + if( p == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + return NULL; + } + + mh = p; + mh->bm_length = s; + setend( (char *)&mh[1] + mh->bm_length ); + if( s > oldlen ) { + /* poison any new memory */ + memset( (char *)&mh[1] + oldlen, 0xff, s - oldlen); + } + + assert( mh->bm_top == LBER_MEM_JUNK); + assert( testdatatop( mh)); + + ber_int_meminuse += s - oldlen; +#ifdef LDAP_MEMORY_TRACE + fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memrealloc %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + BER_MEM_VALID( &mh[1] ); + return &mh[1]; +#else + new = realloc( p, s ); +#endif + } else { + new = (*ber_int_memory_fns->bmf_realloc)( p, s, ctx ); + } + + if( new == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + } + + return new; +} + +void * +ber_memrealloc( void* p, ber_len_t s ) +{ + return ber_memrealloc_x( p, s, NULL ); +} + +void +ber_bvfree_x( struct berval *bv, void *ctx ) +{ + if( bv == NULL ) { + return; + } + + BER_MEM_VALID( bv ); + + if ( bv->bv_val != NULL ) { + ber_memfree_x( bv->bv_val, ctx ); + } + + ber_memfree_x( (char *) bv, ctx ); +} + +void +ber_bvfree( struct berval *bv ) +{ + ber_bvfree_x( bv, NULL ); +} + +void +ber_bvecfree_x( struct berval **bv, void *ctx ) +{ + int i; + + if( bv == NULL ) { + return; + } + + BER_MEM_VALID( bv ); + + /* count elements */ + for ( i = 0; bv[i] != NULL; i++ ) ; + + /* free in reverse order */ + for ( i--; i >= 0; i-- ) { + ber_bvfree_x( bv[i], ctx ); + } + + ber_memfree_x( (char *) bv, ctx ); +} + +void +ber_bvecfree( struct berval **bv ) +{ + ber_bvecfree_x( bv, NULL ); +} + +int +ber_bvecadd_x( struct berval ***bvec, struct berval *bv, void *ctx ) +{ + ber_len_t i; + struct berval **new; + + if( *bvec == NULL ) { + if( bv == NULL ) { + /* nothing to add */ + return 0; + } + + *bvec = ber_memalloc_x( 2 * sizeof(struct berval *), ctx ); + + if( *bvec == NULL ) { + return -1; + } + + (*bvec)[0] = bv; + (*bvec)[1] = NULL; + + return 1; + } + + BER_MEM_VALID( bvec ); + + /* count entries */ + for ( i = 0; (*bvec)[i] != NULL; i++ ) { + /* EMPTY */; + } + + if( bv == NULL ) { + return i; + } + + new = ber_memrealloc_x( *bvec, (i+2) * sizeof(struct berval *), ctx); + + if( new == NULL ) { + return -1; + } + + *bvec = new; + + (*bvec)[i++] = bv; + (*bvec)[i] = NULL; + + return i; +} + +int +ber_bvecadd( struct berval ***bvec, struct berval *bv ) +{ + return ber_bvecadd_x( bvec, bv, NULL ); +} + +struct berval * +ber_dupbv_x( + struct berval *dst, struct berval *src, void *ctx ) +{ + struct berval *new, tmp; + + if( src == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + if ( dst ) { + new = &tmp; + } else { + if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { + return NULL; + } + } + + if ( src->bv_val == NULL ) { + new->bv_val = NULL; + new->bv_len = 0; + } else { + + if(( new->bv_val = ber_memalloc_x( src->bv_len + 1, ctx )) == NULL ) { + if ( !dst ) + ber_memfree_x( new, ctx ); + return NULL; + } + + AC_MEMCPY( new->bv_val, src->bv_val, src->bv_len ); + new->bv_val[src->bv_len] = '\0'; + new->bv_len = src->bv_len; + } + + if ( dst ) { + *dst = *new; + new = dst; + } + + return new; +} + +struct berval * +ber_dupbv( + struct berval *dst, struct berval *src ) +{ + return ber_dupbv_x( dst, src, NULL ); +} + +struct berval * +ber_bvdup( + struct berval *src ) +{ + return ber_dupbv_x( NULL, src, NULL ); +} + +struct berval * +ber_str2bv_x( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv, + void *ctx) +{ + struct berval *new; + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + if( bv ) { + new = bv; + } else { + if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { + return NULL; + } + } + + new->bv_len = len ? len : strlen( s ); + if ( dup ) { + if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) { + if ( !bv ) + ber_memfree_x( new, ctx ); + return NULL; + } + + AC_MEMCPY( new->bv_val, s, new->bv_len ); + new->bv_val[new->bv_len] = '\0'; + } else { + new->bv_val = (char *) s; + } + + return( new ); +} + +struct berval * +ber_str2bv( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv) +{ + return ber_str2bv_x( s, len, dup, bv, NULL ); +} + +struct berval * +ber_mem2bv_x( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv, + void *ctx) +{ + struct berval *new; + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + if( bv ) { + new = bv; + } else { + if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { + return NULL; + } + } + + new->bv_len = len; + if ( dup ) { + if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) { + if ( !bv ) { + ber_memfree_x( new, ctx ); + } + return NULL; + } + + AC_MEMCPY( new->bv_val, s, new->bv_len ); + new->bv_val[new->bv_len] = '\0'; + } else { + new->bv_val = (char *) s; + } + + return( new ); +} + +struct berval * +ber_mem2bv( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv) +{ + return ber_mem2bv_x( s, len, dup, bv, NULL ); +} + +char * +ber_strdup_x( LDAP_CONST char *s, void *ctx ) +{ + char *p; + size_t len; + +#ifdef LDAP_MEMORY_DEBUG + assert(s != NULL); /* bv damn better point to something */ +#endif + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + len = strlen( s ) + 1; + if ( (p = ber_memalloc_x( len, ctx )) != NULL ) { + AC_MEMCPY( p, s, len ); + } + + return p; +} + +char * +ber_strdup( LDAP_CONST char *s ) +{ + return ber_strdup_x( s, NULL ); +} + +ber_len_t +ber_strnlen( LDAP_CONST char *s, ber_len_t len ) +{ + ber_len_t l; + + for ( l = 0; l < len && s[l] != '\0'; l++ ) ; + + return l; +} + +char * +ber_strndup_x( LDAP_CONST char *s, ber_len_t l, void *ctx ) +{ + char *p; + size_t len; + +#ifdef LDAP_MEMORY_DEBUG + assert(s != NULL); /* bv damn better point to something */ +#endif + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + len = ber_strnlen( s, l ); + if ( (p = ber_memalloc_x( len + 1, ctx )) != NULL ) { + AC_MEMCPY( p, s, len ); + p[len] = '\0'; + } + + return p; +} + +char * +ber_strndup( LDAP_CONST char *s, ber_len_t l ) +{ + return ber_strndup_x( s, l, NULL ); +} + +/* + * dst is resized as required by src and the value of src is copied into dst + * dst->bv_val must be NULL (and dst->bv_len must be 0), or it must be + * alloc'ed with the context ctx + */ +struct berval * +ber_bvreplace_x( struct berval *dst, LDAP_CONST struct berval *src, void *ctx ) +{ + assert( dst != NULL ); + assert( !BER_BVISNULL( src ) ); + + if ( BER_BVISNULL( dst ) || dst->bv_len < src->bv_len ) { + dst->bv_val = ber_memrealloc_x( dst->bv_val, src->bv_len + 1, ctx ); + } + + AC_MEMCPY( dst->bv_val, src->bv_val, src->bv_len + 1 ); + dst->bv_len = src->bv_len; + + return dst; +} + +struct berval * +ber_bvreplace( struct berval *dst, LDAP_CONST struct berval *src ) +{ + return ber_bvreplace_x( dst, src, NULL ); +} + +void +ber_bvarray_free_x( BerVarray a, void *ctx ) +{ + int i; + + if (a) { + BER_MEM_VALID( a ); + + /* count elements */ + for (i=0; a[i].bv_val; i++) ; + + /* free in reverse order */ + for (i--; i>=0; i--) { + ber_memfree_x(a[i].bv_val, ctx); + } + + ber_memfree_x(a, ctx); + } +} + +void +ber_bvarray_free( BerVarray a ) +{ + ber_bvarray_free_x(a, NULL); +} + +int +ber_bvarray_dup_x( BerVarray *dst, BerVarray src, void *ctx ) +{ + int i, j; + BerVarray new; + + if ( !src ) { + *dst = NULL; + return 0; + } + + for (i=0; !BER_BVISNULL( &src[i] ); i++) ; + new = ber_memalloc_x(( i+1 ) * sizeof(BerValue), ctx ); + if ( !new ) + return -1; + for (j=0; j<i; j++) { + ber_dupbv_x( &new[j], &src[j], ctx ); + if ( BER_BVISNULL( &new[j] )) { + ber_bvarray_free_x( new, ctx ); + return -1; + } + } + BER_BVZERO( &new[j] ); + *dst = new; + return 0; +} + +int +ber_bvarray_add_x( BerVarray *a, BerValue *bv, void *ctx ) +{ + int n; + + if ( *a == NULL ) { + if (bv == NULL) { + return 0; + } + n = 0; + + *a = (BerValue *) ber_memalloc_x( 2 * sizeof(BerValue), ctx ); + if ( *a == NULL ) { + return -1; + } + + } else { + BerVarray atmp; + BER_MEM_VALID( a ); + + for ( n = 0; *a != NULL && (*a)[n].bv_val != NULL; n++ ) { + ; /* just count them */ + } + + if (bv == NULL) { + return n; + } + + atmp = (BerValue *) ber_memrealloc_x( (char *) *a, + (n + 2) * sizeof(BerValue), ctx ); + + if( atmp == NULL ) { + return -1; + } + + *a = atmp; + } + + (*a)[n++] = *bv; + (*a)[n].bv_val = NULL; + (*a)[n].bv_len = 0; + + return n; +} + +int +ber_bvarray_add( BerVarray *a, BerValue *bv ) +{ + return ber_bvarray_add_x( a, bv, NULL ); +} diff --git a/libraries/liblber/nt_err.c b/libraries/liblber/nt_err.c new file mode 100644 index 0000000..93a496a --- /dev/null +++ b/libraries/liblber/nt_err.c @@ -0,0 +1,96 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ + +#include "portable.h" + +#ifdef HAVE_WINSOCK2 +#include <winsock2.h> +#elif defined(HAVE_WINSOCK) +#include <winsock.h> +#endif /* HAVE_WINSOCK(2) */ + +#define LBER_RETSTR( x ) case x: return #x; + +char *ber_pvt_wsa_err2string( int err ) +{ + switch( err ) { + LBER_RETSTR( WSAEINTR ) + LBER_RETSTR( WSAEBADF ) + LBER_RETSTR( WSAEACCES ) + LBER_RETSTR( WSAEFAULT ) + LBER_RETSTR( WSAEINVAL ) + LBER_RETSTR( WSAEMFILE ) + LBER_RETSTR( WSAEWOULDBLOCK ) + LBER_RETSTR( WSAEINPROGRESS ) + LBER_RETSTR( WSAEALREADY ) + LBER_RETSTR( WSAENOTSOCK ) + LBER_RETSTR( WSAEDESTADDRREQ ) + LBER_RETSTR( WSAEMSGSIZE ) + LBER_RETSTR( WSAEPROTOTYPE ) + LBER_RETSTR( WSAENOPROTOOPT ) + LBER_RETSTR( WSAEPROTONOSUPPORT ) + LBER_RETSTR( WSAESOCKTNOSUPPORT ) + LBER_RETSTR( WSAEOPNOTSUPP ) + LBER_RETSTR( WSAEPFNOSUPPORT ) + LBER_RETSTR( WSAEAFNOSUPPORT ) + LBER_RETSTR( WSAEADDRINUSE ) + LBER_RETSTR( WSAEADDRNOTAVAIL ) + LBER_RETSTR( WSAENETDOWN ) + LBER_RETSTR( WSAENETUNREACH ) + LBER_RETSTR( WSAENETRESET ) + LBER_RETSTR( WSAECONNABORTED ) + LBER_RETSTR( WSAECONNRESET ) + LBER_RETSTR( WSAENOBUFS ) + LBER_RETSTR( WSAEISCONN ) + LBER_RETSTR( WSAENOTCONN ) + LBER_RETSTR( WSAESHUTDOWN ) + LBER_RETSTR( WSAETOOMANYREFS ) + LBER_RETSTR( WSAETIMEDOUT ) + LBER_RETSTR( WSAECONNREFUSED ) + LBER_RETSTR( WSAELOOP ) + LBER_RETSTR( WSAENAMETOOLONG ) + LBER_RETSTR( WSAEHOSTDOWN ) + LBER_RETSTR( WSAEHOSTUNREACH ) + LBER_RETSTR( WSAENOTEMPTY ) + LBER_RETSTR( WSAEPROCLIM ) + LBER_RETSTR( WSAEUSERS ) + LBER_RETSTR( WSAEDQUOT ) + LBER_RETSTR( WSAESTALE ) + LBER_RETSTR( WSAEREMOTE ) + LBER_RETSTR( WSASYSNOTREADY ) + LBER_RETSTR( WSAVERNOTSUPPORTED ) + LBER_RETSTR( WSANOTINITIALISED ) + LBER_RETSTR( WSAEDISCON ) + +#ifdef HAVE_WINSOCK2 + LBER_RETSTR( WSAENOMORE ) + LBER_RETSTR( WSAECANCELLED ) + LBER_RETSTR( WSAEINVALIDPROCTABLE ) + LBER_RETSTR( WSAEINVALIDPROVIDER ) + LBER_RETSTR( WSASYSCALLFAILURE ) + LBER_RETSTR( WSASERVICE_NOT_FOUND ) + LBER_RETSTR( WSATYPE_NOT_FOUND ) + LBER_RETSTR( WSA_E_NO_MORE ) + LBER_RETSTR( WSA_E_CANCELLED ) + LBER_RETSTR( WSAEREFUSED ) +#endif /* HAVE_WINSOCK2 */ + + LBER_RETSTR( WSAHOST_NOT_FOUND ) + LBER_RETSTR( WSATRY_AGAIN ) + LBER_RETSTR( WSANO_RECOVERY ) + LBER_RETSTR( WSANO_DATA ) + } + return "unknown WSA error"; +} diff --git a/libraries/liblber/options.c b/libraries/liblber/options.c new file mode 100644 index 0000000..9b2baaf --- /dev/null +++ b/libraries/liblber/options.c @@ -0,0 +1,233 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ + +#include "portable.h" + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/stdarg.h> +#include "lber-int.h" + +char ber_pvt_opt_on; /* used to get a non-NULL address for *_OPT_ON */ + +struct lber_options ber_int_options = { + LBER_UNINITIALIZED, 0, 0 }; + +static BerMemoryFunctions ber_int_memory_fns_datum; + +int +ber_get_option( + void *item, + int option, + void *outvalue) +{ + const BerElement *ber; + const Sockbuf *sb; + + if(outvalue == NULL) { + /* no place to get to */ + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + if(item == NULL) { + switch ( option ) { + case LBER_OPT_BER_DEBUG: + * (int *) outvalue = ber_int_debug; + return LBER_OPT_SUCCESS; + + case LBER_OPT_MEMORY_INUSE: + /* The memory inuse is a global variable on kernal implementations. + * This means that memory debug is shared by all LDAP processes + * so for this variable to have much meaning, only one LDAP process + * should be running and memory inuse should be initialized to zero + * using the lber_set_option() function during startup. + * The counter is not accurate for multithreaded ldap applications. + */ +#ifdef LDAP_MEMORY_DEBUG + * (int *) outvalue = ber_int_meminuse; + return LBER_OPT_SUCCESS; +#else + return LBER_OPT_ERROR; +#endif + + case LBER_OPT_LOG_PRINT_FILE: + *((FILE**)outvalue) = (FILE*)ber_pvt_err_file; + return LBER_OPT_SUCCESS; + } + + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + ber = item; + sb = item; + + switch(option) { + case LBER_OPT_BER_OPTIONS: + assert( LBER_VALID( ber ) ); + * (int *) outvalue = ber->ber_options; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_DEBUG: + assert( LBER_VALID( ber ) ); + * (int *) outvalue = ber->ber_debug; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_REMAINING_BYTES: + assert( LBER_VALID( ber ) ); + *((ber_len_t *) outvalue) = ber_pvt_ber_remaining(ber); + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_TOTAL_BYTES: + assert( LBER_VALID( ber ) ); + *((ber_len_t *) outvalue) = ber_pvt_ber_total(ber); + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_BYTES_TO_WRITE: + assert( LBER_VALID( ber ) ); + *((ber_len_t *) outvalue) = ber_pvt_ber_write(ber); + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_MEMCTX: + assert( LBER_VALID( ber ) ); + *((void **) outvalue) = ber->ber_memctx; + return LBER_OPT_SUCCESS; + + default: + /* bad param */ + ber_errno = LBER_ERROR_PARAM; + break; + } + + return LBER_OPT_ERROR; +} + +int +ber_set_option( + void *item, + int option, + LDAP_CONST void *invalue) +{ + BerElement *ber; + Sockbuf *sb; + + if(invalue == NULL) { + /* no place to set from */ + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + if(item == NULL) { + switch ( option ) { + case LBER_OPT_BER_DEBUG: + ber_int_debug = * (const int *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_LOG_PRINT_FN: + ber_pvt_log_print = (BER_LOG_PRINT_FN) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_LOG_PRINT_FILE: + ber_pvt_err_file = (void *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_MEMORY_INUSE: + /* The memory inuse is a global variable on kernal implementations. + * This means that memory debug is shared by all LDAP processes + * so for this variable to have much meaning, only one LDAP process + * should be running and memory inuse should be initialized to zero + * using the lber_set_option() function during startup. + * The counter is not accurate for multithreaded applications. + */ +#ifdef LDAP_MEMORY_DEBUG + ber_int_meminuse = * (int *) invalue; + return LBER_OPT_SUCCESS; +#else + return LBER_OPT_ERROR; +#endif + case LBER_OPT_MEMORY_FNS: + if ( ber_int_memory_fns == NULL ) + { + const BerMemoryFunctions *f = + (const BerMemoryFunctions *) invalue; + /* make sure all functions are provided */ + if(!( f->bmf_malloc && f->bmf_calloc + && f->bmf_realloc && f->bmf_free )) + { + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + ber_int_memory_fns = &ber_int_memory_fns_datum; + + AC_MEMCPY(ber_int_memory_fns, f, + sizeof(BerMemoryFunctions)); + + return LBER_OPT_SUCCESS; + } + break; + + case LBER_OPT_LOG_PROC: + ber_int_log_proc = (BER_LOG_FN)invalue; + return LBER_OPT_SUCCESS; + } + + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + ber = item; + sb = item; + + switch(option) { + case LBER_OPT_BER_OPTIONS: + assert( LBER_VALID( ber ) ); + ber->ber_options = * (const int *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_DEBUG: + assert( LBER_VALID( ber ) ); + ber->ber_debug = * (const int *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_REMAINING_BYTES: + assert( LBER_VALID( ber ) ); + ber->ber_end = &ber->ber_ptr[* (const ber_len_t *) invalue]; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_TOTAL_BYTES: + assert( LBER_VALID( ber ) ); + ber->ber_end = &ber->ber_buf[* (const ber_len_t *) invalue]; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_BYTES_TO_WRITE: + assert( LBER_VALID( ber ) ); + ber->ber_ptr = &ber->ber_buf[* (const ber_len_t *) invalue]; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_MEMCTX: + assert( LBER_VALID( ber ) ); + ber->ber_memctx = *(void **)invalue; + return LBER_OPT_SUCCESS; + + default: + /* bad param */ + ber_errno = LBER_ERROR_PARAM; + break; + } + + return LBER_OPT_ERROR; +} diff --git a/libraries/liblber/sockbuf.c b/libraries/liblber/sockbuf.c new file mode 100644 index 0000000..a84160a --- /dev/null +++ b/libraries/liblber/sockbuf.c @@ -0,0 +1,988 @@ +/* sockbuf.c - i/o routines with support for adding i/o layers. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif /* HAVE_IO_H */ + +#if defined( HAVE_FCNTL_H ) +#include <fcntl.h> +#endif + +#if defined( HAVE_SYS_FILIO_H ) +#include <sys/filio.h> +#elif defined( HAVE_SYS_IOCTL_H ) +#include <sys/ioctl.h> +#endif + +#include "lber-int.h" + +#ifndef LBER_MIN_BUFF_SIZE +#define LBER_MIN_BUFF_SIZE 4096 +#endif +#ifndef LBER_MAX_BUFF_SIZE +#define LBER_MAX_BUFF_SIZE (65536*256) +#endif +#ifndef LBER_DEFAULT_READAHEAD +#define LBER_DEFAULT_READAHEAD 16384 +#endif + +Sockbuf * +ber_sockbuf_alloc( void ) +{ + Sockbuf *sb; + + sb = LBER_CALLOC( 1, sizeof( Sockbuf ) ); + + if( sb == NULL ) return NULL; + + ber_int_sb_init( sb ); + return sb; +} + +void +ber_sockbuf_free( Sockbuf *sb ) +{ + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + ber_int_sb_close( sb ); + ber_int_sb_destroy( sb ); + LBER_FREE( sb ); +} + +/* Return values: -1: error, 0: no operation performed or the answer is false, + * 1: successful operation or the answer is true + */ +int +ber_sockbuf_ctrl( Sockbuf *sb, int opt, void *arg ) +{ + Sockbuf_IO_Desc *p; + int ret = 0; + + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + switch ( opt ) { + case LBER_SB_OPT_HAS_IO: + p = sb->sb_iod; + while ( p && p->sbiod_io != (Sockbuf_IO *)arg ) { + p = p->sbiod_next; + } + + if ( p ) { + ret = 1; + } + break; + + case LBER_SB_OPT_GET_FD: + if ( arg != NULL ) { + *((ber_socket_t *)arg) = sb->sb_fd; + } + ret = ( sb->sb_fd == AC_SOCKET_INVALID ? -1 : 1); + break; + + case LBER_SB_OPT_SET_FD: + sb->sb_fd = *((ber_socket_t *)arg); + ret = 1; + break; + + case LBER_SB_OPT_SET_NONBLOCK: + ret = ber_pvt_socket_set_nonblock( sb->sb_fd, arg != NULL) + ? -1 : 1; + break; + + case LBER_SB_OPT_DRAIN: { + /* Drain the data source to enable possible errors (e.g. + * TLS) to be propagated to the upper layers + */ + char buf[LBER_MIN_BUFF_SIZE]; + + do { + ret = ber_int_sb_read( sb, buf, sizeof( buf ) ); + } while ( ret == sizeof( buf ) ); + + ret = 1; + } break; + + case LBER_SB_OPT_NEEDS_READ: + ret = ( sb->sb_trans_needs_read ? 1 : 0 ); + break; + + case LBER_SB_OPT_NEEDS_WRITE: + ret = ( sb->sb_trans_needs_write ? 1 : 0 ); + break; + + case LBER_SB_OPT_GET_MAX_INCOMING: + if ( arg != NULL ) { + *((ber_len_t *)arg) = sb->sb_max_incoming; + } + ret = 1; + break; + + case LBER_SB_OPT_SET_MAX_INCOMING: + sb->sb_max_incoming = *((ber_len_t *)arg); + ret = 1; + break; + + case LBER_SB_OPT_UNGET_BUF: +#ifdef LDAP_PF_LOCAL_SENDMSG + sb->sb_ungetlen = ((struct berval *)arg)->bv_len; + if ( sb->sb_ungetlen <= sizeof( sb->sb_ungetbuf )) { + AC_MEMCPY( sb->sb_ungetbuf, ((struct berval *)arg)->bv_val, + sb->sb_ungetlen ); + ret = 1; + } else { + sb->sb_ungetlen = 0; + ret = -1; + } +#endif + break; + + default: + ret = sb->sb_iod->sbiod_io->sbi_ctrl( sb->sb_iod, opt, arg ); + break; + } + + return ret; +} + +int +ber_sockbuf_add_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer, void *arg ) +{ + Sockbuf_IO_Desc *d, *p, **q; + + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + if ( sbio == NULL ) { + return -1; + } + + q = &sb->sb_iod; + p = *q; + while ( p && p->sbiod_level > layer ) { + q = &p->sbiod_next; + p = *q; + } + + d = LBER_MALLOC( sizeof( *d ) ); + if ( d == NULL ) { + return -1; + } + + d->sbiod_level = layer; + d->sbiod_sb = sb; + d->sbiod_io = sbio; + memset( &d->sbiod_pvt, '\0', sizeof( d->sbiod_pvt ) ); + d->sbiod_next = p; + *q = d; + + if ( sbio->sbi_setup != NULL && ( sbio->sbi_setup( d, arg ) < 0 ) ) { + return -1; + } + + return 0; +} + +int +ber_sockbuf_remove_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer ) +{ + Sockbuf_IO_Desc *p, **q; + + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + if ( sb->sb_iod == NULL ) { + return -1; + } + + q = &sb->sb_iod; + while ( *q != NULL ) { + p = *q; + if ( layer == p->sbiod_level && p->sbiod_io == sbio ) { + if ( p->sbiod_io->sbi_remove != NULL && + p->sbiod_io->sbi_remove( p ) < 0 ) + { + return -1; + } + *q = p->sbiod_next; + LBER_FREE( p ); + break; + } + q = &p->sbiod_next; + } + + return 0; +} + +void +ber_pvt_sb_buf_init( Sockbuf_Buf *buf ) +{ + buf->buf_base = NULL; + buf->buf_ptr = 0; + buf->buf_end = 0; + buf->buf_size = 0; +} + +void +ber_pvt_sb_buf_destroy( Sockbuf_Buf *buf ) +{ + assert( buf != NULL); + + if (buf->buf_base) { + LBER_FREE( buf->buf_base ); + } + ber_pvt_sb_buf_init( buf ); +} + +int +ber_pvt_sb_grow_buffer( Sockbuf_Buf *buf, ber_len_t minsize ) +{ + ber_len_t pw; + char *p; + + assert( buf != NULL ); + + for ( pw = LBER_MIN_BUFF_SIZE; pw < minsize; pw <<= 1 ) { + if (pw > LBER_MAX_BUFF_SIZE) return -1; + } + + if ( buf->buf_size < pw ) { + p = LBER_REALLOC( buf->buf_base, pw ); + if ( p == NULL ) return -1; + buf->buf_base = p; + buf->buf_size = pw; + } + return 0; +} + +ber_len_t +ber_pvt_sb_copy_out( Sockbuf_Buf *sbb, char *buf, ber_len_t len ) +{ + ber_len_t max; + + assert( buf != NULL ); + assert( sbb != NULL ); +#if 0 + assert( sbb->buf_size > 0 ); +#endif + + max = sbb->buf_end - sbb->buf_ptr; + max = ( max < len) ? max : len; + if ( max ) { + AC_MEMCPY( buf, sbb->buf_base + sbb->buf_ptr, max ); + sbb->buf_ptr += max; + if ( sbb->buf_ptr >= sbb->buf_end ) { + sbb->buf_ptr = sbb->buf_end = 0; + } + } + return max; +} + +ber_slen_t +ber_pvt_sb_do_write( Sockbuf_IO_Desc *sbiod, Sockbuf_Buf *buf_out ) +{ + ber_len_t to_go; + ber_slen_t ret; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + to_go = buf_out->buf_end - buf_out->buf_ptr; + assert( to_go > 0 ); + + for(;;) { + ret = LBER_SBIOD_WRITE_NEXT( sbiod, buf_out->buf_base + + buf_out->buf_ptr, to_go ); +#ifdef EINTR + if ((ret<0) && (errno==EINTR)) continue; +#endif + break; + } + + if ( ret <= 0 ) return ret; + + buf_out->buf_ptr += ret; + if (buf_out->buf_ptr == buf_out->buf_end) { + buf_out->buf_end = buf_out->buf_ptr = 0; + } + + return ret; +} + +int +ber_pvt_socket_set_nonblock( ber_socket_t sd, int nb ) +{ +#ifdef HAVE_FCNTL + int flags = fcntl( sd, F_GETFL); + if( nb ) { + flags |= O_NONBLOCK; + } else { + flags &= ~O_NONBLOCK; + } + return fcntl( sd, F_SETFL, flags ); + +#elif defined( FIONBIO ) + ioctl_t status = nb ? 1 : 0; + return ioctl( sd, FIONBIO, &status ); +#endif +} + +int +ber_int_sb_init( Sockbuf *sb ) +{ + assert( sb != NULL); + + sb->sb_valid=LBER_VALID_SOCKBUF; + sb->sb_options = 0; + sb->sb_debug = ber_int_debug; + sb->sb_fd = AC_SOCKET_INVALID; + sb->sb_iod = NULL; + sb->sb_trans_needs_read = 0; + sb->sb_trans_needs_write = 0; + + assert( SOCKBUF_VALID( sb ) ); + return 0; +} + +int +ber_int_sb_close( Sockbuf *sb ) +{ + Sockbuf_IO_Desc *p; + + assert( sb != NULL); + + p = sb->sb_iod; + while ( p ) { + if ( p->sbiod_io->sbi_close && p->sbiod_io->sbi_close( p ) < 0 ) { + return -1; + } + p = p->sbiod_next; + } + + sb->sb_fd = AC_SOCKET_INVALID; + + return 0; +} + +int +ber_int_sb_destroy( Sockbuf *sb ) +{ + Sockbuf_IO_Desc *p; + + assert( sb != NULL); + assert( SOCKBUF_VALID( sb ) ); + + while ( sb->sb_iod ) { + p = sb->sb_iod->sbiod_next; + ber_sockbuf_remove_io( sb, sb->sb_iod->sbiod_io, + sb->sb_iod->sbiod_level ); + sb->sb_iod = p; + } + + return ber_int_sb_init( sb ); +} + +ber_slen_t +ber_int_sb_read( Sockbuf *sb, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + + assert( buf != NULL ); + assert( sb != NULL); + assert( sb->sb_iod != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + for (;;) { + ret = sb->sb_iod->sbiod_io->sbi_read( sb->sb_iod, buf, len ); + +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) continue; +#endif + break; + } + + return ret; +} + +ber_slen_t +ber_int_sb_write( Sockbuf *sb, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + + assert( buf != NULL ); + assert( sb != NULL); + assert( sb->sb_iod != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + for (;;) { + ret = sb->sb_iod->sbiod_io->sbi_write( sb->sb_iod, buf, len ); + +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) continue; +#endif + break; + } + + return ret; +} + +/* + * Support for TCP + */ + +static ber_slen_t +sb_stream_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + +#if defined(MACOS) +/* + * MacTCP/OpenTransport + */ + return tcpread( sbiod->sbiod_sb->sb_fd, 0, (unsigned char *)buf, + len, NULL ); + +#elif defined( HAVE_PCNFS ) || \ + defined( HAVE_WINSOCK ) || defined ( __BEOS__ ) +/* + * PCNFS (under DOS) + */ +/* + * Windows Socket API (under DOS/Windows 3.x) + */ +/* + * 32-bit Windows Socket API (under Windows NT or Windows 95) + */ + return recv( sbiod->sbiod_sb->sb_fd, buf, len, 0 ); + +#elif defined( HAVE_NCSA ) +/* + * NCSA Telnet TCP/IP stack (under DOS) + */ + return nread( sbiod->sbiod_sb->sb_fd, buf, len ); + +#else + return read( sbiod->sbiod_sb->sb_fd, buf, len ); +#endif +} + +static ber_slen_t +sb_stream_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + +#if defined(MACOS) +/* + * MacTCP/OpenTransport + */ +#define MAX_WRITE 65535 + return tcpwrite( sbiod->sbiod_sb->sb_fd, (unsigned char *)buf, + (len<MAX_WRITE) ? len : MAX_WRITE ); + +#elif defined( HAVE_PCNFS) \ + || defined( HAVE_WINSOCK) || defined ( __BEOS__ ) +/* + * PCNFS (under DOS) + */ +/* + * Windows Socket API (under DOS/Windows 3.x) + */ +/* + * 32-bit Windows Socket API (under Windows NT or Windows 95) + */ + return send( sbiod->sbiod_sb->sb_fd, buf, len, 0 ); + +#elif defined(HAVE_NCSA) + return netwrite( sbiod->sbiod_sb->sb_fd, buf, len ); + +#elif defined(VMS) +/* + * VMS -- each write must be 64K or smaller + */ +#define MAX_WRITE 65535 + return write( sbiod->sbiod_sb->sb_fd, buf, + (len<MAX_WRITE) ? len : MAX_WRITE); +#else + return write( sbiod->sbiod_sb->sb_fd, buf, len ); +#endif +} + +static int +sb_stream_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID ) + tcp_close( sbiod->sbiod_sb->sb_fd ); + return 0; +} + +/* The argument is a pointer to the socket descriptor */ +static int +sb_stream_setup( Sockbuf_IO_Desc *sbiod, void *arg ) { + assert( sbiod != NULL ); + + if ( arg != NULL ) { + sbiod->sbiod_sb->sb_fd = *((int *)arg); + } + return 0; +} + +static int +sb_stream_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) { + /* This is an end IO descriptor */ + return 0; +} + +Sockbuf_IO ber_sockbuf_io_tcp = { + sb_stream_setup, /* sbi_setup */ + NULL, /* sbi_remove */ + sb_stream_ctrl, /* sbi_ctrl */ + sb_stream_read, /* sbi_read */ + sb_stream_write, /* sbi_write */ + sb_stream_close /* sbi_close */ +}; + + +/* + * Support for readahead (UDP needs it) + */ + +static int +sb_rdahead_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + Sockbuf_Buf *p; + + assert( sbiod != NULL ); + + p = LBER_MALLOC( sizeof( *p ) ); + if ( p == NULL ) return -1; + + ber_pvt_sb_buf_init( p ); + + if ( arg == NULL ) { + ber_pvt_sb_grow_buffer( p, LBER_DEFAULT_READAHEAD ); + } else { + ber_pvt_sb_grow_buffer( p, *((int *)arg) ); + } + + sbiod->sbiod_pvt = p; + return 0; +} + +static int +sb_rdahead_remove( Sockbuf_IO_Desc *sbiod ) +{ + Sockbuf_Buf *p; + + assert( sbiod != NULL ); + + p = (Sockbuf_Buf *)sbiod->sbiod_pvt; + + if ( p->buf_ptr != p->buf_end ) return -1; + + ber_pvt_sb_buf_destroy( (Sockbuf_Buf *)(sbiod->sbiod_pvt) ); + LBER_FREE( sbiod->sbiod_pvt ); + sbiod->sbiod_pvt = NULL; + + return 0; +} + +static ber_slen_t +sb_rdahead_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + Sockbuf_Buf *p; + ber_slen_t bufptr = 0, ret, max; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + assert( sbiod->sbiod_next != NULL ); + + p = (Sockbuf_Buf *)sbiod->sbiod_pvt; + + assert( p->buf_size > 0 ); + + /* Are there anything left in the buffer? */ + ret = ber_pvt_sb_copy_out( p, buf, len ); + bufptr += ret; + len -= ret; + + if ( len == 0 ) return bufptr; + + max = p->buf_size - p->buf_end; + ret = 0; + while ( max > 0 ) { + ret = LBER_SBIOD_READ_NEXT( sbiod, p->buf_base + p->buf_end, + max ); +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) continue; +#endif + break; + } + + if ( ret < 0 ) { + return ( bufptr ? bufptr : ret ); + } + + p->buf_end += ret; + bufptr += ber_pvt_sb_copy_out( p, (char *) buf + bufptr, len ); + return bufptr; +} + +static ber_slen_t +sb_rdahead_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL ); + assert( sbiod->sbiod_next != NULL ); + + return LBER_SBIOD_WRITE_NEXT( sbiod, buf, len ); +} + +static int +sb_rdahead_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + + /* Just erase the buffer */ + ber_pvt_sb_buf_destroy((Sockbuf_Buf *)sbiod->sbiod_pvt); + return 0; +} + +static int +sb_rdahead_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + Sockbuf_Buf *p; + + p = (Sockbuf_Buf *)sbiod->sbiod_pvt; + + if ( opt == LBER_SB_OPT_DATA_READY ) { + if ( p->buf_ptr != p->buf_end ) { + return 1; + } + + } else if ( opt == LBER_SB_OPT_SET_READAHEAD ) { + if ( p->buf_size >= *((ber_len_t *)arg) ) { + return 0; + } + return ( ber_pvt_sb_grow_buffer( p, *((int *)arg) ) ? + -1 : 1 ); + } + + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +Sockbuf_IO ber_sockbuf_io_readahead = { + sb_rdahead_setup, /* sbi_setup */ + sb_rdahead_remove, /* sbi_remove */ + sb_rdahead_ctrl, /* sbi_ctrl */ + sb_rdahead_read, /* sbi_read */ + sb_rdahead_write, /* sbi_write */ + sb_rdahead_close /* sbi_close */ +}; + +/* + * Support for simple file IO + */ + +static ber_slen_t +sb_fd_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + +#ifdef LDAP_PF_LOCAL_SENDMSG + if ( sbiod->sbiod_sb->sb_ungetlen ) { + ber_len_t blen = sbiod->sbiod_sb->sb_ungetlen; + if ( blen > len ) + blen = len; + AC_MEMCPY( buf, sbiod->sbiod_sb->sb_ungetbuf, blen ); + buf = (char *) buf + blen; + len -= blen; + sbiod->sbiod_sb->sb_ungetlen -= blen; + if ( sbiod->sbiod_sb->sb_ungetlen ) { + AC_MEMCPY( sbiod->sbiod_sb->sb_ungetbuf, + sbiod->sbiod_sb->sb_ungetbuf+blen, + sbiod->sbiod_sb->sb_ungetlen ); + } + if ( len == 0 ) + return blen; + } +#endif + return read( sbiod->sbiod_sb->sb_fd, buf, len ); +} + +static ber_slen_t +sb_fd_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + return write( sbiod->sbiod_sb->sb_fd, buf, len ); +} + +static int +sb_fd_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID ) + close( sbiod->sbiod_sb->sb_fd ); + return 0; +} + +/* The argument is a pointer to the file descriptor */ +static int +sb_fd_setup( Sockbuf_IO_Desc *sbiod, void *arg ) { + assert( sbiod != NULL ); + + if ( arg != NULL ) + sbiod->sbiod_sb->sb_fd = *((int *)arg); + return 0; +} + +static int +sb_fd_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) { + /* This is an end IO descriptor */ + return 0; +} + +Sockbuf_IO ber_sockbuf_io_fd = { + sb_fd_setup, /* sbi_setup */ + NULL, /* sbi_remove */ + sb_fd_ctrl, /* sbi_ctrl */ + sb_fd_read, /* sbi_read */ + sb_fd_write, /* sbi_write */ + sb_fd_close /* sbi_close */ +}; + +/* + * Debugging layer + */ + +static int +sb_debug_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + assert( sbiod != NULL ); + + if ( arg == NULL ) arg = "sockbuf_"; + + sbiod->sbiod_pvt = LBER_MALLOC( strlen( arg ) + 1 ); + if ( sbiod->sbiod_pvt == NULL ) return -1; + + strcpy( (char *)sbiod->sbiod_pvt, (char *)arg ); + return 0; +} + +static int +sb_debug_remove( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + LBER_FREE( sbiod->sbiod_pvt ); + sbiod->sbiod_pvt = NULL; + return 0; +} + +static int +sb_debug_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +static ber_slen_t +sb_debug_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + char ebuf[128]; + + ret = LBER_SBIOD_READ_NEXT( sbiod, buf, len ); + if (sbiod->sbiod_sb->sb_debug & LDAP_DEBUG_PACKETS) { + int err = sock_errno(); + if ( ret < 0 ) { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%sread: want=%ld error=%s\n", (char *)sbiod->sbiod_pvt, + (long)len, AC_STRERROR_R( err, ebuf, sizeof ebuf ) ); + } else { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%sread: want=%ld, got=%ld\n", (char *)sbiod->sbiod_pvt, + (long)len, (long)ret ); + ber_log_bprint( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + (const char *)buf, ret ); + } + sock_errset(err); + } + return ret; +} + +static ber_slen_t +sb_debug_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + char ebuf[128]; + + ret = LBER_SBIOD_WRITE_NEXT( sbiod, buf, len ); + if (sbiod->sbiod_sb->sb_debug & LDAP_DEBUG_PACKETS) { + int err = sock_errno(); + if ( ret < 0 ) { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%swrite: want=%ld error=%s\n", + (char *)sbiod->sbiod_pvt, (long)len, + AC_STRERROR_R( err, ebuf, sizeof ebuf ) ); + } else { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%swrite: want=%ld, written=%ld\n", + (char *)sbiod->sbiod_pvt, (long)len, (long)ret ); + ber_log_bprint( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + (const char *)buf, ret ); + } + sock_errset(err); + } + + return ret; +} + +Sockbuf_IO ber_sockbuf_io_debug = { + sb_debug_setup, /* sbi_setup */ + sb_debug_remove, /* sbi_remove */ + sb_debug_ctrl, /* sbi_ctrl */ + sb_debug_read, /* sbi_read */ + sb_debug_write, /* sbi_write */ + NULL /* sbi_close */ +}; + +#ifdef LDAP_CONNECTIONLESS + +/* + * Support for UDP (CLDAP) + * + * All I/O at this level must be atomic. For ease of use, the sb_readahead + * must be used above this module. All data reads and writes are prefixed + * with a sockaddr_storage containing the address of the remote entity. Upper levels + * must read and write this sockaddr_storage before doing the usual ber_printf/scanf + * operations on LDAP messages. + */ + +static int +sb_dgram_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + if ( arg != NULL ) sbiod->sbiod_sb->sb_fd = *((int *)arg); + return 0; +} + +static ber_slen_t +sb_dgram_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t rc; + ber_socklen_t addrlen; + struct sockaddr *src; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + assert( buf != NULL ); + + addrlen = sizeof( struct sockaddr_storage ); + src = buf; + buf = (char *) buf + addrlen; + len -= addrlen; + rc = recvfrom( sbiod->sbiod_sb->sb_fd, buf, len, 0, src, &addrlen ); + + return rc > 0 ? rc+sizeof(struct sockaddr_storage) : rc; +} + +static ber_slen_t +sb_dgram_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t rc; + struct sockaddr *dst; + socklen_t dstsize; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + assert( buf != NULL ); + + dst = buf; + buf = (char *) buf + sizeof( struct sockaddr_storage ); + len -= sizeof( struct sockaddr_storage ); + dstsize = dst->sa_family == AF_INET ? sizeof( struct sockaddr_in ) +#ifdef LDAP_PF_INET6 + : dst->sa_family == AF_INET6 ? sizeof( struct sockaddr_in6 ) +#endif + : sizeof( struct sockaddr_storage ); + rc = sendto( sbiod->sbiod_sb->sb_fd, buf, len, 0, dst, dstsize ); + + if ( rc < 0 ) return -1; + + /* fake error if write was not atomic */ + if (rc < len) { +# ifdef EMSGSIZE + errno = EMSGSIZE; +# endif + return -1; + } + rc = len + sizeof(struct sockaddr_storage); + return rc; +} + +static int +sb_dgram_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID ) + tcp_close( sbiod->sbiod_sb->sb_fd ); + return 0; +} + +static int +sb_dgram_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + /* This is an end IO descriptor */ + return 0; +} + +Sockbuf_IO ber_sockbuf_io_udp = +{ + sb_dgram_setup, /* sbi_setup */ + NULL, /* sbi_remove */ + sb_dgram_ctrl, /* sbi_ctrl */ + sb_dgram_read, /* sbi_read */ + sb_dgram_write, /* sbi_write */ + sb_dgram_close /* sbi_close */ +}; + +#endif /* LDAP_CONNECTIONLESS */ diff --git a/libraries/liblber/stdio.c b/libraries/liblber/stdio.c new file mode 100644 index 0000000..064096b --- /dev/null +++ b/libraries/liblber/stdio.c @@ -0,0 +1,243 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdarg.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <lutil.h> + +#if !defined(HAVE_VSNPRINTF) && !defined(HAVE_EBCDIC) +/* Write at most n characters to the buffer in str, return the + * number of chars written or -1 if the buffer would have been + * overflowed. + * + * This is portable to any POSIX-compliant system. We use pipe() + * to create a valid file descriptor, and then fdopen() it to get + * a valid FILE pointer. The user's buffer and size are assigned + * to the FILE pointer using setvbuf. Then we close the read side + * of the pipe to invalidate the descriptor. + * + * If the write arguments all fit into size n, the write will + * return successfully. If the write is too large, the stdio + * buffer will need to be flushed to the underlying file descriptor. + * The flush will fail because it is attempting to write to a + * broken pipe, and the write will be terminated. + * -- hyc, 2002-07-19 + */ +/* This emulation uses vfprintf; on OS/390 we're also emulating + * that function so it's more efficient just to have a separate + * version of vsnprintf there. + */ +#include <ac/signal.h> +int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap ) +{ + int fds[2], res; + FILE *f; + RETSIGTYPE (*sig)(); + + if (pipe( fds )) return -1; + + f = fdopen( fds[1], "w" ); + if ( !f ) { + close( fds[1] ); + close( fds[0] ); + return -1; + } + setvbuf( f, str, _IOFBF, n ); + sig = signal( SIGPIPE, SIG_IGN ); + close( fds[0] ); + + res = vfprintf( f, fmt, ap ); + + fclose( f ); + signal( SIGPIPE, sig ); + if ( res > 0 && res < n ) { + res = vsprintf( str, fmt, ap ); + } + return res; +} +#endif + +#ifndef HAVE_SNPRINTF +int ber_pvt_snprintf( char *str, size_t n, const char *fmt, ... ) +{ + va_list ap; + int res; + + va_start( ap, fmt ); + res = vsnprintf( str, n, fmt, ap ); + va_end( ap ); + return res; +} +#endif /* !HAVE_SNPRINTF */ + +#ifdef HAVE_EBCDIC +/* stdio replacements with ASCII/EBCDIC translation for OS/390. + * The OS/390 port depends on the CONVLIT compiler option being + * used to force character and string literals to be compiled in + * ISO8859-1, and the __LIBASCII cpp symbol to be defined to use the + * OS/390 ASCII-compatibility library. This library only supplies + * an ASCII version of sprintf, so other needed functions are + * provided here. + * + * All of the internal character manipulation is done in ASCII, + * but file I/O is EBCDIC, so we catch any stdio reading/writing + * of files here and do the translations. + */ + +#undef fputs +#undef fgets + +char *ber_pvt_fgets( char *s, int n, FILE *fp ) +{ + s = (char *)fgets( s, n, fp ); + if ( s ) __etoa( s ); + return s; +} + +int ber_pvt_fputs( const char *str, FILE *fp ) +{ + char buf[8192]; + + strncpy( buf, str, sizeof(buf) ); + __atoe( buf ); + return fputs( buf, fp ); +} + +/* The __LIBASCII doesn't include a working vsprintf, so we make do + * using just sprintf. This is a very simplistic parser that looks for + * format strings and uses sprintf to process them one at a time. + * Literal text is just copied straight to the destination. + * The result is appended to the destination string. The parser + * recognizes field-width specifiers and the 'l' qualifier; it + * may need to be extended to recognize other qualifiers but so + * far this seems to be enough. + */ +int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap ) +{ + char *ptr, *pct, *s2, *f2, *end; + char fm2[64]; + int len, rem; + + ptr = (char *)fmt; + s2 = str; + fm2[0] = '%'; + if (n) { + end = str + n; + } else { + end = NULL; + } + + for (pct = strchr(ptr, '%'); pct; pct = strchr(ptr, '%')) { + len = pct-ptr; + if (end) { + rem = end-s2; + if (rem < 1) return -1; + if (rem < len) len = rem; + } + s2 = lutil_strncopy( s2, ptr, len ); + /* Did we cheat the length above? If so, bail out */ + if (len < pct-ptr) return -1; + for (pct++, f2 = fm2+1; isdigit(*pct);) *f2++ = *pct++; + if (*pct == 'l') *f2++ = *pct++; + if (*pct == '%') { + *s2++ = '%'; + } else { + *f2++ = *pct; + *f2 = '\0'; + if (*pct == 's') { + char *ss = va_arg(ap, char *); + /* Attempt to limit sprintf output. This + * may be thrown off if field widths were + * specified for this string. + * + * If it looks like the string is too + * long for the remaining buffer, bypass + * sprintf and just copy what fits, then + * quit. + */ + if (end && strlen(ss) > (rem=end-s2)) { + strncpy(s2, ss, rem); + return -1; + } else { + s2 += sprintf(s2, fm2, ss); + } + } else { + s2 += sprintf(s2, fm2, va_arg(ap, int)); + } + } + ptr = pct + 1; + } + if (end) { + rem = end-s2; + if (rem > 0) { + len = strlen(ptr); + s2 = lutil_strncopy( s2, ptr, rem ); + rem -= len; + } + if (rem < 0) return -1; + } else { + s2 = lutil_strcopy( s2, ptr ); + } + return s2 - str; +} + +int ber_pvt_vsprintf( char *str, const char *fmt, va_list ap ) +{ + return vsnprintf( str, 0, fmt, ap ); +} + +/* The fixed buffer size here is a problem, we don't know how + * to flush the buffer and keep printing if the msg is too big. + * Hopefully we never try to write something bigger than this + * in a log msg... + */ +int ber_pvt_vfprintf( FILE *fp, const char *fmt, va_list ap ) +{ + char buf[8192]; + int res; + + vsnprintf( buf, sizeof(buf), fmt, ap ); + __atoe( buf ); + res = fputs( buf, fp ); + if (res == EOF) res = -1; + return res; +} + +int ber_pvt_printf( const char *fmt, ... ) +{ + va_list ap; + int res; + + va_start( ap, fmt ); + res = ber_pvt_vfprintf( stdout, fmt, ap ); + va_end( ap ); + return res; +} + +int ber_pvt_fprintf( FILE *fp, const char *fmt, ... ) +{ + va_list ap; + int res; + + va_start( ap, fmt ); + res = ber_pvt_vfprintf( fp, fmt, ap ); + va_end( ap ); + return res; +} +#endif |