openldap/libraries/liblber/io.c
Kurt Zeilenga b5494457d8 Remove extern declarations of library functions from source.c.
This could cause problems on odd systems.  The generic
  headers should be extended as needed to include necessary
  system headers or, if necessary, make explicit declarations.
Extended ac/string.h header to look for string.h/strings.h if
  STDC_HEADERS is not defined.  Also provide basic declarations for
  str*() functions.  This could cause problems on odd systems.
Extended ac/unistd.h header to define basic declaration for misc
  functions that might be missing from headers.   This includes
  externs for getenv(), getopt(), mktemp(), tempname().
Protect fax500.h from multiple inclusion.  Moved includes of
  system/generic headers back to source files.
Made mail500 helper functions static.
Fixed includes of ctype.h, signal.h, etc. to use generics.
lutil/tempname.c: was including stdlib.h twice, one should stdio.h.
Wrapped <sys/resource.h> with HAVE_SYS_RESOURCE_H.
lber/io.c/ber_get_next(): Changed noctets back to signed.
  Used with BerRead which expects signed int as second arg and
  returns signed int.
1998-11-16 05:07:27 +00:00

586 lines
13 KiB
C

/* io.c - ber general i/o routines */
/*
* 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.
*/
#include "portable.h"
#include <stdio.h>
#include <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"
static int ber_realloc LDAP_P(( BerElement *ber, unsigned long len ));
static int ber_filbuf LDAP_P(( Sockbuf *sb, long len ));
static long BerRead LDAP_P(( Sockbuf *sb, char *buf, long len ));
#ifdef PCNFS
static int BerWrite LDAP_P(( Sockbuf *sb, char *buf, long len ));
#endif /* PCNFS */
#define bergetc( sb, len ) ( sb->sb_ber.ber_end > sb->sb_ber.ber_ptr ? \
(unsigned char)*sb->sb_ber.ber_ptr++ : \
ber_filbuf( sb, len ))
#ifdef MACOS
/*
* MacTCP/OpenTransport
*/
#define read( s, b, l ) tcpread( s, 0, (unsigned char *)b, l, NULL )
#define MAX_WRITE 65535
#define BerWrite( sb, b, l ) tcpwrite( sb->sb_sd, (unsigned char *)(b), (l<MAX_WRITE)? l : MAX_WRITE )
#else /* MACOS */
#ifdef DOS
#ifdef PCNFS
/*
* PCNFS (under DOS)
*/
#define read( s, b, l ) recv( s, b, l, 0 )
#define BerWrite( s, b, l ) send( s->sb_sd, b, (int) l, 0 )
#endif /* PCNFS */
#ifdef NCSA
/*
* NCSA Telnet TCP/IP stack (under DOS)
*/
#define read( s, b, l ) nread( s, b, l )
#define BerWrite( s, b, l ) netwrite( s->sb_sd, b, l )
#endif /* NCSA */
#ifdef WINSOCK
/*
* Windows Socket API (under DOS/Windows 3.x)
*/
#define read( s, b, l ) recv( s, b, l, 0 )
#define BerWrite( s, b, l ) send( s->sb_sd, b, l, 0 )
#endif /* WINSOCK */
#else /* DOS */
#ifdef _WIN32
/*
* 32-bit Windows Socket API (under Windows NT or Windows 95)
*/
#define read( s, b, l ) recv( s, b, l, 0 )
#define BerWrite( s, b, l ) send( s->sb_sd, b, l, 0 )
#else /* _WIN32 */
#ifdef VMS
/*
* VMS -- each write must be 64K or smaller
*/
#define MAX_WRITE 65535
#define BerWrite( sb, b, l ) write( sb->sb_sd, b, (l<MAX_WRITE)? l : MAX_WRITE)
#else /* VMS */
/*
* everything else (Unix/BSD 4.3 socket API)
*/
#define BerWrite( sb, b, l ) write( sb->sb_sd, b, l )
#endif /* VMS */
#define udp_read( sb, b, l, al ) recvfrom(sb->sb_sd, (char *)b, l, 0, \
(struct sockaddr *)sb->sb_fromaddr, \
(al = sizeof(struct sockaddr), &al))
#define udp_write( sb, b, l ) sendto(sb->sb_sd, (char *)(b), l, 0, \
(struct sockaddr *)sb->sb_useaddr, sizeof(struct sockaddr))
#endif /* _WIN32 */
#endif /* DOS */
#endif /* MACOS */
#ifndef udp_read
#define udp_read( sb, b, l, al ) CLDAP NOT SUPPORTED
#define udp_write( sb, b, l ) CLDAP NOT SUPPORTED
#endif /* udp_read */
#define EXBUFSIZ 1024
static int
ber_filbuf( Sockbuf *sb, long len )
{
short rc;
#ifdef LDAP_CONNECTIONLESS
int addrlen;
#endif /* LDAP_CONNECTIONLESS */
if ( sb->sb_ber.ber_buf == NULL ) {
if ( (sb->sb_ber.ber_buf = (char *) malloc( READBUFSIZ )) ==
NULL )
return( -1 );
sb->sb_ber.ber_ptr = sb->sb_ber.ber_buf;
sb->sb_ber.ber_end = sb->sb_ber.ber_buf;
}
if ( sb->sb_naddr > 0 ) {
#ifdef LDAP_CONNECTIONLESS
rc = udp_read(sb, sb->sb_ber.ber_buf, READBUFSIZ, addrlen );
#ifdef LDAP_DEBUG
if ( lber_debug ) {
fprintf( stderr, "ber_filbuf udp_read %d bytes\n",
rc );
if ( lber_debug > 1 && rc > 0 )
lber_bprint( sb->sb_ber.ber_buf, rc );
}
#endif /* LDAP_DEBUG */
#else /* LDAP_CONNECTIONLESS */
rc = -1;
#endif /* LDAP_CONNECTIONLESS */
} else {
rc = read( sb->sb_sd, sb->sb_ber.ber_buf,
((sb->sb_options & LBER_NO_READ_AHEAD) && (len < READBUFSIZ)) ?
len : READBUFSIZ );
}
if ( rc > 0 ) {
sb->sb_ber.ber_ptr = sb->sb_ber.ber_buf + 1;
sb->sb_ber.ber_end = sb->sb_ber.ber_buf + rc;
return( (unsigned char)*sb->sb_ber.ber_buf );
}
return( -1 );
}
static long
BerRead( Sockbuf *sb, char *buf, long len )
{
int c;
long nread = 0;
while ( len > 0 ) {
if ( (c = bergetc( sb, len )) < 0 ) {
if ( nread > 0 )
break;
return( c );
}
*buf++ = (char) c;
nread++;
len--;
}
return( nread );
}
long
ber_read( BerElement *ber, char *buf, unsigned long len )
{
unsigned long actuallen, nleft;
nleft = ber->ber_end - ber->ber_ptr;
actuallen = nleft < len ? nleft : len;
SAFEMEMCPY( buf, ber->ber_ptr, (size_t)actuallen );
ber->ber_ptr += actuallen;
return( (long)actuallen );
}
long
ber_write( BerElement *ber, char *buf, unsigned long len, int nosos )
{
if ( nosos || ber->ber_sos == NULL ) {
if ( ber->ber_ptr + len > ber->ber_end ) {
if ( ber_realloc( ber, len ) != 0 )
return( -1 );
}
SAFEMEMCPY( ber->ber_ptr, buf, (size_t)len );
ber->ber_ptr += len;
return( len );
} else {
if ( ber->ber_sos->sos_ptr + len > ber->ber_end ) {
if ( ber_realloc( ber, len ) != 0 )
return( -1 );
}
SAFEMEMCPY( ber->ber_sos->sos_ptr, buf, (size_t)len );
ber->ber_sos->sos_ptr += len;
ber->ber_sos->sos_clen += len;
return( len );
}
}
static int
ber_realloc( BerElement *ber, unsigned long len )
{
unsigned long need, have, total;
Seqorset *s;
long off;
char *oldbuf;
have = (ber->ber_end - ber->ber_buf) / EXBUFSIZ;
need = (len < EXBUFSIZ ? 1 : (len + (EXBUFSIZ - 1)) / EXBUFSIZ);
total = have * EXBUFSIZ + need * EXBUFSIZ;
oldbuf = ber->ber_buf;
if ( ber->ber_buf == NULL ) {
if ( (ber->ber_buf = (char *) malloc( (size_t)total )) == NULL )
return( -1 );
} else if ( (ber->ber_buf = (char *) realloc( ber->ber_buf,
(size_t)total )) == NULL )
return( -1 );
ber->ber_end = ber->ber_buf + total;
/*
* If the stinking thing was moved, we need to go through and
* reset all the sos and ber pointers. Offsets would've been
* a better idea... oh well.
*/
if ( ber->ber_buf != oldbuf ) {
ber->ber_ptr = ber->ber_buf + (ber->ber_ptr - oldbuf);
for ( s = ber->ber_sos; s != NULLSEQORSET; s = s->sos_next ) {
off = s->sos_first - oldbuf;
s->sos_first = ber->ber_buf + off;
off = s->sos_ptr - oldbuf;
s->sos_ptr = ber->ber_buf + off;
}
}
return( 0 );
}
void
ber_free( BerElement *ber, int freebuf )
{
if ( freebuf && ber->ber_buf != NULL )
free( ber->ber_buf );
free( (char *) ber );
}
int
ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
{
long nwritten, towrite, rc;
if ( ber->ber_rwptr == NULL ) {
ber->ber_rwptr = ber->ber_buf;
}
towrite = ber->ber_ptr - ber->ber_rwptr;
#ifdef LDAP_DEBUG
if ( lber_debug ) {
fprintf( stderr, "ber_flush: %ld bytes to sd %ld%s\n", towrite,
(long) sb->sb_sd, ber->ber_rwptr != ber->ber_buf ? " (re-flush)"
: "" );
if ( lber_debug > 1 )
lber_bprint( ber->ber_rwptr, towrite );
}
#endif
#if !defined(MACOS) && !defined(DOS)
if ( sb->sb_options & (LBER_TO_FILE | LBER_TO_FILE_ONLY) ) {
rc = write( sb->sb_fd, ber->ber_buf, towrite );
if ( sb->sb_options & LBER_TO_FILE_ONLY ) {
return( (int)rc );
}
}
#endif
nwritten = 0;
do {
if (sb->sb_naddr > 0) {
#ifdef LDAP_CONNECTIONLESS
rc = udp_write( sb, ber->ber_buf + nwritten,
(size_t)towrite );
#else /* LDAP_CONNECTIONLESS */
rc = -1;
#endif /* LDAP_CONNECTIONLESS */
if ( rc <= 0 )
return( -1 );
/* fake error if write was not atomic */
if (rc < towrite) {
#ifdef EMSGSIZE
errno = EMSGSIZE;
#endif
return( -1 );
}
} else {
if ( (rc = BerWrite( sb, ber->ber_rwptr,
(size_t) towrite )) <= 0 ) {
return( -1 );
}
}
towrite -= rc;
nwritten += rc;
ber->ber_rwptr += rc;
} while ( towrite > 0 );
if ( freeit )
ber_free( ber, 1 );
return( 0 );
}
BerElement *
ber_alloc_t( int options )
{
BerElement *ber;
if ( (ber = (BerElement *) calloc( 1, sizeof(BerElement) )) == NULLBER )
return( NULLBER );
ber->ber_tag = LBER_DEFAULT;
ber->ber_options = (char) options;
return( ber );
}
BerElement *
ber_alloc( void )
{
return( ber_alloc_t( 0 ) );
}
BerElement *
der_alloc( void )
{
return( ber_alloc_t( LBER_USE_DER ) );
}
BerElement *
ber_dup( BerElement *ber )
{
BerElement *new;
if ( (new = ber_alloc()) == NULLBER )
return( NULLBER );
*new = *ber;
return( new );
}
void
ber_init_w_nullc( BerElement *ber, int options )
{
(void) memset( (char *)ber, '\0', sizeof( BerElement ));
ber->ber_tag = LBER_DEFAULT;
ber->ber_options = (char) options;
}
BerElement *
ber_init( struct berval *bv )
{
return ( NULL );
}
void
ber_reset( BerElement *ber, int was_writing )
{
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;
}
#ifdef LDAP_DEBUG
void
ber_dump( BerElement *ber, int inout )
{
fprintf( stderr, "ber_dump: buf 0x%lx, ptr 0x%lx, end 0x%lx\n",
(long) ber->ber_buf,
(long) ber->ber_ptr,
(long) ber->ber_end );
if ( inout == 1 ) {
fprintf( stderr, " current len %ld, contents:\n",
(long) (ber->ber_end - ber->ber_ptr) );
lber_bprint( ber->ber_ptr, ber->ber_end - ber->ber_ptr );
} else {
fprintf( stderr, " current len %ld, contents:\n",
(long) (ber->ber_ptr - ber->ber_buf) );
lber_bprint( ber->ber_buf, ber->ber_ptr - ber->ber_buf );
}
}
void
ber_sos_dump( Seqorset *sos )
{
fprintf( stderr, "*** sos dump ***\n" );
while ( sos != NULLSEQORSET ) {
fprintf( stderr, "ber_sos_dump: clen %ld first 0x%lx ptr 0x%lx\n",
(long) sos->sos_clen, (long) sos->sos_first, (long) sos->sos_ptr );
fprintf( stderr, " current len %ld contents:\n",
(long) (sos->sos_ptr - sos->sos_first) );
lber_bprint( sos->sos_first, sos->sos_ptr - sos->sos_first );
sos = sos->sos_next;
}
fprintf( stderr, "*** end dump ***\n" );
}
#endif
/* return the tag - LBER_DEFAULT returned means trouble */
static unsigned long
get_tag( Sockbuf *sb )
{
unsigned char xbyte;
unsigned long tag;
char *tagp;
unsigned int i;
if ( BerRead( sb, (char *) &xbyte, 1 ) != 1 )
return( LBER_DEFAULT );
if ( (xbyte & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK )
return( (unsigned long) xbyte );
tagp = (char *) &tag;
tagp[0] = xbyte;
for ( i = 1; i < sizeof(long); i++ ) {
if ( BerRead( sb, (char *) &xbyte, 1 ) != 1 )
return( LBER_DEFAULT );
tagp[i] = xbyte;
if ( ! (xbyte & LBER_MORE_TAG_MASK) )
break;
}
/* tag too big! */
if ( i == sizeof(long) )
return( LBER_DEFAULT );
/* want leading, not trailing 0's */
return( tag >> (sizeof(long) - i - 1) );
}
unsigned long
ber_get_next( Sockbuf *sb, unsigned long *len, BerElement *ber )
{
unsigned long tag = 0, netlen, toread;
unsigned char lc;
long rc;
int noctets;
unsigned int diff;
#ifdef LDAP_DEBUG
if ( lber_debug )
fprintf( stderr, "ber_get_next\n" );
#endif
/*
* 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
*/
/*
* first time through - malloc the buffer, set up ptrs, and
* read the tag and the length and as much of the rest as we can
*/
if ( ber->ber_rwptr == NULL ) {
/*
* First, we read the tag.
*/
if ( (tag = get_tag( sb )) == LBER_DEFAULT ) {
return( LBER_DEFAULT );
}
ber->ber_tag = tag;
/*
* Next, read the length. The first byte contains the length
* of the length. If bit 8 is set, the length is the long
* form, otherwise it's the short form. We don't allow a
* length that's greater than what we can hold in an unsigned
* long.
*/
*len = netlen = 0;
if ( BerRead( sb, (char *) &lc, 1 ) != 1 ) {
return( LBER_DEFAULT );
}
if ( lc & 0x80 ) {
noctets = (lc & 0x7f);
if ( noctets > sizeof(unsigned long) )
return( LBER_DEFAULT );
diff = sizeof(unsigned long) - noctets;
if ( BerRead( sb, (char *) &netlen + diff, noctets ) !=
noctets ) {
return( LBER_DEFAULT );
}
*len = AC_NTOHL( netlen );
} else {
*len = lc;
}
ber->ber_len = *len;
/*
* Finally, malloc a buffer for the contents and read it in.
* It's this buffer that's passed to all the other ber decoding
* routines.
*/
#if defined( DOS ) && !defined( _WIN32 )
if ( *len > 65535 ) { /* DOS can't allocate > 64K */
return( LBER_DEFAULT );
}
#endif /* DOS && !_WIN32 */
if ( ( sb->sb_options & LBER_MAX_INCOMING_SIZE ) &&
*len > (unsigned long) sb->sb_max_incoming ) {
return( LBER_DEFAULT );
}
if ( (ber->ber_buf = (char *) malloc( (size_t)*len )) == NULL ) {
return( LBER_DEFAULT );
}
ber->ber_ptr = ber->ber_buf;
ber->ber_end = ber->ber_buf + *len;
ber->ber_rwptr = ber->ber_buf;
}
toread = (unsigned long)ber->ber_end - (unsigned long)ber->ber_rwptr;
do {
if ( (rc = BerRead( sb, ber->ber_rwptr, (long)toread )) <= 0 ) {
return( LBER_DEFAULT );
}
toread -= rc;
ber->ber_rwptr += rc;
} while ( toread > 0 );
#ifdef LDAP_DEBUG
if ( lber_debug ) {
fprintf( stderr, "ber_get_next: tag 0x%lx len %ld contents:\n",
tag, ber->ber_len );
if ( lber_debug > 1 )
ber_dump( ber, 1 );
}
#endif
*len = ber->ber_len;
ber->ber_rwptr = NULL;
return( ber->ber_tag );
}