diff --git a/CMakeLists.txt b/CMakeLists.txt index 656e050..a56e23e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,11 +26,16 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) find_package(WeeChat REQUIRED) find_package(Tox REQUIRED COMPONENTS CORE - OPTIONAL_COMPONENTS AV ENCRYPTSAVE) + OPTIONAL_COMPONENTS AV ENCRYPTSAVE DNS) +find_package(Ldns) set(PLUGIN_PATH "lib/weechat/plugins" CACHE PATH "Path to install the plugin binary to.") +if(Ldns_FOUND AND Tox_DNS_FOUND) + list(APPEND DNS_SRCS src/twc-dns.c) +endif() + add_library(tox MODULE src/twc.c src/twc-bootstrap.c @@ -38,6 +43,7 @@ add_library(tox MODULE src/twc-commands.c src/twc-completion.c src/twc-config.c + ${DNS_SRCS} src/twc-friend-request.c src/twc-gui.c src/twc-group-invite.c @@ -53,8 +59,9 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -Wno-unused-paramet include_directories(${Tox_INCLUDE_DIRS}) include_directories(${WeeChat_INCLUDE_DIRS}) +include_directories(${Ldns_INCLUDE_DIRS}) -target_link_libraries(tox ${Tox_LIBRARIES}) +target_link_libraries(tox ${Tox_LIBRARIES} ${Ldns_LIBRARIES}) if(Tox_AV_FOUND) add_definitions(-DTOXAV_ENABLED) @@ -64,8 +71,11 @@ if(Tox_ENCRYPTSAVE_FOUND) add_definitions(-DTOXENCRYPTSAVE_ENABLED) endif() +if(Ldns_FOUND AND Tox_DNS_FOUND) + add_definitions(-DLDNS_ENABLED) +endif() + # remove lib prefix (libtox.so -> tox.so) set_target_properties(tox PROPERTIES PREFIX "") install(TARGETS tox DESTINATION "${PLUGIN_PATH}") - diff --git a/cmake/FindLdns.cmake b/cmake/FindLdns.cmake new file mode 100644 index 0000000..dbac43b --- /dev/null +++ b/cmake/FindLdns.cmake @@ -0,0 +1,14 @@ +set(Ldns_INCLUDE_DIRS) +set(Ldns_LIBRARIES) + +find_path(Ldns_INCLUDE_DIR ldns/ldns.h) +find_library(Ldns_LIBRARY ldns) + +if (Ldns_INCLUDE_DIR AND Ldns_LIBRARY) + set(Ldns_FOUND TRUE) + list(APPEND Ldns_LIBRARIES ldns) + list(APPEND Ldns_INCLUDE_DIRS ${Ldns_INCLUDE_DIR}) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Ldns FOUND_VAR Ldns_FOUND REQUIRED_VARS Ldns_INCLUDE_DIRS Ldns_LIBRARIES) diff --git a/src/twc-commands.c b/src/twc-commands.c index edcd628..cc1627a 100644 --- a/src/twc-commands.c +++ b/src/twc-commands.c @@ -27,6 +27,7 @@ #include "twc-list.h" #include "twc-profile.h" #include "twc-chat.h" +#include "twc-dns.h" #include "twc-friend-request.h" #include "twc-group-invite.h" #include "twc-bootstrap.h" @@ -41,6 +42,15 @@ enum TWC_FRIEND_MATCH TWC_FRIEND_MATCH_NOMATCH = -2, }; + +typedef struct +{ + const bool force; + struct t_twc_profile *profile; + char *dns_name; + const char *message; +} t_twc_friend_add_data; + /** * Make sure a command is executed on a Tox profile buffer. If not, warn user * and abort. @@ -215,6 +225,165 @@ twc_cmd_bootstrap(void *data, struct t_gui_buffer *buffer, return WEECHAT_RC_ERROR; } +#ifdef LDNS_ENABLED +void +twc_cmd_dns_cb(void *data, enum t_twc_dns_rc rc, const uint8_t *tox_id) +{ + if (!rc == TWC_DNS_RC_OK) + { + weechat_printf(weechat_current_buffer(), "%s Error resolving Tox DNS ID (%d).", + weechat_prefix("error"), rc); + return; + } + char toxid[TOX_ADDRESS_SIZE * 2 + 1]; + twc_bin2hex(tox_id, TOX_ADDRESS_SIZE, toxid); + toxid[TOX_ADDRESS_SIZE * 2] = 0; + weechat_printf(weechat_current_buffer(), "Resolved Tox DNS ID to %s", toxid); +} + +/** + * Command /dns callback. + */ +int +twc_cmd_dns(void *data, struct t_gui_buffer *buffer, + int argc, char **argv, char**argv_eol) +{ + if (argc == 1) + return WEECHAT_RC_ERROR; + + struct t_twc_profile *profile = twc_profile_search_buffer(buffer); + TWC_CHECK_PROFILE(profile); + TWC_CHECK_PROFILE_LOADED(profile); + + char dns_id[TWC_DNS_ID_MAXLEN + 1]; + snprintf(dns_id, TWC_DNS_ID_MAXLEN, "%s", argv_eol[1]); + enum t_twc_dns_rc dns_rc; + dns_rc = twc_dns_query(dns_id, &twc_cmd_dns_cb, NULL); + if (dns_rc) + { + weechat_printf(buffer, "%sResolving TOX-DNS ID. Error code %d", + weechat_prefix("error"), dns_rc); + return WEECHAT_RC_ERROR; + } + return WEECHAT_RC_OK; +} +#endif // LDNS_ENABLED + +/** + * Sub-command '/friend add' callback. + */ +static void +twc_cmd_friend_add_cb(void *data, enum t_twc_dns_rc rc, const uint8_t *tox_id) +{ + const t_twc_friend_add_data *d = data; + const bool force = d->force; + char *name = d->dns_name; + struct t_twc_profile *profile = d->profile; + const char *message = d->message; + char toxid[TOX_ADDRESS_SIZE * 2 + 1]; + + switch (rc) + { + case TWC_DNS_RC_OK: + break; + case TWC_DNS_RC_VERSION: + weechat_printf(profile->buffer, + "%sUnsupported Tox DNS version in reply.", + weechat_prefix("error")); + return; + case TWC_DNS_RC_ERROR: + weechat_printf(profile->buffer, "%sCould not resolve Tox ID.", + weechat_prefix("error")); + return; + case TWC_DNS_RC_EINVAL: + weechat_printf(profile->buffer, "%sInvalid Tox DNS ID.", + weechat_prefix("error")); + return; + default: + weechat_printf(profile->buffer, + "%sUnknown error resolving Tox ID (%d).", + weechat_prefix("error"), rc); + return; + } + twc_bin2hex(tox_id, TOX_ADDRESS_SIZE, toxid); + toxid[TOX_ADDRESS_SIZE * 2] = 0; + weechat_printf(profile->buffer, "Resolved tox dns id '%s' to %s", name, toxid); + free(name); + + /* -force to delete friend before sending a new friend request */ + if (force) + { + bool fail = false; + char *hex_key = strndup(toxid, TOX_PUBLIC_KEY_SIZE * 2); + int32_t friend_number = twc_match_friend(profile, hex_key); + free(hex_key); + + if (friend_number == TWC_FRIEND_MATCH_AMBIGUOUS) + fail = true; + else if (friend_number != TWC_FRIEND_MATCH_NOMATCH) + fail = !tox_friend_delete(profile->tox, friend_number, NULL); + + if (fail) + { + weechat_printf(profile->buffer, + "%scould not remove friend; please remove " + "manually before resending friend request", + weechat_prefix("error")); + return; + } + } + + TOX_ERR_FRIEND_ADD err; + (void)tox_friend_add(profile->tox, + (uint8_t *)tox_id, + (uint8_t *)message, + strlen(message), &err); + + switch (err) + { + case TOX_ERR_FRIEND_ADD_OK: + weechat_printf(profile->buffer, + "%sFriend request sent!", + weechat_prefix("network")); + break; + case TOX_ERR_FRIEND_ADD_TOO_LONG: + weechat_printf(profile->buffer, + "%sFriend request message too long! Try again.", + weechat_prefix("error")); + break; + case TOX_ERR_FRIEND_ADD_ALREADY_SENT: + case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM: + weechat_printf(profile->buffer, + "%sYou have already sent a friend request to " + "that address (use -force to circumvent)", + weechat_prefix("error")); + break; + case TOX_ERR_FRIEND_ADD_OWN_KEY: + weechat_printf(profile->buffer, + "%sYou can't add yourself as a friend.", + weechat_prefix("error")); + break; + case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM: + weechat_printf(profile->buffer, + "%sInvalid friend address - try again.", + weechat_prefix("error")); + break; + case TOX_ERR_FRIEND_ADD_MALLOC: + weechat_printf(profile->buffer, + "%sCould not add friend (out of memory).", + weechat_prefix("error")); + break; + case TOX_ERR_FRIEND_ADD_NULL: + case TOX_ERR_FRIEND_ADD_NO_MESSAGE: /* this should not happen as we + validate the message */ + default: + weechat_printf(profile->buffer, + "%sCould not add friend (unknown error %d).", + weechat_prefix("error"), err); + break; + } +} + /** * Command /friend callback. */ @@ -286,89 +455,48 @@ twc_cmd_friend(void *data, struct t_gui_buffer *buffer, if (!message) message = weechat_config_string(twc_config_friend_request_message); - if (strlen(hex_id) != TOX_ADDRESS_SIZE * 2) + /* strip "tox:" URI prefix */ + char *l = weechat_strcasestr(hex_id, "tox:"); + if (l && (l == hex_id)) + hex_id = &hex_id[4]; + + char *dns_name = strdup(hex_id); + if (!dns_name) { weechat_printf(profile->buffer, - "%sTox ID length invalid. Please try again.", + "%sMemory allocation error.", weechat_prefix("error")); + return WEECHAT_RC_OK; + } + t_twc_friend_add_data data = { force, profile, dns_name, message }; + uint8_t address[TOX_ADDRESS_SIZE]; + if (strlen(hex_id) != TOX_ADDRESS_SIZE * 2) + { +#ifdef LDNS_ENABLED + /* resolve toxid */ + enum t_twc_dns_rc dns_rc; + dns_rc = twc_dns_query(hex_id, &twc_cmd_friend_add_cb, &data); + if (dns_rc) + { + weechat_printf(profile->buffer, + "%sError calling Tox DNS resolver (%d).", + weechat_prefix("error"), dns_rc); + } +#else + /* immediately fail */ + weechat_printf(profile->buffer, + "%sTox ID length invalid and Tox DNS resolution not implemented." + " Please try again.", + weechat_prefix("error")); +#endif // LDNS_ENABLED return WEECHAT_RC_OK; } - uint8_t address[TOX_ADDRESS_SIZE]; twc_hex2bin(hex_id, TOX_ADDRESS_SIZE, address); + /* call the callback by hand and finish the /friend add command */ + twc_cmd_friend_add_cb(&data, TWC_DNS_RC_OK, address); - if (force) - { - bool fail = false; - char *hex_key = strndup(hex_id, TOX_PUBLIC_KEY_SIZE * 2); - int32_t friend_number = twc_match_friend(profile, hex_key); - free(hex_key); - - if (friend_number == TWC_FRIEND_MATCH_AMBIGUOUS) - fail = true; - else if (friend_number != TWC_FRIEND_MATCH_NOMATCH) - fail = !tox_friend_delete(profile->tox, friend_number, NULL); - - if (fail) - { - weechat_printf(profile->buffer, - "%scould not remove friend; please remove " - "manually before resending friend request", - weechat_prefix("error")); - return WEECHAT_RC_OK; - } - } - - TOX_ERR_FRIEND_ADD err; - (void)tox_friend_add(profile->tox, - (uint8_t *)address, - (uint8_t *)message, - strlen(message), &err); - - switch (err) - { - case TOX_ERR_FRIEND_ADD_OK: - weechat_printf(profile->buffer, - "%sFriend request sent!", - weechat_prefix("network")); - break; - case TOX_ERR_FRIEND_ADD_TOO_LONG: - weechat_printf(profile->buffer, - "%sFriend request message too long! Try again.", - weechat_prefix("error")); - break; - case TOX_ERR_FRIEND_ADD_ALREADY_SENT: - case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM: - weechat_printf(profile->buffer, - "%sYou have already sent a friend request to " - "that address (use -force to circumvent)", - weechat_prefix("error")); - break; - case TOX_ERR_FRIEND_ADD_OWN_KEY: - weechat_printf(profile->buffer, - "%sYou can't add yourself as a friend.", - weechat_prefix("error")); - break; - case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM: - weechat_printf(profile->buffer, - "%sInvalid friend address - try again.", - weechat_prefix("error")); - break; - case TOX_ERR_FRIEND_ADD_MALLOC: - weechat_printf(profile->buffer, - "%sCould not add friend (out of memory).", - weechat_prefix("error")); - break; - case TOX_ERR_FRIEND_ADD_NULL: - case TOX_ERR_FRIEND_ADD_NO_MESSAGE: /* this should not happen as we - validate the message */ - default: - weechat_printf(profile->buffer, - "%sCould not add friend (unknown error %d).", - weechat_prefix("error"), err); - break; - } return WEECHAT_RC_OK; } @@ -668,6 +796,7 @@ twc_cmd_me(void *data, struct t_gui_buffer *buffer, return WEECHAT_RC_OK; } + /** * Command /msg callback. */ @@ -1208,6 +1337,13 @@ twc_commands_init() "", "message: message to send", NULL, twc_cmd_me, NULL); +#ifdef LDNS_ENABLED + weechat_hook_command("dns", + "test toxdns", + "", + "[tox:][@|._tox.]\n", + NULL, twc_cmd_dns, NULL); +#endif // LDNS_ENABLED weechat_hook_command("msg", "send a message to a Tox friend", diff --git a/src/twc-dns.c b/src/twc-dns.c index fb9f132..2527da9 100644 --- a/src/twc-dns.c +++ b/src/twc-dns.c @@ -1,5 +1,6 @@ /* - * Copyright (c) 2015 Håvard Pettersson + * Copyright (c) 2015 Håvard Pettersson , + * Michael Raitza * * This file is part of Tox-WeeChat. * @@ -21,10 +22,14 @@ #include #include #include +#include #include #include +#include +#undef dprintf /* well, yeah, well... */ + #include "twc.h" #include "twc-utils.h" @@ -43,6 +48,312 @@ struct t_twc_dns_callback_info struct t_hook *hook; }; +/** + * Struct holding the split dns_id. + */ +typedef struct +{ + char *name; + char *domain; +} t_twc_toxdns_data; + +/** + * Struct holding a list of DNS responses. + */ +typedef struct +{ + size_t count; + uint8_t **data; +} t_resolve_response_list; + +static t_twc_toxdns_data * +toxdns_data_new(size_t namelen, size_t domlen) +{ + t_twc_toxdns_data *d; + d = (t_twc_toxdns_data *)malloc(sizeof(t_twc_toxdns_data) + + namelen + domlen + 2); + if (!d) + return NULL; + d->name = (char *)malloc(namelen + 1); + if (!d->name) + { + free(d); + return NULL; + } + d->domain = (char *)malloc(domlen + 1); + if (!d->domain) + { + free(d); + free(d->name); + return NULL; + } + return d; +} + +static void +toxdns_data_deep_free(t_twc_toxdns_data *d) +{ + if (d) + { + if (d->name) + free(d->name); + if (d->domain) + free(d->domain); + free(d); + } +} + +/** + * Splits [tox:][@|._tox.] into a struct S with two + * fields S.name = "" and S.domain = "_tox..". Note the + * ending dot that finalises the DNS domain for later query! + * + * name: a string that is shorter than or equal to RESOLVE_ID_MAXLEN + * and must be in the form mentioned above. + */ +static t_twc_toxdns_data * +split_name(const char *name, enum t_twc_dns_rc *error) +{ + enum t_twc_dns_rc err = TWC_DNS_RC_OK; + t_twc_toxdns_data *data = NULL; + uint32_t name_len = strlen(name); + char n[name_len + 1]; + char d[name_len + 6]; + char *domain; + char *d_pos = d; + char *n_pos = n; + + strcpy(n, name); + domain = strstr(n, "tox:"); /* ignore a "tox:" uri prefix */ + if (domain && (domain == n)) + n_pos = &n[4]; + + domain = strstr(n_pos, "._tox."); + if ((!domain) || (domain == n_pos) || (strlen(domain) <= 6)) + { + domain = strstr(n_pos, "@"); + if ((!domain) || (domain == n_pos) || (strlen(domain) <= 1)) + { + err = TWC_DNS_RC_EINVAL; + goto err; + } + d_pos = stpcpy(d_pos, "_tox."); + } + d_pos = stpcpy(d_pos, &domain[1]); + strcpy(d_pos, "."); + domain[0] = 0; + + data = toxdns_data_new(strlen(n_pos), strlen(d)); + if (!data) + { + err = TWC_DNS_RC_ERROR; + goto err; + } + strcpy(data->name, n_pos); + strcpy(data->domain, d); + + err: + *error = err; + return data; +} + +static size_t +resolve_response_count(const t_resolve_response_list *resp) +{ + if (resp) + return resp->count; + return 0; +} + +static uint8_t * +resolve_response_data(const t_resolve_response_list *resp, const size_t nr) +{ + if (nr < resolve_response_count(resp)) + return resp->data[nr]; + return NULL; +} + +static t_resolve_response_list * +resolve_response_list_new(size_t size) +{ + t_resolve_response_list *resps; + resps = (t_resolve_response_list *)malloc(sizeof(t_resolve_response_list) + + sizeof(char*) * size); + if (!resps) + return NULL; + resps->count = 0; + resps->data = (uint8_t **)&resps->data + 1; + return resps; +} + +static void +resolve_response_list_deep_free(t_resolve_response_list *resp) +{ + if (!resp) + return; + for (size_t i = 0; i < resolve_response_count(resp); ++i) + if (resp->data[i]) + free(resp->data[i]); + free(resp); +} + +static ldns_rr_list * +twc_ldns_query(ldns_resolver* res, const char *name, const ldns_rr_type t, + enum t_twc_dns_rc *err) +{ + ldns_rdf *domain = ldns_dname_new_frm_str(name); + ldns_pkt *pkt; + ldns_rr_list *txt = NULL; + + *err = TWC_DNS_RC_ERROR; + + if (!domain) + goto err_dom; + + for (size_t i = 0; i < ldns_resolver_nameserver_count(res); ++i) + { + pkt = ldns_resolver_query(res, domain, t, LDNS_RR_CLASS_IN, LDNS_RD); + if (!pkt) + goto fin; + switch (ldns_pkt_get_rcode(pkt)) + { + case LDNS_RCODE_NOERROR: + *err = TWC_DNS_RC_OK; + txt = ldns_pkt_rr_list_by_type(pkt, t, LDNS_SECTION_ANSWER); + goto fin; + case LDNS_RCODE_REFUSED: + case LDNS_RCODE_NXDOMAIN: + case LDNS_RCODE_SERVFAIL: + for (size_t i = 0; i < ldns_resolver_nameserver_count(res); ++i) + if (!ldns_rdf_compare(ldns_pkt_answerfrom(pkt), + ldns_resolver_nameservers(res)[i])) + { + /* mark faulty nameserver as unreachable */ + ldns_resolver_set_nameserver_rtt(res, i, LDNS_RESOLV_RTT_INF); + break; + } + break; + default: + goto fin; + } + } + fin: + ldns_rdf_deep_free(domain); + if (pkt) + ldns_pkt_free(pkt); + err_dom: + return txt; +} + +/** + * Resolve the TXT records for NAME and return all responses. Outer + * quotes are stripped from TXT records. All fields of a TXT record + * are stiched together to a long string stripping white space as + * requested by protocol. + */ +static t_resolve_response_list * +twc_ldns_resolve(char *name, enum t_twc_dns_rc *err) { + ldns_resolver *res; + ldns_status s; + ldns_rr_list *txt = NULL; + t_resolve_response_list *txts = NULL; + + *err= TWC_DNS_RC_OK; + + s = ldns_resolver_new_frm_file(&res, NULL); + if (s != LDNS_STATUS_OK) + { + *err = TWC_DNS_RC_ERROR; + goto err; + } + txt = twc_ldns_query(res, name, LDNS_RR_TYPE_TXT, err); + if (!txt) + goto err_pkt; + txts = resolve_response_list_new(ldns_rr_list_rr_count(txt)); + if (!txts) + { + *err = TWC_DNS_RC_ERROR; + goto err_txt; + } + + for (size_t i = 0; i < ldns_rr_list_rr_count(txt); ++i) + { + ldns_rr *rr = ldns_rr_list_rr(txt, i); + char txt_field[256] = {0}; /* TXT RR RDATA can be 255 bytes long */ + char *cur_pos = txt_field; + + /* stich together all TXT RR RDATA fields, stripping quotes */ + for (size_t j = 0; j < ldns_rr_rd_count(rr); ++j) + { + char *rdata = ldns_rdf2str(ldns_rr_rdf(rr, j)); + size_t len = strlen(rdata); + + if (rdata[0] == '"') + { + rdata[len - 1] = 0; + cur_pos = stpcpy(cur_pos, &rdata[1]); + } else + cur_pos = stpcpy(cur_pos, rdata); + free(rdata); + } + txts->data[i] = (uint8_t *)strdup(txt_field); + if (!txts->data[i]) + { + *err = TWC_DNS_RC_ERROR; + goto err_resp; + } + ++txts->count; + } + goto err_txt; // Normal return + + err_resp: + resolve_response_list_deep_free(txts); + txts = NULL; + err_txt: + ldns_rr_list_deep_free(txt); + err_pkt: + ldns_resolver_deep_free(res); + err: + return txts; +} + +static uint8_t * +resolve_toxdns1(const t_twc_toxdns_data *data, enum t_twc_dns_rc *error) +{ + char buf[TWC_DNS_ID_MAXLEN + 2]; + t_resolve_response_list *id_rrs; + uint8_t *toxid = NULL; + *error = TWC_DNS_RC_OK; + + sprintf(buf, "%s.%s", data->name, data->domain); + id_rrs = twc_ldns_resolve(buf, error); + if (!id_rrs) + goto err; + + for (size_t j = 0; j < id_rrs->count; ++j) + { + uint8_t *id_rr = resolve_response_data(id_rrs, j); + if (strncmp("v=tox1;id=", (char *)id_rr, 10)) + { + *error = TWC_DNS_RC_VERSION; + continue; + } + toxid = (uint8_t *)strndup((char *)&id_rr[10], TOX_ADDRESS_SIZE * 2); + if (!toxid) + { + *error = TWC_DNS_RC_ERROR; + goto err_id; + } + *error = TWC_DNS_RC_OK; + break; + } + + err_id: + resolve_response_list_deep_free(id_rrs); + err: + return toxid; +} + /** * Callback when the DNS resolver child process has written to our file * descriptor. Process the data a bit and pass it on to the original callback. @@ -87,7 +398,35 @@ twc_dns_fd_callback(void *data, int fd) void twc_perform_dns_lookup(const char *dns_id, int out_fd) { - dprintf(out_fd, "%d", TWC_DNS_RC_ERROR); + enum t_twc_dns_rc err = TWC_DNS_RC_OK; + t_twc_toxdns_data *d; + uint8_t *toxid; + + if (!dns_id || weechat_strlen_screen(dns_id) > TWC_DNS_ID_MAXLEN) + { + err = TWC_DNS_RC_EINVAL; + goto err; + } + /* Split name from domain in preparation for tox3dns protocol. */ + d = split_name(dns_id, &err); + + if (!d) + { + err = TWC_DNS_RC_EINVAL; + goto err; + } + + /* For now only resolve Tox DNS version 1 addresses */ + toxid = resolve_toxdns1(d, &err); + if (!toxid) + goto err; + + err: + if (err) + dprintf(out_fd, "%d", err); + else + dprintf(out_fd, "%s", toxid); + toxdns_data_deep_free(d); } /** diff --git a/src/twc-dns.h b/src/twc-dns.h index ece5714..7b4fce5 100644 --- a/src/twc-dns.h +++ b/src/twc-dns.h @@ -22,10 +22,19 @@ #include +#ifdef LDNS_ENABLED +#include + +#define TWC_DNS_ID_MAXLEN TOXDNS_MAX_RECOMMENDED_NAME_LENGTH + +#endif // LDNS_ENABLED + enum t_twc_dns_rc { TWC_DNS_RC_OK = 0, TWC_DNS_RC_ERROR = -1, + TWC_DNS_RC_EINVAL = -2, + TWC_DNS_RC_VERSION = -4, }; enum t_twc_dns_rc