diff --git a/include/lutil.h b/include/lutil.h index 9e40663201..2400624159 100644 --- a/include/lutil.h +++ b/include/lutil.h @@ -304,6 +304,9 @@ lutil_atoulx( unsigned long *v, const char *s, int x ); #define lutil_atol(v, s) lutil_atolx((v), (s), 10) #define lutil_atoul(v, s) lutil_atoulx((v), (s), 10) +LDAP_LUTIL_F (int) +lutil_str2bin( struct berval *in, struct berval *out ); + /* Parse and unparse time intervals */ LDAP_LUTIL_F (int) lutil_parse_time( const char *in, unsigned long *tp ); diff --git a/libraries/liblutil/utils.c b/libraries/liblutil/utils.c index 2cd105ed92..1c6fd0e294 100644 --- a/libraries/liblutil/utils.c +++ b/libraries/liblutil/utils.c @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef HAVE_IO_H #include #endif @@ -602,6 +603,169 @@ lutil_atoulx( unsigned long *v, const char *s, int x ) return 0; } +/* Multiply an integer by 100000000 and add new */ +typedef struct _decnum { + unsigned char *buf; + int bufsiz; + int beg; + int len; +} _decnum; + +#define FACTOR1 (100000000&0xffff) +#define FACTOR2 (100000000>>16) + +static void +scale( int new, _decnum *prev, unsigned char *tmp ) +{ + int i, j; + unsigned char *in = prev->buf+prev->beg; + unsigned int part; + unsigned char *out = tmp + prev->bufsiz - prev->len; + + memset( tmp, 0, prev->bufsiz ); + if ( prev->len ) { + for ( i = prev->len-1; i>=0; i-- ) { + part = in[i] * FACTOR1; + for ( j = i; part; j-- ) { + part += out[j]; + out[j] = part & 0xff; + part >>= 8; + } + part = in[i] * FACTOR2; + for ( j = i-2; part; j-- ) { + part += out[j]; + out[j] = part & 0xff; + part >>= 8; + } + } + j++; + prev->beg += j; + prev->len -= j; + } + + out = tmp + prev->bufsiz - 1; + for ( i = 0; new ; i-- ) { + new += out[i]; + out[i] = new & 0xff; + new >>= 8; + if (!new ) { + if ( !prev->len ) { + prev->beg += i; + prev->len = -i; + prev->len++; + } + break; + } + } + AC_MEMCPY( prev->buf+prev->beg, tmp+prev->beg, prev->len ); +} + +/* Convert unlimited length decimal or hex string to binary. + * Output buffer must be provided, bv_len must indicate buffer size + * Hex input can be "0x1234" or "'1234'H" + */ +int +lutil_str2bin( struct berval *in, struct berval *out ) +{ + char *pin, *pout, ctmp; + char *end; + long l; + int i, chunk, len, rc = 0, hex = 0; + if ( !out || !out->bv_val || out->bv_len < in->bv_len ) + return -1; + + pout = out->bv_val; + /* Leading "0x" for hex input */ + if ( in->bv_len > 2 && in->bv_val[0] == '0' && + ( in->bv_val[1] == 'x' || in->bv_val[1] == 'X' )) { + len = in->bv_len - 2; + pin = in->bv_val + 2; + hex = 1; + } else if ( in->bv_len > 3 && in->bv_val[0] == '\'' && + ( in->bv_val[in->bv_len-2] == '\'' && + in->bv_val[in->bv_len-1] == 'H' )) { + len = in->bv_len - 3; + pin = in->bv_val + 1; + hex = 1; + } + if ( hex ) { +#define HEXMAX (2 * sizeof(long)) + /* Convert a longword at a time, but handle leading + * odd bytes first + */ + chunk = len & (HEXMAX-1); + if ( !chunk ) + chunk = HEXMAX; + + while ( len ) { + ctmp = pin[chunk]; + pin[chunk] = '\0'; + errno = 0; + l = strtol( pin, &end, 16 ); + pin[chunk] = ctmp; + if ( errno ) + return -1; + chunk++; + chunk >>= 1; + for ( i = chunk; i>=0; i-- ) { + pout[i] = l & 0xff; + l >>= 8; + } + pin += chunk; + pout += sizeof(long); + len -= chunk; + chunk = HEXMAX; + } + out->bv_len = pout + len - out->bv_val; + } else { + /* Decimal */ + char tmpbuf[64], *tmp; + _decnum num; + + len = in->bv_len; + pin = in->bv_val; + num.buf = out->bv_val; + num.bufsiz = out->bv_len; + num.beg = num.bufsiz-1; + num.len = 0; + +#define DECMAX 8 /* 8 digits at a time */ + + if ( len > sizeof(tmpbuf)) { + tmp = ber_memalloc( len ); + } else { + tmp = tmpbuf; + } + chunk = len & (DECMAX-1); + if ( !chunk ) + chunk = DECMAX; + + while ( len ) { + ctmp = pin[chunk]; + pin[chunk] = '\0'; + errno = 0; + l = strtol( pin, &end, 10 ); + pin[chunk] = ctmp; + if ( errno ) { + rc = -1; + goto decfail; + } + scale( l, &num, tmp ); + pin += chunk; + len -= chunk; + chunk = HEXMAX; + } + if ( num.beg ) + AC_MEMCPY( num.buf, num.buf+num.beg, num.len ); + out->bv_len = num.len; +decfail: + if ( tmp != tmpbuf ) { + ber_memfree( tmp ); + } + } + return rc; +} + static char time_unit[] = "dhms"; /* Used to parse and unparse time intervals, not timestamps */