diff options
Diffstat (limited to 'src/libknot/yparser/yptrafo.c')
-rw-r--r-- | src/libknot/yparser/yptrafo.c | 1104 |
1 files changed, 1104 insertions, 0 deletions
diff --git a/src/libknot/yparser/yptrafo.c b/src/libknot/yparser/yptrafo.c new file mode 100644 index 0000000..60b3717 --- /dev/null +++ b/src/libknot/yparser/yptrafo.c @@ -0,0 +1,1104 @@ +/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <arpa/inet.h> +#include <inttypes.h> +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> + +#include "libknot/yparser/yptrafo.h" +#include "libknot/attribute.h" +#include "libknot/consts.h" +#include "libknot/dname.h" +#include "contrib/base64.h" +#include "contrib/ctype.h" +#include "contrib/musl/inet_ntop.h" +#include "contrib/sockaddr.h" +#include "contrib/wire_ctx.h" + +enum { + UNIT_BYTE = 'B', + UNIT_KILO = 'K', + UNIT_MEGA = 'M', + UNIT_GIGA = 'G', + UNIT_SEC = 's', + UNIT_MIN = 'm', + UNIT_HOUR = 'h', + UNIT_DAY = 'd' +}; + +enum { + MULTI_BYTE = 1, + MULTI_KILO = 1024, + MULTI_MEGA = 1024 * 1024, + MULTI_GIGA = 1024 * 1024 * 1024, + MULTI_SEC = 1, + MULTI_MIN = 60, + MULTI_HOUR = 3600, + MULTI_DAY = 24 * 3600 +}; + +static wire_ctx_t copy_in( + wire_ctx_t *in, + size_t in_len, + char *buf, + size_t buf_len) +{ + wire_ctx_t ctx = wire_ctx_init((uint8_t *)buf, buf_len); + wire_ctx_write(&ctx, in->position, in_len); + wire_ctx_skip(in, in_len); + // Write the terminator. + wire_ctx_write_u8(&ctx, '\0'); + wire_ctx_skip(&ctx, -1); + return ctx; +} + +_public_ +int yp_str_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + wire_ctx_write(out, in->position, YP_LEN); + wire_ctx_skip(in, YP_LEN); + // Write string terminator. + wire_ctx_write_u8(out, '\0'); + + YP_CHECK_RET; +} + +_public_ +int yp_str_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + size_t len = strlen((char *)in->position) + 1; + + wire_ctx_write(out, in->position, len); + wire_ctx_skip(in, len); + // Set the terminator as a current position. + wire_ctx_skip(out, -1); + + YP_CHECK_RET; +} + +_public_ +int yp_bool_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + if (strncasecmp((char *)in->position, "on", YP_LEN) == 0 || + strncasecmp((char *)in->position, "true", YP_LEN) == 0) { + wire_ctx_write_u8(out, 1); + } else if (strncasecmp((char *)in->position, "off", YP_LEN) == 0 || + strncasecmp((char *)in->position, "false", YP_LEN) == 0) { + wire_ctx_write_u8(out, 0); + } else { + return KNOT_EINVAL; + } + + wire_ctx_skip(in, YP_LEN); + + YP_CHECK_RET; +} + +_public_ +int yp_bool_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + const char *value; + + switch (wire_ctx_read_u8(in)) { + case 0: + value = "off"; + break; + case 1: + value = "on"; + break; + default: + return KNOT_EINVAL; + } + + int ret = snprintf((char *)out->position, wire_ctx_available(out), "%s", + value); + if (ret <= 0 || ret >= wire_ctx_available(out)) { + return KNOT_ESPACE; + } + wire_ctx_skip(out, ret); + + YP_CHECK_RET; +} + +static int remove_unit( + int64_t *number, + char unit, + yp_style_t style) +{ + int64_t multiplier = 1; + + // Get the multiplier for the unit. + if (style & YP_SSIZE) { + switch (unit) { + case UNIT_BYTE: + multiplier = MULTI_BYTE; + break; + case UNIT_KILO: + multiplier = MULTI_KILO; + break; + case UNIT_MEGA: + multiplier = MULTI_MEGA; + break; + case UNIT_GIGA: + multiplier = MULTI_GIGA; + break; + default: + return KNOT_EINVAL; + } + } else if (style & YP_STIME) { + switch (unit) { + case UNIT_SEC: + multiplier = MULTI_SEC; + break; + case UNIT_MIN: + multiplier = MULTI_MIN; + break; + case UNIT_HOUR: + multiplier = MULTI_HOUR; + break; + case UNIT_DAY: + multiplier = MULTI_DAY; + break; + default: + return KNOT_EINVAL; + } + } else { + return KNOT_EINVAL; + } + + // Check for possible number overflow. + if (INT64_MAX / multiplier < (*number >= 0 ? *number : -*number)) { + return KNOT_ERANGE; + } + + *number *= multiplier; + + return KNOT_EOK; +} + +_public_ +int yp_int_to_bin( + YP_TXT_BIN_PARAMS, + int64_t min, + int64_t max, + yp_style_t style) +{ + YP_CHECK_PARAMS_BIN; + + // Copy input string to the buffer to limit strtoll overread. + char buf[32]; + wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf)); + if (buf_ctx.error != KNOT_EOK) { + return buf_ctx.error; + } + + // Parse the number. + char *end; + errno = 0; + int64_t number = strtoll(buf, &end, 10); + + // Check for number overflow. + if (errno == ERANGE && (number == LLONG_MAX || number == LLONG_MIN)) { + return KNOT_ERANGE; + } + // Check if the whole string is invalid. + if ((errno != 0 && number == 0) || end == buf) { + return KNOT_EINVAL; + } + // Check the rest of the string for a unit. + if (*end != '\0') { + // Check just for one-char rest. + if (*(end + 1) != '\0') { + return KNOT_EINVAL; + } + + // Try to apply a unit on the number. + int ret = remove_unit(&number, *end, style); + if (ret != KNOT_EOK) { + return ret; + } + } + + // Check for the result number overflow. + if (number < min || number > max) { + return KNOT_ERANGE; + } + + // Write the result. + wire_ctx_write_u64(out, number); + + YP_CHECK_RET; +} + +static void add_unit( + int64_t *number, + char *unit, + yp_style_t style) +{ + int64_t multiplier = 1; + char basic_unit = '\0'; + char new_unit = '\0'; + + // Get the multiplier for the unit. + if (style & YP_SSIZE) { + basic_unit = UNIT_BYTE; + + if (*number < MULTI_KILO) { + multiplier = MULTI_BYTE; + new_unit = UNIT_BYTE; + } else if (*number < MULTI_MEGA) { + multiplier = MULTI_KILO; + new_unit = UNIT_KILO; + } else if (*number < MULTI_GIGA) { + multiplier = MULTI_MEGA; + new_unit = UNIT_MEGA; + } else { + multiplier = MULTI_GIGA; + new_unit = UNIT_GIGA; + } + } else if (style & YP_STIME) { + basic_unit = UNIT_SEC; + + if (*number < MULTI_MIN) { + multiplier = MULTI_SEC; + new_unit = UNIT_SEC; + } else if (*number < MULTI_HOUR) { + multiplier = MULTI_MIN; + new_unit = UNIT_MIN; + } else if (*number < MULTI_DAY) { + multiplier = MULTI_HOUR; + new_unit = UNIT_HOUR; + } else { + multiplier = MULTI_DAY; + new_unit = UNIT_DAY; + } + } + + // Check for unit application without any remainder. + if ((*number % multiplier) == 0) { + *number /= multiplier; + *unit = new_unit; + } else { + *unit = basic_unit; + } +} + +_public_ +int yp_int_to_txt( + YP_BIN_TXT_PARAMS, + yp_style_t style) +{ + YP_CHECK_PARAMS_TXT; + + char unit[2] = { '\0' }; + int64_t number = wire_ctx_read_u64(in); + add_unit(&number, unit, style); + + int ret = snprintf((char *)out->position, wire_ctx_available(out), + "%"PRId64"%s", number, unit); + if (ret <= 0 || ret >= wire_ctx_available(out)) { + return KNOT_ESPACE; + } + wire_ctx_skip(out, ret); + + YP_CHECK_RET; +} + +static uint8_t sock_type_guess( + const uint8_t *str, + size_t len) +{ + size_t dots = 0; + size_t semicolons = 0; + size_t digits = 0; + + // Analyze the string. + for (size_t i = 0; i < len; i++) { + if (str[i] == '.') dots++; + else if (str[i] == ':') semicolons++; + else if (is_digit(str[i])) digits++; + } + + // Guess socket type. + if (semicolons >= 1) { + return 6; + } else if (semicolons == 0 && dots == 3 && digits >= 3) { + return 4; + } else { + return 0; + } +} + +_public_ +int yp_addr_noport_to_bin( + YP_TXT_BIN_PARAMS, + bool allow_unix) +{ + YP_CHECK_PARAMS_BIN; + + struct in_addr addr4; + struct in6_addr addr6; + + uint8_t type = sock_type_guess(in->position, YP_LEN); + + // Copy address to the buffer to limit inet_pton overread. + char buf[INET6_ADDRSTRLEN]; + if (type == 4 || type == 6) { + wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf)); + if (buf_ctx.error != KNOT_EOK) { + return buf_ctx.error; + } + } + + // Write address type. + wire_ctx_write_u8(out, type); + + // Write address as such. + if (type == 4 && inet_pton(AF_INET, buf, &addr4) == 1) { + wire_ctx_write(out, (uint8_t *)&(addr4.s_addr), + sizeof(addr4.s_addr)); + } else if (type == 6 && inet_pton(AF_INET6, buf, &addr6) == 1) { + wire_ctx_write(out, (uint8_t *)&(addr6.s6_addr), + sizeof(addr6.s6_addr)); + } else if (type == 0 && allow_unix) { + int ret = yp_str_to_bin(in, out, stop); + if (ret != KNOT_EOK) { + return ret; + } + } else { + return KNOT_EINVAL; + } + + YP_CHECK_RET; +} + +_public_ +int yp_addr_noport_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + struct in_addr addr4; + struct in6_addr addr6; + + int ret; + + switch (wire_ctx_read_u8(in)) { + case 0: + ret = yp_str_to_txt(in, out); + if (ret != KNOT_EOK) { + return ret; + } + break; + case 4: + wire_ctx_read(in, &(addr4.s_addr), sizeof(addr4.s_addr)); + if (knot_inet_ntop(AF_INET, &addr4, (char *)out->position, + wire_ctx_available(out)) == NULL) { + return KNOT_EINVAL; + } + wire_ctx_skip(out, strlen((char *)out->position)); + break; + case 6: + wire_ctx_read(in, &(addr6.s6_addr), sizeof(addr6.s6_addr)); + if (knot_inet_ntop(AF_INET6, &addr6, (char *)out->position, + wire_ctx_available(out)) == NULL) { + return KNOT_EINVAL; + } + wire_ctx_skip(out, strlen((char *)out->position)); + break; + default: + return KNOT_EINVAL; + } + + YP_CHECK_RET; +} + +_public_ +int yp_addr_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Check for address@port separator. + const uint8_t *pos = (uint8_t *)strrchr((char *)in->position, '@'); + // Ignore out-of-bounds result. + if (pos >= stop) { + pos = NULL; + } + + // Store address type position. + uint8_t *type = out->position; + + // Write the address without a port. + int ret = yp_addr_noport_to_bin(in, out, pos, true); + if (ret != KNOT_EOK) { + return ret; + } + + if (pos != NULL) { + if (*type == 0) { + // Rewrite string terminator. + wire_ctx_skip(out, -1); + // Append the rest (separator and port) as a string. + ret = yp_str_to_bin(in, out, stop); + } else { + // Skip the separator. + wire_ctx_skip(in, sizeof(uint8_t)); + + // Write the port as a number. + ret = yp_int_to_bin(in, out, stop, 0, UINT16_MAX, YP_SNONE); + } + if (ret != KNOT_EOK) { + return ret; + } + } else if (*type == 4 || *type == 6) { + wire_ctx_write_u64(out, (uint64_t)-1); + } + + YP_CHECK_RET; +} + +_public_ +int yp_addr_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + // Store address type position. + uint8_t *type = in->position; + + // Write address. + int ret = yp_addr_noport_to_txt(in, out); + if (ret != KNOT_EOK) { + return ret; + } + + // Write port. + if (*type == 4 || *type == 6) { + int64_t port = wire_ctx_read_u64(in); + + if (port >= 0) { + // Write separator. + wire_ctx_write_u8(out, '@'); + + // Write port. + wire_ctx_skip(in, -sizeof(uint64_t)); + ret = yp_int_to_txt(in, out, YP_SNONE); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + YP_CHECK_RET; +} + +_public_ +int yp_addr_range_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Format: 0 - single address, 1 - address prefix, 2 - address range. + uint8_t format = 0; + + const bool unix_path = (in->position[0] == '/'); + const uint8_t *pos = NULL; + + if (!unix_path) { + // Check for the "addr/mask" format. + pos = (uint8_t *)strchr((char *)in->position, '/'); + if (pos >= stop) { + pos = NULL; + } + + if (pos != NULL) { + format = 1; + } else { + // Check for the "addr1-addr2" format. + pos = (uint8_t *)strchr((char *)in->position, '-'); + if (pos >= stop) { + pos = NULL; + } + if (pos != NULL) { + format = 2; + } + } + } + + // Store address1 type position. + uint8_t *type1 = out->position; + + // Write the first address. + int ret = yp_addr_noport_to_bin(in, out, pos, unix_path); + if (ret != KNOT_EOK) { + return ret; + } + + wire_ctx_write_u8(out, format); + + switch (format) { + case 1: + // Skip the separator. + wire_ctx_skip(in, sizeof(uint8_t)); + + // Write the prefix length. + ret = yp_int_to_bin(in, out, stop, 0, (*type1 == 4) ? 32 : 128, + YP_SNONE); + if (ret != KNOT_EOK) { + return ret; + } + break; + case 2: + // Skip the separator. + wire_ctx_skip(in, sizeof(uint8_t)); + + // Store address2 type position. + uint8_t *type2 = out->position; + + // Write the second address. + ret = yp_addr_noport_to_bin(in, out, stop, false); + if (ret != KNOT_EOK) { + return ret; + } + + // Check for address mismatch. + if (*type1 != *type2) { + return KNOT_EINVAL; + } + break; + default: + break; + } + + YP_CHECK_RET; +} + +_public_ +int yp_addr_range_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + // Write the first address. + int ret = yp_addr_noport_to_txt(in, out); + if (ret != KNOT_EOK) { + return ret; + } + + uint8_t format = wire_ctx_read_u8(in); + + switch (format) { + case 1: + // Write the separator. + wire_ctx_write_u8(out, '/'); + + // Write the prefix length. + ret = yp_int_to_txt(in, out, YP_SNONE); + if (ret != KNOT_EOK) { + return ret; + } + break; + case 2: + // Write the separator. + wire_ctx_write_u8(out, '-'); + + // Write the second address. + ret = yp_addr_noport_to_txt(in, out); + if (ret != KNOT_EOK) { + return ret; + } + break; + default: + break; + } + + YP_CHECK_RET; +} + +_public_ +int yp_option_to_bin( + YP_TXT_BIN_PARAMS, + const knot_lookup_t *opts) +{ + YP_CHECK_PARAMS_BIN; + + while (opts->name != NULL) { + if (YP_LEN == strlen(opts->name) && + strncasecmp((char *)in->position, opts->name, YP_LEN) == 0) { + wire_ctx_write_u8(out, opts->id); + wire_ctx_skip(in, YP_LEN); + YP_CHECK_RET; + } + opts++; + } + + return KNOT_EINVAL; +} + +_public_ +int yp_option_to_txt( + YP_BIN_TXT_PARAMS, + const knot_lookup_t *opts) +{ + uint8_t id = wire_ctx_read_u8(in); + + while (opts->name != NULL) { + if (id == opts->id) { + int ret = snprintf((char *)out->position, + wire_ctx_available(out), "%s", + opts->name); + if (ret <= 0 || ret >= wire_ctx_available(out)) { + return KNOT_ESPACE; + } + wire_ctx_skip(out, ret); + YP_CHECK_RET; + } + opts++; + } + + return KNOT_EINVAL; +} + +_public_ +int yp_dname_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Copy dname string to the buffer to limit dname_from_str overread. + knot_dname_txt_storage_t buf; + wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf)); + if (buf_ctx.error != KNOT_EOK) { + return buf_ctx.error; + } + + // Convert the dname. + if (knot_dname_from_str(out->position, buf, wire_ctx_available(out)) == NULL) { + return KNOT_EINVAL; + } + + // Check the result and count the length. + int ret = knot_dname_wire_check(out->position, + out->position + wire_ctx_available(out), + NULL); + if (ret <= 0) { + return KNOT_EINVAL; + } + + // Convert the result to lower case. + knot_dname_to_lower(out->position); + + wire_ctx_skip(out, ret); + + YP_CHECK_RET; +} + +_public_ +int yp_dname_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + if (knot_dname_to_str((char *)out->position, in->position, + wire_ctx_available(out)) == NULL) { + return KNOT_EINVAL; + } + + wire_ctx_skip(out, strlen((char *)out->position)); + + YP_CHECK_RET; +} + +static int hex_to_num(char hex) { + if (hex >= '0' && hex <= '9') return hex - '0'; + if (hex >= 'a' && hex <= 'f') return hex - 'a' + 10; + if (hex >= 'A' && hex <= 'F') return hex - 'A' + 10; + return -1; +} + +_public_ +int yp_hex_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Check for hex notation (leading "0x"). + if (wire_ctx_available(in) >= 2 && + in->position[0] == '0' && in->position[1] == 'x') { + wire_ctx_skip(in, 2); + + if (YP_LEN % 2 != 0) { + return KNOT_EINVAL; + } + + // Write data length. + wire_ctx_write_u16(out, YP_LEN / 2); + + // Decode hex string. + while (YP_LEN > 0) { + uint8_t buf[2] = { 0 }; + wire_ctx_read(in, buf, sizeof(buf)); + + if (!is_xdigit(buf[0]) || + !is_xdigit(buf[1])) { + return KNOT_EINVAL; + } + + wire_ctx_write_u8(out, 16 * hex_to_num(buf[0]) + + hex_to_num(buf[1])); + } + } else { + // Write data length. + wire_ctx_write_u16(out, YP_LEN); + + // Write textual string (without terminator). + wire_ctx_write(out, in->position, YP_LEN); + wire_ctx_skip(in, YP_LEN); + } + + YP_CHECK_RET; +} + +_public_ +int yp_hex_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + size_t len = wire_ctx_read_u16(in); + + bool printable = true; + + // Check for printable string. + for (size_t i = 0; i < len; i++) { + if (!is_print(in->position[i])) { + printable = false; + break; + } + } + + if (printable) { + wire_ctx_write(out, in->position, len); + wire_ctx_skip(in, len); + } else { + const char *prefix = "0x"; + const char *hex = "0123456789ABCDEF"; + + // Write hex prefix. + wire_ctx_write(out, (uint8_t *)prefix, strlen(prefix)); + + // Encode data to hex. + for (size_t i = 0; i < len; i++) { + uint8_t bin = wire_ctx_read_u8(in); + wire_ctx_write_u8(out, hex[bin / 16]); + wire_ctx_write_u8(out, hex[bin % 16]); + } + } + + // Write the terminator. + wire_ctx_write_u8(out, '\0'); + wire_ctx_skip(out, -1); + + YP_CHECK_RET; +} + +_public_ +int yp_base64_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Reserve some space for data length. + wire_ctx_skip(out, sizeof(uint16_t)); + + int ret = knot_base64_decode(in->position, YP_LEN, out->position, + wire_ctx_available(out)); + if (ret < 0) { + return ret; + } + wire_ctx_skip(in, YP_LEN); + + // Write the data length. + wire_ctx_skip(out, -sizeof(uint16_t)); + wire_ctx_write_u16(out, ret); + wire_ctx_skip(out, ret); + + YP_CHECK_RET; +} + +_public_ +int yp_base64_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + // Read the data length. + uint16_t len = wire_ctx_read_u16(in); + + int ret = knot_base64_encode(in->position, len, out->position, + wire_ctx_available(out)); + if (ret < 0) { + return ret; + } + wire_ctx_skip(out, ret); + + // Write the terminator. + wire_ctx_write_u8(out, '\0'); + wire_ctx_skip(out, -1); + + YP_CHECK_RET; +} + +_public_ +int yp_item_to_bin( + const yp_item_t *item, + const char *txt, + size_t txt_len, + uint8_t *bin, + size_t *bin_len) +{ + if (item == NULL || txt == NULL || bin == NULL || bin_len == NULL) { + return KNOT_EINVAL; + } + + wire_ctx_t in = wire_ctx_init_const((const uint8_t *)txt, txt_len); + wire_ctx_t out = wire_ctx_init(bin, *bin_len); + + int ret; + size_t ref_len; + + switch (item->type) { + case YP_TINT: + ret = yp_int_to_bin(&in, &out, NULL, item->var.i.min, + item->var.i.max, item->var.i.unit); + break; + case YP_TBOOL: + ret = yp_bool_to_bin(&in, &out, NULL); + break; + case YP_TOPT: + ret = yp_option_to_bin(&in, &out, NULL, item->var.o.opts); + break; + case YP_TSTR: + ret = yp_str_to_bin(&in, &out, NULL); + break; + case YP_TADDR: + ret = yp_addr_to_bin(&in, &out, NULL); + break; + case YP_TNET: + ret = yp_addr_range_to_bin(&in, &out, NULL); + break; + case YP_TDNAME: + ret = yp_dname_to_bin(&in, &out, NULL); + break; + case YP_THEX: + ret = yp_hex_to_bin(&in, &out, NULL); + break; + case YP_TB64: + ret = yp_base64_to_bin(&in, &out, NULL); + break; + case YP_TDATA: + ret = item->var.d.to_bin(&in, &out, NULL); + break; + case YP_TREF: + ref_len = wire_ctx_available(&out); + ret = yp_item_to_bin(item->var.r.ref->var.g.id, + (char *)in.position, wire_ctx_available(&in), + out.position, &ref_len); + wire_ctx_skip(&out, ref_len); + break; + default: + ret = KNOT_EOK; + } + + if (ret != KNOT_EOK) { + return ret; + } else if (in.error != KNOT_EOK) { + return in.error; + } else if (out.error != KNOT_EOK) { + return out.error; + } + + *bin_len = wire_ctx_offset(&out); + + return KNOT_EOK; +} + +_public_ +int yp_item_to_txt( + const yp_item_t *item, + const uint8_t *bin, + size_t bin_len, + char *txt, + size_t *txt_len, + yp_style_t style) +{ + if (item == NULL || bin == NULL || txt == NULL || txt_len == NULL) { + return KNOT_EINVAL; + } + + wire_ctx_t in = wire_ctx_init_const(bin, bin_len); + wire_ctx_t out = wire_ctx_init((uint8_t *)txt, *txt_len); + + // Write leading quote. + if (!(style & YP_SNOQUOTE)) { + wire_ctx_write_u8(&out, '"'); + } + + int ret; + size_t ref_len; + + switch (item->type) { + case YP_TINT: + ret = yp_int_to_txt(&in, &out, item->var.i.unit & style); + break; + case YP_TBOOL: + ret = yp_bool_to_txt(&in, &out); + break; + case YP_TOPT: + ret = yp_option_to_txt(&in, &out, item->var.o.opts); + break; + case YP_TSTR: + ret = yp_str_to_txt(&in, &out); + break; + case YP_TADDR: + ret = yp_addr_to_txt(&in, &out); + break; + case YP_TNET: + ret = yp_addr_range_to_txt(&in, &out); + break; + case YP_TDNAME: + ret = yp_dname_to_txt(&in, &out); + break; + case YP_THEX: + ret = yp_hex_to_txt(&in, &out); + break; + case YP_TB64: + ret = yp_base64_to_txt(&in, &out); + break; + case YP_TDATA: + ret = item->var.d.to_txt(&in, &out); + break; + case YP_TREF: + ref_len = wire_ctx_available(&out); + ret = yp_item_to_txt(item->var.r.ref->var.g.id, + in.position, wire_ctx_available(&in), + (char *)out.position, + &ref_len, style | YP_SNOQUOTE); + wire_ctx_skip(&out, ref_len); + break; + default: + ret = KNOT_EOK; + } + + // Write trailing quote. + if (!(style & YP_SNOQUOTE)) { + wire_ctx_write_u8(&out, '"'); + } + + // Write string terminator. + wire_ctx_write_u8(&out, '\0'); + wire_ctx_skip(&out, -1); + + if (ret != KNOT_EOK) { + return ret; + } else if (in.error != KNOT_EOK) { + return in.error; + } else if (out.error != KNOT_EOK) { + return out.error; + } + + *txt_len = wire_ctx_offset(&out); + + return KNOT_EOK; +} + +_public_ +struct sockaddr_storage yp_addr_noport( + const uint8_t *data) +{ + struct sockaddr_storage ss = { AF_UNSPEC }; + + // Read address type. + uint8_t type = *data; + data += sizeof(type); + + // Set address. + switch (type) { + case 0: + sockaddr_set(&ss, AF_UNIX, (char *)data, 0); + break; + case 4: + sockaddr_set_raw(&ss, AF_INET, data, + sizeof(((struct in_addr *)NULL)->s_addr)); + break; + case 6: + sockaddr_set_raw(&ss, AF_INET6, data, + sizeof(((struct in6_addr *)NULL)->s6_addr)); + break; + } + + return ss; +} + +_public_ +struct sockaddr_storage yp_addr( + const uint8_t *data, + bool *no_port) +{ + struct sockaddr_storage ss = yp_addr_noport(data); + + size_t addr_len; + + // Get binary address length. + switch (ss.ss_family) { + case AF_INET: + addr_len = sizeof(((struct in_addr *)NULL)->s_addr); + break; + case AF_INET6: + addr_len = sizeof(((struct in6_addr *)NULL)->s6_addr); + break; + default: + addr_len = 0; + *no_port = true; + } + + if (addr_len > 0) { + int64_t port = knot_wire_read_u64(data + sizeof(uint8_t) + addr_len); + if (port >= 0) { + sockaddr_port_set(&ss, port); + *no_port = false; + } else { + *no_port = true; + } + } + + return ss; +} |