kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

utils.c (14234B)



      1 /*
      2  * Copyright (c) 2013-2022 Joris Vink <joris@coders.se>
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include <sys/types.h>
     18 #include <sys/time.h>
     19 
     20 #include <ctype.h>
     21 #include <stdio.h>
     22 #include <stdarg.h>
     23 #include <string.h>
     24 #include <stdlib.h>
     25 #include <time.h>
     26 #include <limits.h>
     27 
     28 #include "kore.h"
     29 
     30 static struct {
     31 	char		*name;
     32 	int		value;
     33 } month_names[] = {
     34 	{ "Jan",	0 },
     35 	{ "Feb",	1 },
     36 	{ "Mar",	2 },
     37 	{ "Apr",	3 },
     38 	{ "May",	4 },
     39 	{ "Jun",	5 },
     40 	{ "Jul",	6 },
     41 	{ "Aug",	7 },
     42 	{ "Sep",	8 },
     43 	{ "Oct",	9 },
     44 	{ "Nov",	10 },
     45 	{ "Dec",	11 },
     46 	{ NULL,		0 },
     47 };
     48 
     49 static void	fatal_log(const char *, va_list);
     50 static int	utils_base64_encode(const void *, size_t, char **,
     51 		    const char *, int);
     52 static int	utils_base64_decode(const char *, u_int8_t **,
     53 		    size_t *, const char *, int);
     54 static int	utils_x509name_tobuf(void *, int, int, const char *,
     55 		    const void *, size_t, int);
     56 
     57 static char b64_table[] = 	\
     58     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
     59 
     60 static char b64url_table[] = 	\
     61     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
     62 
     63 /* b64_table and b64url_table are the same size. */
     64 #define B64_TABLE_LEN		(sizeof(b64_table))
     65 
     66 #if defined(KORE_DEBUG)
     67 void
     68 kore_debug_internal(char *file, int line, const char *fmt, ...)
     69 {
     70 	va_list		args;
     71 	char		buf[2048];
     72 
     73 	va_start(args, fmt);
     74 	(void)vsnprintf(buf, sizeof(buf), fmt, args);
     75 	va_end(args);
     76 
     77 	printf("[%d] %s:%d - %s\n", kore_pid, file, line, buf);
     78 }
     79 #endif
     80 
     81 size_t
     82 kore_strlcpy(char *dst, const char *src, const size_t len)
     83 {
     84 	char		*d = dst;
     85 	const char	*s = src;
     86 	const char	*end = dst + len - 1;
     87 
     88 	if (len == 0)
     89 		fatal("kore_strlcpy: len == 0");
     90 
     91 	while ((*d = *s) != '\0') {
     92 		if (d == end) {
     93 			*d = '\0';
     94 			break;
     95 		}
     96 
     97 		d++;
     98 		s++;
     99 	}
    100 
    101 	while (*s != '\0')
    102 		s++;
    103 
    104 	return (s - src);
    105 }
    106 
    107 int
    108 kore_snprintf(char *str, size_t size, int *len, const char *fmt, ...)
    109 {
    110 	int		l;
    111 	va_list		args;
    112 
    113 	va_start(args, fmt);
    114 	l = vsnprintf(str, size, fmt, args);
    115 	va_end(args);
    116 
    117 	if (l == -1 || (size_t)l >= size)
    118 		return (KORE_RESULT_ERROR);
    119 
    120 	if (len != NULL)
    121 		*len = l;
    122 
    123 	return (KORE_RESULT_OK);
    124 }
    125 
    126 long long
    127 kore_strtonum(const char *str, int base, long long min, long long max, int *err)
    128 {
    129 	long long	l;
    130 	char		*ep;
    131 
    132 	if (min > max) {
    133 		*err = KORE_RESULT_ERROR;
    134 		return (0);
    135 	}
    136 
    137 	errno = 0;
    138 	l = strtoll(str, &ep, base);
    139 	if (errno != 0 || str == ep || *ep != '\0') {
    140 		*err = KORE_RESULT_ERROR;
    141 		return (0);
    142 	}
    143 
    144 	if (l < min) {
    145 		*err = KORE_RESULT_ERROR;
    146 		return (0);
    147 	}
    148 
    149 	if (l > max) {
    150 		*err = KORE_RESULT_ERROR;
    151 		return (0);
    152 	}
    153 
    154 	*err = KORE_RESULT_OK;
    155 	return (l);
    156 }
    157 
    158 u_int64_t
    159 kore_strtonum64(const char *str, int sign, int *err)
    160 {
    161 	u_int64_t	l;
    162 	long long	ll;
    163 	char		*ep;
    164 	int		check;
    165 
    166 	l = 0;
    167 	check = 1;
    168 
    169 	ll = strtoll(str, &ep, 10);
    170 	if ((errno == EINVAL || errno == ERANGE) &&
    171 	    (ll == LLONG_MIN || ll == LLONG_MAX)) {
    172 		if (sign) {
    173 			*err = KORE_RESULT_ERROR;
    174 			return (0);
    175 		}
    176 
    177 		check = 0;
    178 	}
    179 
    180 	if (!sign) {
    181 		l = strtoull(str, &ep, 10);
    182 		if ((errno == EINVAL || errno == ERANGE) && l == ULONG_MAX) {
    183 			*err = KORE_RESULT_ERROR;
    184 			return (0);
    185 		}
    186 
    187 		if (check && ll < 0) {
    188 			*err = KORE_RESULT_ERROR;
    189 			return (0);
    190 		}
    191 	}
    192 
    193 	if (str == ep || *ep != '\0') {
    194 		*err = KORE_RESULT_ERROR;
    195 		return (0);
    196 	}
    197 
    198 	*err = KORE_RESULT_OK;
    199 	return ((sign) ? (u_int64_t)ll : l);
    200 }
    201 
    202 double
    203 kore_strtodouble(const char *str, long double min, long double max, int *err)
    204 {
    205 	double		d;
    206 	char		*ep;
    207 
    208 	if (min > max) {
    209 		*err = KORE_RESULT_ERROR;
    210 		return (0);
    211 	}
    212 
    213 	errno = 0;
    214 	d = strtod(str, &ep);
    215 	if (errno == ERANGE || str == ep || *ep != '\0') {
    216 		*err = KORE_RESULT_ERROR;
    217 		return (0);
    218 	}
    219 
    220 	if (d < min) {
    221 		*err = KORE_RESULT_ERROR;
    222 		return (0);
    223 	}
    224 
    225 	if (d > max) {
    226 		*err = KORE_RESULT_ERROR;
    227 		return (0);
    228 	}
    229 
    230 	*err = KORE_RESULT_OK;
    231 	return (d);
    232 }
    233 
    234 int
    235 kore_split_string(char *input, const char *delim, char **out, size_t ele)
    236 {
    237 	int		count;
    238 	char		**ap;
    239 
    240 	if (ele == 0)
    241 		return (0);
    242 
    243 	count = 0;
    244 	for (ap = out; ap < &out[ele - 1] &&
    245 	    (*ap = strsep(&input, delim)) != NULL;) {
    246 		if (**ap != '\0') {
    247 			ap++;
    248 			count++;
    249 		}
    250 	}
    251 
    252 	*ap = NULL;
    253 	return (count);
    254 }
    255 
    256 void
    257 kore_strip_chars(char *in, const char strip, char **out)
    258 {
    259 	u_int32_t	len;
    260 	char		*s, *p;
    261 
    262 	len = strlen(in);
    263 	*out = kore_malloc(len + 1);
    264 	p = *out;
    265 
    266 	for (s = in; s < (in + len); s++) {
    267 		if (*s == strip)
    268 			continue;
    269 
    270 		*p++ = *s;
    271 	}
    272 
    273 	*p = '\0';
    274 }
    275 
    276 time_t
    277 kore_date_to_time(const char *http_date)
    278 {
    279 	time_t			t;
    280 	int			err, i;
    281 	struct tm		tm, *ltm;
    282 	char			*args[7], *tbuf[5], *sdup;
    283 
    284 	time(&t);
    285 	ltm = localtime(&t);
    286 	sdup = kore_strdup(http_date);
    287 
    288 	t = KORE_RESULT_ERROR;
    289 
    290 	if (kore_split_string(sdup, " ", args, 7) != 6) {
    291 		kore_debug("misformed http-date: '%s'", http_date);
    292 		goto out;
    293 	}
    294 
    295 	memset(&tm, 0, sizeof(tm));
    296 
    297 	tm.tm_year = kore_strtonum(args[3], 10, 1900, 2068, &err) - 1900;
    298 	if (err == KORE_RESULT_ERROR) {
    299 		kore_debug("misformed year in http-date: '%s'", http_date);
    300 		goto out;
    301 	}
    302 
    303 	for (i = 0; month_names[i].name != NULL; i++) {
    304 		if (!strcmp(month_names[i].name, args[2])) {
    305 			tm.tm_mon = month_names[i].value;
    306 			break;
    307 		}
    308 	}
    309 
    310 	if (month_names[i].name == NULL) {
    311 		kore_debug("misformed month in http-date: '%s'", http_date);
    312 		goto out;
    313 	}
    314 
    315 	tm.tm_mday = kore_strtonum(args[1], 10, 1, 31, &err);
    316 	if (err == KORE_RESULT_ERROR) {
    317 		kore_debug("misformed mday in http-date: '%s'", http_date);
    318 		goto out;
    319 	}
    320 
    321 	if (kore_split_string(args[4], ":", tbuf, 5) != 3) {
    322 		kore_debug("misformed HH:MM:SS in http-date: '%s'", http_date);
    323 		goto out;
    324 	}
    325 
    326 	tm.tm_hour = kore_strtonum(tbuf[0], 10, 0, 23, &err);
    327 	if (err == KORE_RESULT_ERROR) {
    328 		kore_debug("misformed hour in http-date: '%s'", http_date);
    329 		goto out;
    330 	}
    331 
    332 	tm.tm_min = kore_strtonum(tbuf[1], 10, 0, 59, &err);
    333 	if (err == KORE_RESULT_ERROR) {
    334 		kore_debug("misformed minutes in http-date: '%s'", http_date);
    335 		goto out;
    336 	}
    337 
    338 	tm.tm_sec = kore_strtonum(tbuf[2], 10, 0, 60, &err);
    339 	if (err == KORE_RESULT_ERROR) {
    340 		kore_debug("misformed seconds in http-date: '%s'", http_date);
    341 		goto out;
    342 	}
    343 
    344 	tm.tm_isdst = ltm->tm_isdst;
    345 	t = mktime(&tm) + ltm->tm_gmtoff;
    346 	if (t == -1) {
    347 		t = 0;
    348 		kore_debug("mktime() on '%s' failed", http_date);
    349 	}
    350 
    351 out:
    352 	kore_free(sdup);
    353 	return (t);
    354 }
    355 
    356 char *
    357 kore_time_to_date(time_t now)
    358 {
    359 	struct tm		*tm;
    360 	static time_t		last = 0;
    361 	static char		tbuf[32];
    362 
    363 	if (now != last) {
    364 		last = now;
    365 
    366 		tm = gmtime(&now);
    367 		if (!strftime(tbuf, sizeof(tbuf), "%a, %d %b %Y %T GMT", tm)) {
    368 			kore_debug("strftime() gave us NULL (%ld)", now);
    369 			return (NULL);
    370 		}
    371 	}
    372 
    373 	return (tbuf);
    374 }
    375 
    376 u_int64_t
    377 kore_time_ms(void)
    378 {
    379 	struct timespec		ts;
    380 
    381 	(void)clock_gettime(CLOCK_MONOTONIC, &ts);
    382 
    383 	return ((u_int64_t)(ts.tv_sec * 1000 + (ts.tv_nsec / 1000000)));
    384 }
    385 
    386 int
    387 kore_base64url_encode(const void *data, size_t len, char **out, int flags)
    388 {
    389 	return (utils_base64_encode(data, len, out, b64url_table, flags));
    390 }
    391 
    392 int
    393 kore_base64_encode(const void *data, size_t len, char **out)
    394 {
    395 	return (utils_base64_encode(data, len, out, b64_table, 0));
    396 }
    397 
    398 int
    399 kore_base64url_decode(const char *in, u_int8_t **out, size_t *olen, int flags)
    400 {
    401 	return (utils_base64_decode(in, out, olen, b64url_table, flags));
    402 }
    403 
    404 int
    405 kore_base64_decode(const char *in, u_int8_t **out, size_t *olen)
    406 {
    407 	return (utils_base64_decode(in, out, olen, b64_table, 0));
    408 }
    409 
    410 void *
    411 kore_mem_find(void *src, size_t slen, const void *needle, size_t len)
    412 {
    413 	size_t		pos;
    414 
    415 	for (pos = 0; pos < slen; pos++) {
    416 		if ( *((u_int8_t *)src + pos) != *(const u_int8_t *)needle)
    417 			continue;
    418 
    419 		if ((slen - pos) < len)
    420 			return (NULL);
    421 
    422 		if (!memcmp((u_int8_t *)src + pos, needle, len))
    423 			return ((u_int8_t *)src + pos);
    424 	}
    425 
    426 	return (NULL);
    427 }
    428 
    429 char *
    430 kore_text_trim(char *string, size_t len)
    431 {
    432 	char		*end;
    433 
    434 	if (len == 0)
    435 		return (string);
    436 
    437 	end = (string + len) - 1;
    438 	while (isspace(*(unsigned char *)string) && string < end)
    439 		string++;
    440 
    441 	while (isspace(*(unsigned char *)end) && end > string)
    442 		*(end)-- = '\0';
    443 
    444 	return (string);
    445 }
    446 
    447 char *
    448 kore_read_line(FILE *fp, char *in, size_t len)
    449 {
    450 	char	*p, *t;
    451 
    452 	if (fgets(in, len, fp) == NULL)
    453 		return (NULL);
    454 
    455 	p = in;
    456 	in[strcspn(in, "\n")] = '\0';
    457 
    458 	while (isspace(*(unsigned char *)p))
    459 		p++;
    460 
    461 	if (p[0] == '#' || p[0] == '\0') {
    462 		p[0] = '\0';
    463 		return (p);
    464 	}
    465 
    466 	for (t = p; *t != '\0'; t++) {
    467 		if (*t == '\t')
    468 			*t = ' ';
    469 	}
    470 
    471 	return (p);
    472 }
    473 
    474 const char *
    475 kore_worker_name(int id)
    476 {
    477 	static char	buf[64];
    478 
    479 	switch (id) {
    480 	case KORE_WORKER_KEYMGR:
    481 		(void)snprintf(buf, sizeof(buf), "[keymgr]");
    482 		break;
    483 	case KORE_WORKER_ACME:
    484 		(void)snprintf(buf, sizeof(buf), "[acme]");
    485 		break;
    486 	default:
    487 		(void)snprintf(buf, sizeof(buf), "[wrk %d]", id);
    488 		break;
    489 	}
    490 
    491 	return (buf);
    492 }
    493 
    494 int
    495 kore_x509_issuer_name(struct connection *c, char **out, int flags)
    496 {
    497 	struct kore_buf		buf;
    498 	KORE_X509_NAMES		*name;
    499 
    500 	if ((name = kore_tls_x509_issuer_name(c)) == NULL)
    501 		return (KORE_RESULT_ERROR);
    502 
    503 	kore_buf_init(&buf, 1024);
    504 
    505 	if (!kore_tls_x509name_foreach(name, flags, &buf,
    506 	    utils_x509name_tobuf)) {
    507 		kore_buf_cleanup(&buf);
    508 		return (KORE_RESULT_ERROR);
    509 	}
    510 
    511 	*out = kore_buf_stringify(&buf, NULL);
    512 
    513 	buf.offset = 0;
    514 	buf.data = NULL;
    515 
    516 	return (KORE_RESULT_OK);
    517 }
    518 
    519 int
    520 kore_x509_subject_name(struct connection *c, char **out, int flags)
    521 {
    522 	struct kore_buf		buf;
    523 	KORE_X509_NAMES		*name;
    524 
    525 	if ((name = kore_tls_x509_subject_name(c)) == NULL)
    526 		return (KORE_RESULT_ERROR);
    527 
    528 	kore_buf_init(&buf, 1024);
    529 
    530 	if (!kore_tls_x509name_foreach(name, flags, &buf,
    531 	    utils_x509name_tobuf)) {
    532 		kore_buf_cleanup(&buf);
    533 		return (KORE_RESULT_ERROR);
    534 	}
    535 
    536 	*out = kore_buf_stringify(&buf, NULL);
    537 
    538 	buf.offset = 0;
    539 	buf.data = NULL;
    540 
    541 	return (KORE_RESULT_OK);
    542 }
    543 
    544 void
    545 fatal(const char *fmt, ...)
    546 {
    547 	va_list		args;
    548 
    549 	va_start(args, fmt);
    550 	fatal_log(fmt, args);
    551 	va_end(args);
    552 
    553 	exit(1);
    554 }
    555 
    556 void
    557 fatalx(const char *fmt, ...)
    558 {
    559 	va_list		args;
    560 
    561 	/* In case people call fatalx() from the parent context. */
    562 	if (worker != NULL)
    563 		kore_msg_send(KORE_MSG_PARENT, KORE_MSG_SHUTDOWN, NULL, 0);
    564 
    565 	va_start(args, fmt);
    566 	fatal_log(fmt, args);
    567 	va_end(args);
    568 
    569 	exit(1);
    570 }
    571 
    572 static void
    573 fatal_log(const char *fmt, va_list args)
    574 {
    575 	char			buf[2048];
    576 
    577 	(void)vsnprintf(buf, sizeof(buf), fmt, args);
    578 	kore_log(LOG_ERR, "FATAL: %s", buf);
    579 
    580 	if (worker != NULL && worker->id == KORE_WORKER_KEYMGR)
    581 		kore_keymgr_cleanup(1);
    582 }
    583 
    584 static int
    585 utils_x509name_tobuf(void *udata, int islast, int nid, const char *field,
    586     const void *data, size_t len, int flags)
    587 {
    588 	struct kore_buf		*buf = udata;
    589 
    590 	if (flags & KORE_X509_COMMON_NAME_ONLY) {
    591 		if (nid == KORE_X509_NAME_COMMON_NAME)
    592 			kore_buf_append(buf, data, len);
    593 	} else {
    594 		kore_buf_appendf(buf, "%s=", field);
    595 		kore_buf_append(buf, data, len);
    596 		if (!islast)
    597 			kore_buf_appendf(buf, " ");
    598 	}
    599 
    600 	return (KORE_RESULT_OK);
    601 }
    602 
    603 static int
    604 utils_base64_encode(const void *data, size_t len, char **out,
    605     const char *table, int flags)
    606 {
    607 	u_int8_t		n;
    608 	size_t			nb;
    609 	const u_int8_t		*ptr;
    610 	u_int32_t		bytes;
    611 	struct kore_buf		result;
    612 
    613 	nb = 0;
    614 	ptr = data;
    615 	kore_buf_init(&result, (len / 3) * 4);
    616 
    617 	while (len > 0) {
    618 		if (len > 2) {
    619 			nb = 3;
    620 			bytes = *ptr++ << 16;
    621 			bytes |= *ptr++ << 8;
    622 			bytes |= *ptr++;
    623 		} else if (len > 1) {
    624 			nb = 2;
    625 			bytes = *ptr++ << 16;
    626 			bytes |= *ptr++ << 8;
    627 		} else if (len == 1) {
    628 			nb = 1;
    629 			bytes = *ptr++ << 16;
    630 		} else {
    631 			kore_buf_cleanup(&result);
    632 			return (KORE_RESULT_ERROR);
    633 		}
    634 
    635 		n = (bytes >> 18) & 0x3f;
    636 		kore_buf_append(&result, &(table[n]), 1);
    637 		n = (bytes >> 12) & 0x3f;
    638 		kore_buf_append(&result, &(table[n]), 1);
    639 		if (nb > 1) {
    640 			n = (bytes >> 6) & 0x3f;
    641 			kore_buf_append(&result, &(table[n]), 1);
    642 			if (nb > 2) {
    643 				n = bytes & 0x3f;
    644 				kore_buf_append(&result, &(table[n]), 1);
    645 			}
    646 		}
    647 
    648 		len -= nb;
    649 	}
    650 
    651 	if (!(flags & KORE_BASE64_RAW)) {
    652 		switch (nb) {
    653 		case 1:
    654 			kore_buf_appendf(&result, "==");
    655 			break;
    656 		case 2:
    657 			kore_buf_appendf(&result, "=");
    658 			break;
    659 		case 3:
    660 			break;
    661 		default:
    662 			kore_buf_cleanup(&result);
    663 			return (KORE_RESULT_ERROR);
    664 		}
    665 	}
    666 
    667 	/* result.data gets taken over so no need to cleanup result. */
    668 	*out = kore_buf_stringify(&result, NULL);
    669 
    670 	return (KORE_RESULT_OK);
    671 }
    672 
    673 static int
    674 utils_base64_decode(const char *in, u_int8_t **out, size_t *olen,
    675     const char *table, int flags)
    676 {
    677 	int			i, c;
    678 	u_int8_t		d, n, o;
    679 	struct kore_buf		*res, buf;
    680 	const char		*ptr, *pad;
    681 	u_int32_t		b, len, plen, idx;
    682 
    683 	i = 4;
    684 	b = 0;
    685 	d = 0;
    686 	c = 0;
    687 	len = strlen(in);
    688 	memset(&buf, 0, sizeof(buf));
    689 
    690 	if (flags & KORE_BASE64_RAW) {
    691 		switch (len % 4) {
    692 		case 2:
    693 			plen = 2;
    694 			pad = "==";
    695 			break;
    696 		case 3:
    697 			plen = 1;
    698 			pad = "=";
    699 			break;
    700 		default:
    701 			return (KORE_RESULT_ERROR);
    702 		}
    703 
    704 		kore_buf_init(&buf, len + plen);
    705 		kore_buf_append(&buf, in, len);
    706 		kore_buf_append(&buf, pad, plen);
    707 
    708 		len = len + plen;
    709 		ptr = (const char *)buf.data;
    710 	} else {
    711 		ptr = in;
    712 	}
    713 
    714 	res = kore_buf_alloc(len);
    715 
    716 	for (idx = 0; idx < len; idx++) {
    717 		c = ptr[idx];
    718 		if (c == '=')
    719 			break;
    720 
    721 		for (o = 0; o < B64_TABLE_LEN; o++) {
    722 			if (table[o] == c) {
    723 				d = o;
    724 				break;
    725 			}
    726 		}
    727 
    728 		if (o == B64_TABLE_LEN) {
    729 			*out = NULL;
    730 			kore_buf_free(res);
    731 			kore_buf_cleanup(&buf);
    732 			return (KORE_RESULT_ERROR);
    733 		}
    734 
    735 		b |= (d & 0x3f) << ((i - 1) * 6);
    736 		i--;
    737 		if (i == 0) {
    738 			for (i = 2; i >= 0; i--) {
    739 				n = (b >> (8 * i));
    740 				kore_buf_append(res, &n, 1);
    741 			}
    742 
    743 			b = 0;
    744 			i = 4;
    745 		}
    746 	}
    747 
    748 	if (c == '=') {
    749 		if (i > 2) {
    750 			*out = NULL;
    751 			kore_buf_free(res);
    752 			kore_buf_cleanup(&buf);
    753 			return (KORE_RESULT_ERROR);
    754 		}
    755 
    756 		o = i;
    757 		for (i = 2; i >= o; i--) {
    758 			n = (b >> (8 * i));
    759 			kore_buf_append(res, &n, 1);
    760 		}
    761 	}
    762 
    763 	kore_buf_cleanup(&buf);
    764 	*out = kore_buf_release(res, olen);
    765 
    766 	return (KORE_RESULT_OK);
    767 }