d300320fbe
I've been holding back on this because 1.7.0 requires OpenSSL 1.1.0 or newer for full DANE support. But we can't wait forever, and nothing in base uses DANE anyway, so here we go.
774 lines
18 KiB
C
774 lines
18 KiB
C
/*
|
|
* util.c
|
|
*
|
|
* some general memory functions
|
|
*
|
|
* a Net::DNS like library for C
|
|
*
|
|
* (c) NLnet Labs, 2004-2006
|
|
*
|
|
* See the file LICENSE for the license
|
|
*/
|
|
|
|
#include <ldns/config.h>
|
|
|
|
#include <ldns/rdata.h>
|
|
#include <ldns/rr.h>
|
|
#include <ldns/util.h>
|
|
#include <strings.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
|
|
#ifdef HAVE_SSL
|
|
#include <openssl/rand.h>
|
|
#endif
|
|
|
|
ldns_lookup_table *
|
|
ldns_lookup_by_name(ldns_lookup_table *table, const char *name)
|
|
{
|
|
while (table->name != NULL) {
|
|
if (strcasecmp(name, table->name) == 0)
|
|
return table;
|
|
table++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ldns_lookup_table *
|
|
ldns_lookup_by_id(ldns_lookup_table *table, int id)
|
|
{
|
|
while (table->name != NULL) {
|
|
if (table->id == id)
|
|
return table;
|
|
table++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
ldns_get_bit(uint8_t bits[], size_t index)
|
|
{
|
|
/*
|
|
* The bits are counted from left to right, so bit #0 is the
|
|
* left most bit.
|
|
*/
|
|
return (int) (bits[index / 8] & (1 << (7 - index % 8)));
|
|
}
|
|
|
|
int
|
|
ldns_get_bit_r(uint8_t bits[], size_t index)
|
|
{
|
|
/*
|
|
* The bits are counted from right to left, so bit #0 is the
|
|
* right most bit.
|
|
*/
|
|
return (int) bits[index / 8] & (1 << (index % 8));
|
|
}
|
|
|
|
void
|
|
ldns_set_bit(uint8_t *byte, int bit_nr, bool value)
|
|
{
|
|
/*
|
|
* The bits are counted from right to left, so bit #0 is the
|
|
* right most bit.
|
|
*/
|
|
if (bit_nr >= 0 && bit_nr < 8) {
|
|
if (value) {
|
|
*byte = *byte | (0x01 << bit_nr);
|
|
} else {
|
|
*byte = *byte & ~(0x01 << bit_nr);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
ldns_hexdigit_to_int(char ch)
|
|
{
|
|
switch (ch) {
|
|
case '0': return 0;
|
|
case '1': return 1;
|
|
case '2': return 2;
|
|
case '3': return 3;
|
|
case '4': return 4;
|
|
case '5': return 5;
|
|
case '6': return 6;
|
|
case '7': return 7;
|
|
case '8': return 8;
|
|
case '9': return 9;
|
|
case 'a': case 'A': return 10;
|
|
case 'b': case 'B': return 11;
|
|
case 'c': case 'C': return 12;
|
|
case 'd': case 'D': return 13;
|
|
case 'e': case 'E': return 14;
|
|
case 'f': case 'F': return 15;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
char
|
|
ldns_int_to_hexdigit(int i)
|
|
{
|
|
switch (i) {
|
|
case 0: return '0';
|
|
case 1: return '1';
|
|
case 2: return '2';
|
|
case 3: return '3';
|
|
case 4: return '4';
|
|
case 5: return '5';
|
|
case 6: return '6';
|
|
case 7: return '7';
|
|
case 8: return '8';
|
|
case 9: return '9';
|
|
case 10: return 'a';
|
|
case 11: return 'b';
|
|
case 12: return 'c';
|
|
case 13: return 'd';
|
|
case 14: return 'e';
|
|
case 15: return 'f';
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
int
|
|
ldns_hexstring_to_data(uint8_t *data, const char *str)
|
|
{
|
|
size_t i;
|
|
|
|
if (!str || !data) {
|
|
return -1;
|
|
}
|
|
|
|
if (strlen(str) % 2 != 0) {
|
|
return -2;
|
|
}
|
|
|
|
for (i = 0; i < strlen(str) / 2; i++) {
|
|
data[i] =
|
|
16 * (uint8_t) ldns_hexdigit_to_int(str[i*2]) +
|
|
(uint8_t) ldns_hexdigit_to_int(str[i*2 + 1]);
|
|
}
|
|
|
|
return (int) i;
|
|
}
|
|
|
|
const char *
|
|
ldns_version(void)
|
|
{
|
|
return (char*)LDNS_VERSION;
|
|
}
|
|
|
|
/* Number of days per month (except for February in leap years). */
|
|
static const int mdays[] = {
|
|
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
};
|
|
|
|
#define LDNS_MOD(x,y) (((x) % (y) < 0) ? ((x) % (y) + (y)) : ((x) % (y)))
|
|
#define LDNS_DIV(x,y) (((x) % (y) < 0) ? ((x) / (y) - 1 ) : ((x) / (y)))
|
|
|
|
static int
|
|
is_leap_year(int year)
|
|
{
|
|
return LDNS_MOD(year, 4) == 0 && (LDNS_MOD(year, 100) != 0
|
|
|| LDNS_MOD(year, 400) == 0);
|
|
}
|
|
|
|
static int
|
|
leap_days(int y1, int y2)
|
|
{
|
|
--y1;
|
|
--y2;
|
|
return (LDNS_DIV(y2, 4) - LDNS_DIV(y1, 4)) -
|
|
(LDNS_DIV(y2, 100) - LDNS_DIV(y1, 100)) +
|
|
(LDNS_DIV(y2, 400) - LDNS_DIV(y1, 400));
|
|
}
|
|
|
|
/*
|
|
* Code adapted from Python 2.4.1 sources (Lib/calendar.py).
|
|
*/
|
|
time_t
|
|
ldns_mktime_from_utc(const struct tm *tm)
|
|
{
|
|
int year = 1900 + tm->tm_year;
|
|
time_t days = 365 * ((time_t) year - 1970) + leap_days(1970, year);
|
|
time_t hours;
|
|
time_t minutes;
|
|
time_t seconds;
|
|
int i;
|
|
|
|
for (i = 0; i < tm->tm_mon; ++i) {
|
|
days += mdays[i];
|
|
}
|
|
if (tm->tm_mon > 1 && is_leap_year(year)) {
|
|
++days;
|
|
}
|
|
days += tm->tm_mday - 1;
|
|
|
|
hours = days * 24 + tm->tm_hour;
|
|
minutes = hours * 60 + tm->tm_min;
|
|
seconds = minutes * 60 + tm->tm_sec;
|
|
|
|
return seconds;
|
|
}
|
|
|
|
time_t
|
|
mktime_from_utc(const struct tm *tm)
|
|
{
|
|
return ldns_mktime_from_utc(tm);
|
|
}
|
|
|
|
#if SIZEOF_TIME_T <= 4
|
|
|
|
static void
|
|
ldns_year_and_yday_from_days_since_epoch(int64_t days, struct tm *result)
|
|
{
|
|
int year = 1970;
|
|
int new_year;
|
|
|
|
while (days < 0 || days >= (int64_t) (is_leap_year(year) ? 366 : 365)) {
|
|
new_year = year + (int) LDNS_DIV(days, 365);
|
|
days -= (new_year - year) * 365;
|
|
days -= leap_days(year, new_year);
|
|
year = new_year;
|
|
}
|
|
result->tm_year = year;
|
|
result->tm_yday = (int) days;
|
|
}
|
|
|
|
/* Number of days per month in a leap year. */
|
|
static const int leap_year_mdays[] = {
|
|
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
};
|
|
|
|
static void
|
|
ldns_mon_and_mday_from_year_and_yday(struct tm *result)
|
|
{
|
|
int idays = result->tm_yday;
|
|
const int *mon_lengths = is_leap_year(result->tm_year) ?
|
|
leap_year_mdays : mdays;
|
|
|
|
result->tm_mon = 0;
|
|
while (idays >= mon_lengths[result->tm_mon]) {
|
|
idays -= mon_lengths[result->tm_mon++];
|
|
}
|
|
result->tm_mday = idays + 1;
|
|
}
|
|
|
|
static void
|
|
ldns_wday_from_year_and_yday(struct tm *result)
|
|
{
|
|
result->tm_wday = 4 /* 1-1-1970 was a thursday */
|
|
+ LDNS_MOD((result->tm_year - 1970), 7) * LDNS_MOD(365, 7)
|
|
+ leap_days(1970, result->tm_year)
|
|
+ result->tm_yday;
|
|
result->tm_wday = LDNS_MOD(result->tm_wday, 7);
|
|
if (result->tm_wday < 0) {
|
|
result->tm_wday += 7;
|
|
}
|
|
}
|
|
|
|
static struct tm *
|
|
ldns_gmtime64_r(int64_t clock, struct tm *result)
|
|
{
|
|
result->tm_isdst = 0;
|
|
result->tm_sec = (int) LDNS_MOD(clock, 60);
|
|
clock = LDNS_DIV(clock, 60);
|
|
result->tm_min = (int) LDNS_MOD(clock, 60);
|
|
clock = LDNS_DIV(clock, 60);
|
|
result->tm_hour = (int) LDNS_MOD(clock, 24);
|
|
clock = LDNS_DIV(clock, 24);
|
|
|
|
ldns_year_and_yday_from_days_since_epoch(clock, result);
|
|
ldns_mon_and_mday_from_year_and_yday(result);
|
|
ldns_wday_from_year_and_yday(result);
|
|
result->tm_year -= 1900;
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif /* SIZEOF_TIME_T <= 4 */
|
|
|
|
static int64_t
|
|
ldns_serial_arithmitics_time(int32_t time, time_t now)
|
|
{
|
|
int32_t offset = time - (int32_t) now;
|
|
return (int64_t) now + offset;
|
|
}
|
|
|
|
|
|
struct tm *
|
|
ldns_serial_arithmitics_gmtime_r(int32_t time, time_t now, struct tm *result)
|
|
{
|
|
#if SIZEOF_TIME_T <= 4
|
|
int64_t secs_since_epoch = ldns_serial_arithmitics_time(time, now);
|
|
return ldns_gmtime64_r(secs_since_epoch, result);
|
|
#else
|
|
time_t secs_since_epoch = ldns_serial_arithmitics_time(time, now);
|
|
return gmtime_r(&secs_since_epoch, result);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Init the random source
|
|
* applications should call this if they need entropy data within ldns
|
|
* If openSSL is available, it is automatically seeded from /dev/urandom
|
|
* or /dev/random
|
|
*
|
|
* If you need more entropy, or have no openssl available, this function
|
|
* MUST be called at the start of the program
|
|
*
|
|
* If openssl *is* available, this function just adds more entropy
|
|
**/
|
|
int
|
|
ldns_init_random(FILE *fd, unsigned int size)
|
|
{
|
|
/* if fp is given, seed srandom with data from file
|
|
otherwise use /dev/urandom */
|
|
FILE *rand_f;
|
|
uint8_t *seed;
|
|
size_t read = 0;
|
|
unsigned int seed_i;
|
|
struct timeval tv;
|
|
|
|
/* we'll need at least sizeof(unsigned int) bytes for the
|
|
standard prng seed */
|
|
if (size < (unsigned int) sizeof(seed_i)){
|
|
size = (unsigned int) sizeof(seed_i);
|
|
}
|
|
|
|
seed = LDNS_XMALLOC(uint8_t, size);
|
|
if(!seed) {
|
|
return 1;
|
|
}
|
|
|
|
if (!fd) {
|
|
if ((rand_f = fopen("/dev/urandom", "r")) == NULL) {
|
|
/* no readable /dev/urandom, try /dev/random */
|
|
if ((rand_f = fopen("/dev/random", "r")) == NULL) {
|
|
/* no readable /dev/random either, and no entropy
|
|
source given. we'll have to improvise */
|
|
for (read = 0; read < size; read++) {
|
|
gettimeofday(&tv, NULL);
|
|
seed[read] = (uint8_t) (tv.tv_usec % 256);
|
|
}
|
|
} else {
|
|
read = fread(seed, 1, size, rand_f);
|
|
}
|
|
} else {
|
|
read = fread(seed, 1, size, rand_f);
|
|
}
|
|
} else {
|
|
rand_f = fd;
|
|
read = fread(seed, 1, size, rand_f);
|
|
}
|
|
|
|
if (read < size) {
|
|
LDNS_FREE(seed);
|
|
if (!fd) fclose(rand_f);
|
|
return 1;
|
|
} else {
|
|
#ifdef HAVE_SSL
|
|
/* Seed the OpenSSL prng (most systems have it seeded
|
|
automatically, in that case this call just adds entropy */
|
|
RAND_seed(seed, (int) size);
|
|
#else
|
|
/* Seed the standard prng, only uses the first
|
|
* unsigned sizeof(unsiged int) bytes found in the entropy pool
|
|
*/
|
|
memcpy(&seed_i, seed, sizeof(seed_i));
|
|
srandom(seed_i);
|
|
#endif
|
|
LDNS_FREE(seed);
|
|
}
|
|
|
|
if (!fd) {
|
|
if (rand_f) fclose(rand_f);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get random number.
|
|
*
|
|
*/
|
|
uint16_t
|
|
ldns_get_random(void)
|
|
{
|
|
uint16_t rid = 0;
|
|
#ifdef HAVE_SSL
|
|
if (RAND_bytes((unsigned char*)&rid, 2) != 1) {
|
|
rid = (uint16_t) random();
|
|
}
|
|
#else
|
|
rid = (uint16_t) random();
|
|
#endif
|
|
return rid;
|
|
}
|
|
|
|
/*
|
|
* BubbleBabble code taken from OpenSSH
|
|
* Copyright (c) 2001 Carsten Raskgaard. All rights reserved.
|
|
*/
|
|
char *
|
|
ldns_bubblebabble(uint8_t *data, size_t len)
|
|
{
|
|
char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' };
|
|
char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm',
|
|
'n', 'p', 'r', 's', 't', 'v', 'z', 'x' };
|
|
size_t i, j = 0, rounds, seed = 1;
|
|
char *retval;
|
|
|
|
rounds = (len / 2) + 1;
|
|
retval = LDNS_XMALLOC(char, rounds * 6);
|
|
if(!retval) return NULL;
|
|
retval[j++] = 'x';
|
|
for (i = 0; i < rounds; i++) {
|
|
size_t idx0, idx1, idx2, idx3, idx4;
|
|
if ((i + 1 < rounds) || (len % 2 != 0)) {
|
|
idx0 = (((((size_t)(data[2 * i])) >> 6) & 3) +
|
|
seed) % 6;
|
|
idx1 = (((size_t)(data[2 * i])) >> 2) & 15;
|
|
idx2 = ((((size_t)(data[2 * i])) & 3) +
|
|
(seed / 6)) % 6;
|
|
retval[j++] = vowels[idx0];
|
|
retval[j++] = consonants[idx1];
|
|
retval[j++] = vowels[idx2];
|
|
if ((i + 1) < rounds) {
|
|
idx3 = (((size_t)(data[(2 * i) + 1])) >> 4) & 15;
|
|
idx4 = (((size_t)(data[(2 * i) + 1]))) & 15;
|
|
retval[j++] = consonants[idx3];
|
|
retval[j++] = '-';
|
|
retval[j++] = consonants[idx4];
|
|
seed = ((seed * 5) +
|
|
((((size_t)(data[2 * i])) * 7) +
|
|
((size_t)(data[(2 * i) + 1])))) % 36;
|
|
}
|
|
} else {
|
|
idx0 = seed % 6;
|
|
idx1 = 16;
|
|
idx2 = seed / 6;
|
|
retval[j++] = vowels[idx0];
|
|
retval[j++] = consonants[idx1];
|
|
retval[j++] = vowels[idx2];
|
|
}
|
|
}
|
|
retval[j++] = 'x';
|
|
retval[j++] = '\0';
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* For backwards compatibility, because we have always exported this symbol.
|
|
*/
|
|
#ifdef HAVE_B64_NTOP
|
|
int ldns_b64_ntop(const uint8_t* src, size_t srclength,
|
|
char *target, size_t targsize);
|
|
{
|
|
return b64_ntop(src, srclength, target, targsize);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* For backwards compatibility, because we have always exported this symbol.
|
|
*/
|
|
#ifdef HAVE_B64_PTON
|
|
int ldns_b64_pton(const char* src, uint8_t *target, size_t targsize)
|
|
{
|
|
return b64_pton(src, target, targsize);
|
|
}
|
|
#endif
|
|
|
|
|
|
static int
|
|
ldns_b32_ntop_base(const uint8_t* src, size_t src_sz,
|
|
char* dst, size_t dst_sz,
|
|
bool extended_hex, bool add_padding)
|
|
{
|
|
size_t ret_sz;
|
|
const char* b32 = extended_hex ? "0123456789abcdefghijklmnopqrstuv"
|
|
: "abcdefghijklmnopqrstuvwxyz234567";
|
|
|
|
size_t c = 0; /* c is used to carry partial base32 character over
|
|
* byte boundaries for sizes with a remainder.
|
|
* (i.e. src_sz % 5 != 0)
|
|
*/
|
|
|
|
ret_sz = add_padding ? ldns_b32_ntop_calculate_size(src_sz)
|
|
: ldns_b32_ntop_calculate_size_no_padding(src_sz);
|
|
|
|
/* Do we have enough space? */
|
|
if (dst_sz < ret_sz + 1)
|
|
return -1;
|
|
|
|
/* We know the size; terminate the string */
|
|
dst[ret_sz] = '\0';
|
|
|
|
/* First process all chunks of five */
|
|
while (src_sz >= 5) {
|
|
/* 00000... ........ ........ ........ ........ */
|
|
dst[0] = b32[(src[0] ) >> 3];
|
|
|
|
/* .....111 11...... ........ ........ ........ */
|
|
dst[1] = b32[(src[0] & 0x07) << 2 | src[1] >> 6];
|
|
|
|
/* ........ ..22222. ........ ........ ........ */
|
|
dst[2] = b32[(src[1] & 0x3e) >> 1];
|
|
|
|
/* ........ .......3 3333.... ........ ........ */
|
|
dst[3] = b32[(src[1] & 0x01) << 4 | src[2] >> 4];
|
|
|
|
/* ........ ........ ....4444 4....... ........ */
|
|
dst[4] = b32[(src[2] & 0x0f) << 1 | src[3] >> 7];
|
|
|
|
/* ........ ........ ........ .55555.. ........ */
|
|
dst[5] = b32[(src[3] & 0x7c) >> 2];
|
|
|
|
/* ........ ........ ........ ......66 666..... */
|
|
dst[6] = b32[(src[3] & 0x03) << 3 | src[4] >> 5];
|
|
|
|
/* ........ ........ ........ ........ ...77777 */
|
|
dst[7] = b32[(src[4] & 0x1f) ];
|
|
|
|
src_sz -= 5;
|
|
src += 5;
|
|
dst += 8;
|
|
}
|
|
/* Process what remains */
|
|
switch (src_sz) {
|
|
case 4: /* ........ ........ ........ ......66 666..... */
|
|
dst[6] = b32[(src[3] & 0x03) << 3];
|
|
|
|
/* ........ ........ ........ .55555.. ........ */
|
|
dst[5] = b32[(src[3] & 0x7c) >> 2];
|
|
|
|
/* ........ ........ ....4444 4....... ........ */
|
|
c = src[3] >> 7 ;
|
|
case 3: dst[4] = b32[(src[2] & 0x0f) << 1 | c];
|
|
|
|
/* ........ .......3 3333.... ........ ........ */
|
|
c = src[2] >> 4 ;
|
|
case 2: dst[3] = b32[(src[1] & 0x01) << 4 | c];
|
|
|
|
/* ........ ..22222. ........ ........ ........ */
|
|
dst[2] = b32[(src[1] & 0x3e) >> 1];
|
|
|
|
/* .....111 11...... ........ ........ ........ */
|
|
c = src[1] >> 6 ;
|
|
case 1: dst[1] = b32[(src[0] & 0x07) << 2 | c];
|
|
|
|
/* 00000... ........ ........ ........ ........ */
|
|
dst[0] = b32[ src[0] >> 3];
|
|
}
|
|
/* Add padding */
|
|
if (add_padding) {
|
|
switch (src_sz) {
|
|
case 1: dst[2] = '=';
|
|
dst[3] = '=';
|
|
case 2: dst[4] = '=';
|
|
case 3: dst[5] = '=';
|
|
dst[6] = '=';
|
|
case 4: dst[7] = '=';
|
|
}
|
|
}
|
|
return (int)ret_sz;
|
|
}
|
|
|
|
int
|
|
ldns_b32_ntop(const uint8_t* src, size_t src_sz, char* dst, size_t dst_sz)
|
|
{
|
|
return ldns_b32_ntop_base(src, src_sz, dst, dst_sz, false, true);
|
|
}
|
|
|
|
int
|
|
ldns_b32_ntop_extended_hex(const uint8_t* src, size_t src_sz,
|
|
char* dst, size_t dst_sz)
|
|
{
|
|
return ldns_b32_ntop_base(src, src_sz, dst, dst_sz, true, true);
|
|
}
|
|
|
|
#ifndef HAVE_B32_NTOP
|
|
|
|
int
|
|
b32_ntop(const uint8_t* src, size_t src_sz, char* dst, size_t dst_sz)
|
|
{
|
|
return ldns_b32_ntop_base(src, src_sz, dst, dst_sz, false, true);
|
|
}
|
|
|
|
int
|
|
b32_ntop_extended_hex(const uint8_t* src, size_t src_sz,
|
|
char* dst, size_t dst_sz)
|
|
{
|
|
return ldns_b32_ntop_base(src, src_sz, dst, dst_sz, true, true);
|
|
}
|
|
|
|
#endif /* ! HAVE_B32_NTOP */
|
|
|
|
static int
|
|
ldns_b32_pton_base(const char* src, size_t src_sz,
|
|
uint8_t* dst, size_t dst_sz,
|
|
bool extended_hex, bool check_padding)
|
|
{
|
|
size_t i = 0;
|
|
char ch = '\0';
|
|
uint8_t buf[8];
|
|
uint8_t* start = dst;
|
|
|
|
while (src_sz) {
|
|
/* Collect 8 characters in buf (if possible) */
|
|
for (i = 0; i < 8; i++) {
|
|
|
|
do {
|
|
ch = *src++;
|
|
--src_sz;
|
|
|
|
} while (isspace((unsigned char)ch) && src_sz > 0);
|
|
|
|
if (ch == '=' || ch == '\0')
|
|
break;
|
|
|
|
else if (extended_hex)
|
|
|
|
if (ch >= '0' && ch <= '9')
|
|
buf[i] = (uint8_t)ch - '0';
|
|
else if (ch >= 'a' && ch <= 'v')
|
|
buf[i] = (uint8_t)ch - 'a' + 10;
|
|
else if (ch >= 'A' && ch <= 'V')
|
|
buf[i] = (uint8_t)ch - 'A' + 10;
|
|
else
|
|
return -1;
|
|
|
|
else if (ch >= 'a' && ch <= 'z')
|
|
buf[i] = (uint8_t)ch - 'a';
|
|
else if (ch >= 'A' && ch <= 'Z')
|
|
buf[i] = (uint8_t)ch - 'A';
|
|
else if (ch >= '2' && ch <= '7')
|
|
buf[i] = (uint8_t)ch - '2' + 26;
|
|
else
|
|
return -1;
|
|
}
|
|
/* Less that 8 characters. We're done. */
|
|
if (i < 8)
|
|
break;
|
|
|
|
/* Enough space available at the destination? */
|
|
if (dst_sz < 5)
|
|
return -1;
|
|
|
|
/* 00000... ........ ........ ........ ........ */
|
|
/* .....111 11...... ........ ........ ........ */
|
|
dst[0] = buf[0] << 3 | buf[1] >> 2;
|
|
|
|
/* .....111 11...... ........ ........ ........ */
|
|
/* ........ ..22222. ........ ........ ........ */
|
|
/* ........ .......3 3333.... ........ ........ */
|
|
dst[1] = buf[1] << 6 | buf[2] << 1 | buf[3] >> 4;
|
|
|
|
/* ........ .......3 3333.... ........ ........ */
|
|
/* ........ ........ ....4444 4....... ........ */
|
|
dst[2] = buf[3] << 4 | buf[4] >> 1;
|
|
|
|
/* ........ ........ ....4444 4....... ........ */
|
|
/* ........ ........ ........ .55555.. ........ */
|
|
/* ........ ........ ........ ......66 666..... */
|
|
dst[3] = buf[4] << 7 | buf[5] << 2 | buf[6] >> 3;
|
|
|
|
/* ........ ........ ........ ......66 666..... */
|
|
/* ........ ........ ........ ........ ...77777 */
|
|
dst[4] = buf[6] << 5 | buf[7];
|
|
|
|
dst += 5;
|
|
dst_sz -= 5;
|
|
}
|
|
/* Not ending on a eight byte boundary? */
|
|
if (i > 0 && i < 8) {
|
|
|
|
/* Enough space available at the destination? */
|
|
if (dst_sz < (i + 1) / 2)
|
|
return -1;
|
|
|
|
switch (i) {
|
|
case 7: /* ........ ........ ........ ......66 666..... */
|
|
/* ........ ........ ........ .55555.. ........ */
|
|
/* ........ ........ ....4444 4....... ........ */
|
|
dst[3] = buf[4] << 7 | buf[5] << 2 | buf[6] >> 3;
|
|
|
|
case 5: /* ........ ........ ....4444 4....... ........ */
|
|
/* ........ .......3 3333.... ........ ........ */
|
|
dst[2] = buf[3] << 4 | buf[4] >> 1;
|
|
|
|
case 4: /* ........ .......3 3333.... ........ ........ */
|
|
/* ........ ..22222. ........ ........ ........ */
|
|
/* .....111 11...... ........ ........ ........ */
|
|
dst[1] = buf[1] << 6 | buf[2] << 1 | buf[3] >> 4;
|
|
|
|
case 2: /* .....111 11...... ........ ........ ........ */
|
|
/* 00000... ........ ........ ........ ........ */
|
|
dst[0] = buf[0] << 3 | buf[1] >> 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
dst += (i + 1) / 2;
|
|
|
|
if (check_padding) {
|
|
/* Check remaining padding characters */
|
|
if (ch != '=')
|
|
return -1;
|
|
|
|
/* One down, 8 - i - 1 more to come... */
|
|
for (i = 8 - i - 1; i > 0; i--) {
|
|
|
|
do {
|
|
if (src_sz == 0)
|
|
return -1;
|
|
ch = *src++;
|
|
src_sz--;
|
|
|
|
} while (isspace((unsigned char)ch));
|
|
|
|
if (ch != '=')
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
return dst - start;
|
|
}
|
|
|
|
int
|
|
ldns_b32_pton(const char* src, size_t src_sz, uint8_t* dst, size_t dst_sz)
|
|
{
|
|
return ldns_b32_pton_base(src, src_sz, dst, dst_sz, false, true);
|
|
}
|
|
|
|
int
|
|
ldns_b32_pton_extended_hex(const char* src, size_t src_sz,
|
|
uint8_t* dst, size_t dst_sz)
|
|
{
|
|
return ldns_b32_pton_base(src, src_sz, dst, dst_sz, true, true);
|
|
}
|
|
|
|
#ifndef HAVE_B32_PTON
|
|
|
|
int
|
|
b32_pton(const char* src, size_t src_sz, uint8_t* dst, size_t dst_sz)
|
|
{
|
|
return ldns_b32_pton_base(src, src_sz, dst, dst_sz, false, true);
|
|
}
|
|
|
|
int
|
|
b32_pton_extended_hex(const char* src, size_t src_sz,
|
|
uint8_t* dst, size_t dst_sz)
|
|
{
|
|
return ldns_b32_pton_base(src, src_sz, dst, dst_sz, true, true);
|
|
}
|
|
|
|
#endif /* ! HAVE_B32_PTON */
|
|
|