diff options
Diffstat (limited to 'lib/utils.h')
-rw-r--r-- | lib/utils.h | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/lib/utils.h b/lib/utils.h new file mode 100644 index 0000000..54682bb --- /dev/null +++ b/lib/utils.h @@ -0,0 +1,517 @@ +/* Copyright (C) 2014-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include <assert.h> +#include <dirent.h> +#include <stdio.h> +#include <stdbool.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <unistd.h> + +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> + +#include <libknot/libknot.h> +#include <libknot/packet/pkt.h> +#include <libknot/rrset.h> +#include <libknot/rrtype/rrsig.h> +#include <uv.h> + +#include "kresconfig.h" +#include "contrib/mempattern.h" +#include "lib/defines.h" +#include "lib/generic/array.h" + +struct kr_query; +struct kr_request; + +/* + * Logging and debugging. + */ + +/** @brief Callback for request events. */ +typedef void (*trace_callback_f)(struct kr_request *request); +/** + * @brief Callback for request logging handler. + * @param[in] msg Log message. Pointer is not valid after handler returns. */ +typedef void (*trace_log_f)(const struct kr_request *request, const char *msg); + +#define kr_log_info printf +#define kr_log_error(...) fprintf(stderr, ## __VA_ARGS__) +#define kr_log_deprecate(...) fprintf(stderr, "deprecation WARNING: " __VA_ARGS__) + +/* Always export these, but override direct calls by macros conditionally. */ +/** Whether in --verbose mode. Only use this for reading. */ +KR_EXPORT extern bool kr_verbose_status; + +/** Set --verbose mode. Not available if compiled with -DNOVERBOSELOG. */ +KR_EXPORT bool kr_verbose_set(bool status); + +/** + * @brief Return true if the query has request log handler installed. + */ +#define kr_log_rtrace_enabled(req) (__builtin_expect( \ + (req) && (req)->trace_log, \ + false)) + +#define kr_log_qtrace_enabled(qry) (__builtin_expect( \ + (qry) && kr_log_rtrace_enabled(qry->request), \ + false)) + +/** + * Log a message through the request log handler or stdout. + * Caller is responsible for detecting verbose mode, use QRVERBOSE() macro. + * @param qry_uid query ID to append to request ID, 0 means "no query" + * @param indent level of indentation between [req.qry][source] and message + * @param source message source + * @param fmt message format + */ +KR_EXPORT KR_PRINTF(5) +void kr_log_req(const struct kr_request * const req, uint32_t qry_uid, + const unsigned int indent, const char *source, const char *fmt, ...); + +/** + * Log a message through the request log handler or stdout. + * Caller is responsible for detecting verbose mode, use QRVERBOSE() macro. + * @param qry current query + * @param source message source + * @param fmt message format + */ +KR_EXPORT KR_PRINTF(3) +void kr_log_q(const struct kr_query *qry, const char *source, const char *fmt, ...); + +#ifdef NOVERBOSELOG +/* Efficient compile-time disabling of verbose messages. */ +#define kr_verbose_status false +#define kr_verbose_set(x) +#endif + +/** Block run in --verbose mode; optimized when not run. */ +#define VERBOSE_STATUS __builtin_expect(kr_verbose_status, false) +#define WITH_VERBOSE(query) if(__builtin_expect(kr_verbose_status || kr_log_qtrace_enabled(query), false)) +#define kr_log_verbose if(VERBOSE_STATUS) printf + +#define KR_DNAME_GET_STR(dname_str, dname) \ + char dname_str[KR_DNAME_STR_MAXLEN]; \ + knot_dname_to_str(dname_str, (dname), sizeof(dname_str)); \ + dname_str[sizeof(dname_str) - 1] = 0; + +#define KR_RRTYPE_GET_STR(rrtype_str, rrtype) \ + char rrtype_str[KR_RRTYPE_STR_MAXLEN]; \ + knot_rrtype_to_string((rrtype), rrtype_str, sizeof(rrtype_str)); \ + rrtype_str[sizeof(rrtype_str) - 1] = 0; + +/* C11 compatibility, but without any implementation so far. */ +#ifndef static_assert +#define static_assert(cond, msg) +#endif + +// Use this for alocations with mm. +// Use mm_alloc for alocations into mempool + +/** A strcmp() variant directly usable for qsort() on an array of strings. */ +static inline int strcmp_p(const void *p1, const void *p2) +{ + return strcmp(*(char * const *)p1, *(char * const *)p2); +} + + +/** Return time difference in miliseconds. + * @note based on the _BSD_SOURCE timersub() macro */ +static inline long time_diff(struct timeval *begin, struct timeval *end) { + struct timeval res = { + .tv_sec = end->tv_sec - begin->tv_sec, + .tv_usec = end->tv_usec - begin->tv_usec + }; + if (res.tv_usec < 0) { + --res.tv_sec; + res.tv_usec += 1000000; + } + return res.tv_sec * 1000 + res.tv_usec / 1000; +} + +/** Get current working directory with fallback value. */ +static inline void get_workdir(char *out, size_t len) { + if(getcwd(out, len) == NULL) { + static const char errprefix[] = "<invalid working directory>"; + strncpy(out, errprefix, len); + } +} + +/** @cond internal Array types */ +struct kr_context; + +struct ranked_rr_array_entry { + uint32_t qry_uid; + uint8_t rank; /**< enum kr_rank */ + uint8_t revalidation_cnt; + bool cached : 1; /**< Set to true if the entry was written into cache */ + bool yielded : 1; + bool to_wire : 1; /**< whether to be put into the answer */ + bool expiring : 1; /**< low remaining TTL; see is_expiring; only used in cache ATM */ + bool in_progress : 1; /**< build of RRset in progress, i.e. different format of RR data */ + bool dont_cache : 1; /**< avoid caching; useful e.g. for generated data */ + knot_rrset_t *rr; +}; +typedef struct ranked_rr_array_entry ranked_rr_array_entry_t; + +/** Array of RRsets coming from multiple queries; for struct kr_request. + * + * Notes: + * - RRSIGs are only considered to form an RRset when the types covered match; + * cache-related code relies on that! + * - RRsets from the same packet (qry_uid) get merged. + */ +typedef array_t(ranked_rr_array_entry_t *) ranked_rr_array_t; +/* @endcond */ + +/** Concatenate N strings. */ +KR_EXPORT +char* kr_strcatdup(unsigned n, ...); + +/** Construct absolute file path, without resolving symlinks. + * \return malloc-ed string or NULL (+errno in that case) */ +KR_EXPORT +char * kr_absolutize_path(const char *dirname, const char *fname); + +/** You probably want kr_rand_* convenience functions instead. + * This is a buffered version of gnutls_rnd(GNUTLS_RND_NONCE, ..) */ +KR_EXPORT +void kr_rnd_buffered(void *data, unsigned int size); + +/** Return a few random bytes. */ +static inline uint64_t kr_rand_bytes(unsigned int size) +{ + uint64_t result; + if (size <= 0 || size > sizeof(result)) { + kr_log_error("kr_rand_bytes(): EINVAL\n"); + abort(); + } + uint8_t data[sizeof(result)]; + kr_rnd_buffered(data, size); + /* I would have liked to dump the random data into a size_t directly, + * but that would work well only on little-endian machines, + * so instead I hope that the compiler will optimize this out. + * (Tested via reading assembly from usual gcc -O2 setup.) + * Alternatively we could waste more rnd bytes, but that seemed worse. */ + result = 0; + for (unsigned int i = 0; i < size; ++ i) { + result |= ((size_t)data[i]) << (i * 8); + } + return result; +} + +/** Throw a pseudo-random coin, succeeding approximately with probability nomin/denomin. + * - low precision, only one byte of randomness (or none with extreme parameters) + * - tip: use !kr_rand_coin() to get the complementary probability + */ +static inline bool kr_rand_coin(unsigned int nomin, unsigned int denomin) +{ + /* This function might be called with non-constant values + * so we try to handle odd corner cases instead of crash. */ + if (nomin >= denomin) + return true; + else if (nomin <= 0) + return false; + + /* threshold = how many parts from 256 are a success */ + unsigned int threshold = (nomin * 256 + /*rounding*/ denomin / 2) / denomin; + if (threshold == 0) threshold = 1; + if (threshold == 256) threshold = 255; + return (kr_rand_bytes(1) < threshold); +} + +/** Memory reservation routine for knot_mm_t */ +KR_EXPORT +int kr_memreserve(void *baton, void **mem, size_t elm_size, size_t want, size_t *have); + +/** @internal Fast packet reset. */ +KR_EXPORT +int kr_pkt_recycle(knot_pkt_t *pkt); + +/** @internal Clear packet payload. */ +KR_EXPORT +int kr_pkt_clear_payload(knot_pkt_t *pkt); + +/** Construct and put record to packet. */ +KR_EXPORT +int kr_pkt_put(knot_pkt_t *pkt, const knot_dname_t *name, uint32_t ttl, + uint16_t rclass, uint16_t rtype, const uint8_t *rdata, uint16_t rdlen); + +/** Set packet header suitable for authoritative answer. (for policy module) */ +KR_EXPORT +void kr_pkt_make_auth_header(knot_pkt_t *pkt); + +/** Simple storage for IPx address or AF_UNSPEC. */ +union inaddr { + struct sockaddr ip; + struct sockaddr_in ip4; + struct sockaddr_in6 ip6; +}; + +/** Address bytes for given family. */ +KR_EXPORT KR_PURE +const char *kr_inaddr(const struct sockaddr *addr); +/** Address family. */ +KR_EXPORT KR_PURE +int kr_inaddr_family(const struct sockaddr *addr); +/** Address length for given family, i.e. sizeof(struct in*_addr). */ +KR_EXPORT KR_PURE +int kr_inaddr_len(const struct sockaddr *addr); +/** Sockaddr length for given family, i.e. sizeof(struct sockaddr_in*). */ +KR_EXPORT KR_PURE +int kr_sockaddr_len(const struct sockaddr *addr); +/** Compare two given sockaddr. + * return 0 - addresses are equal, error code otherwise. + */ +KR_EXPORT KR_PURE +int kr_sockaddr_cmp(const struct sockaddr *left, const struct sockaddr *right); +/** Port. */ +KR_EXPORT KR_PURE +uint16_t kr_inaddr_port(const struct sockaddr *addr); +/** Set port. */ +KR_EXPORT +void kr_inaddr_set_port(struct sockaddr *addr, uint16_t port); + +/** Write string representation for given address as "<addr>#<port>". + * \param[in] addr the raw address + * \param[out] buf the buffer for output string + * \param[in,out] buflen the available(in) and utilized(out) length, including \0 */ +KR_EXPORT +int kr_inaddr_str(const struct sockaddr *addr, char *buf, size_t *buflen); + +/** Write string representation for given address as "<addr>#<port>". + * It's the same as kr_inaddr_str(), but the input address is input in native format + * like for inet_ntop() (4 or 16 bytes) and port must be separate parameter. */ +KR_EXPORT +int kr_ntop_str(int family, const void *src, uint16_t port, char *buf, size_t *buflen); + +/** @internal Create string representation addr#port. + * @return pointer to static string + */ +static inline char *kr_straddr(const struct sockaddr *addr) +{ + assert(addr != NULL); + /* We are the sinle-threaded application */ + static char str[INET6_ADDRSTRLEN + 1 + 5 + 1]; + size_t len = sizeof(str); + int ret = kr_inaddr_str(addr, str, &len); + return ret != kr_ok() || len == 0 ? NULL : str; +} + + +/** Return address type for string. */ +KR_EXPORT KR_PURE +int kr_straddr_family(const char *addr); +/** Return address length in given family (struct in*_addr). */ +KR_EXPORT KR_CONST +int kr_family_len(int family); + +/** Create a sockaddr* from string+port representation. + * Also accepts IPv6 link-local and AF_UNIX starting with "/" (ignoring port) */ +KR_EXPORT +struct sockaddr * kr_straddr_socket(const char *addr, int port, knot_mm_t *pool); + +/** Parse address and return subnet length (bits). + * @warning 'dst' must be at least `sizeof(struct in6_addr)` long. */ +KR_EXPORT +int kr_straddr_subnet(void *dst, const char *addr); + +/** Splits ip address specified as "addr@port" or "addr#port" into addr and port. + * \param[in] instr zero-terminated input, e.g. "192.0.2.1#12345\0" + * \param[out] ipaddr working buffer for the port-less prefix of instr; + * length >= INET6_ADDRSTRLEN + 1. + * \param[out] port written in case it's specified in instr + * \return error code + * \note Typically you follow this by kr_straddr_socket(). + * \note Only internet addresses are supported, i.e. no AF_UNIX sockets. + */ +KR_EXPORT +int kr_straddr_split(const char *instr, char ipaddr[static restrict (INET6_ADDRSTRLEN + 1)], + uint16_t *port); + +/** Formats ip address and port in "addr#port" format. + * and performs validation. + * @note Port always formatted as five-character string with leading zeros. + * @return kr_error(EINVAL) - addr or buf is NULL or buflen is 0 or + * addr doesn't contain a valid ip address + * kr_error(ENOSP) - buflen is too small + */ +KR_EXPORT +int kr_straddr_join(const char *addr, uint16_t port, char *buf, size_t *buflen); + +/** Compare memory bitwise. The semantics is "the same" as for memcmp(). + * The partial byte is considered with more-significant bits first, + * so this is e.g. suitable for comparing IP prefixes. */ +KR_EXPORT KR_PURE +int kr_bitcmp(const char *a, const char *b, int bits); + +/** @internal RR map flags. */ +static const uint8_t KEY_FLAG_RRSIG = 0x02; +static inline uint8_t KEY_FLAG_RANK(const char *key) + { return ((uint8_t)(key[0])) >> 2; } +static inline bool KEY_COVERING_RRSIG(const char *key) + { return ((uint8_t)(key[0])) & KEY_FLAG_RRSIG; } + +/* Stash key = {[5] class, [1-255] owner, [5] type, [5] additional, [1] \x00 } */ +#define KR_RRKEY_LEN (16 + KNOT_DNAME_MAXLEN) +/** Create unique null-terminated string key for RR. + * @param key Destination buffer for key size, MUST be KR_RRKEY_LEN or larger. + * @param class RR class. + * @param owner RR owner name. + * @param type RR type. + * @param additional flags (for instance can be used for storing covered type + * when RR type is RRSIG). + * @return key length if successful or an error + * */ +KR_EXPORT +int kr_rrkey(char *key, uint16_t class, const knot_dname_t *owner, + uint16_t type, uint16_t additional); + +/** Add RRSet copy to a ranked RR array. + * + * To convert to standard RRs inside, you need to call _finalize() afterwards, + * and the memory of rr->rrs.rdata has to remain until then. + * + * \return array index (>= 0) or error code (< 0) + */ +KR_EXPORT +int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr, + uint8_t rank, bool to_wire, uint32_t qry_uid, knot_mm_t *pool); +/** Finalize in_progress sets - all with matching qry_uid. */ +KR_EXPORT +int kr_ranked_rrarray_finalize(ranked_rr_array_t *array, uint32_t qry_uid, knot_mm_t *pool); + +/** @internal Mark the RRSets from particular query as + * "have (not) to be recorded in the final answer". + * @param array RRSet array. + * @param to_wire Records must be\must not be recorded in final answer. + * @param qry_uid Query uid. + * @param check_dups When to_wire is true, try to avoid duplicate RRSets. + * @param extraCheck optional function checking whether to consider the record + * @return 0 or an error + */ +int kr_ranked_rrarray_set_wire(ranked_rr_array_t *array, bool to_wire, + uint32_t qry_uid, bool check_dups, + bool (*extraCheck)(const ranked_rr_array_entry_t *)); + +/** + * @return Newly allocated string representation of packet. + * Caller has to free() returned string. + */ +KR_EXPORT +char *kr_pkt_text(const knot_pkt_t *pkt); + +KR_PURE +char *kr_rrset_text(const knot_rrset_t *rr); + +KR_PURE +static inline char *kr_dname_text(const knot_dname_t *name) { + return knot_dname_to_str_alloc(name); +} + +KR_CONST +static inline char *kr_rrtype_text(const uint16_t rrtype) { + char type_str[32] = {0}; + knot_rrtype_to_string(rrtype, type_str, sizeof(type_str)); + return strdup(type_str); +} + +/** + * Call module property. + */ +KR_EXPORT +char *kr_module_call(struct kr_context *ctx, const char *module, const char *prop, const char *input); + +/** Swap two places. Note: the parameters need to be without side effects. */ +#define SWAP(x, y) do { /* http://stackoverflow.com/a/3982430/587396 */ \ + unsigned char swap_temp[sizeof(x) == sizeof(y) ? (ssize_t)sizeof(x) : -1]; \ + memcpy(swap_temp, &y, sizeof(x)); \ + memcpy(&y, &x, sizeof(x)); \ + memcpy(&x, swap_temp, sizeof(x)); \ + } while(0) + +/** Return the (covered) type of an nonempty RRset. */ +static inline uint16_t kr_rrset_type_maysig(const knot_rrset_t *rr) +{ + assert(rr && rr->rrs.count && rr->rrs.rdata); + uint16_t type = rr->type; + if (type == KNOT_RRTYPE_RRSIG) + type = knot_rrsig_type_covered(rr->rrs.rdata); + return type; +} + +/** The current time in monotonic milliseconds. + * + * \note it may be outdated in case of long callbacks; see uv_now(). + */ +KR_EXPORT +uint64_t kr_now(); + +/** Call free(handle->data); it's useful e.g. as a callback in uv_close(). */ +KR_EXPORT void kr_uv_free_cb(uv_handle_t* handle); + +/** Convert name from lookup format to wire. See knot_dname_lf + * + * \note len bytes are read and len+1 are written with *normal* LF, + * but it's also allowed that the final zero byte is omitted in LF. + * \return the number of bytes written (>0) or error code (<0) + */ +int knot_dname_lf2wire(knot_dname_t *dst, uint8_t len, const uint8_t *lf); + +/** Patched knot_dname_lf. LF for "." has length zero instead of one, for consistency. + * (TODO: consistency?) + * \param add_wildcard append the wildcard label + * \note packet is always NULL + */ +static inline int kr_dname_lf(uint8_t *dst, const knot_dname_t *src, bool add_wildcard) +{ + knot_dname_storage_t right_aligned_dst; + uint8_t *right_aligned_dname_start = knot_dname_lf(src, right_aligned_dst); + if (!right_aligned_dname_start) { + return kr_error(EINVAL); + } + int len = right_aligned_dname_start[0]; + assert(right_aligned_dname_start + 1 + len - KNOT_DNAME_MAXLEN == right_aligned_dst); + memcpy(dst + 1, right_aligned_dname_start + 1, len); + if (add_wildcard) { + if (len + 2 > KNOT_DNAME_MAXLEN) + return kr_error(ENOSPC); + dst[len + 1] = '*'; + dst[len + 2] = '\0'; + len += 2; + } + dst[0] = len; + return KNOT_EOK; +} + +/** + * Difference between two calendar times specified as strings. + * \param[in] format format for strptime + * \param[out] diff result from C difftime(time1, time0) + */ +KR_EXPORT +const char *kr_strptime_diff(const char *format, const char *time1_str, + const char *time0_str, double *diff); + +/* Trivial non-inline wrappers, to be used in lua. */ +KR_EXPORT void kr_rrset_init(knot_rrset_t *rrset, knot_dname_t *owner, + uint16_t type, uint16_t rclass, uint32_t ttl); +KR_EXPORT uint16_t kr_pkt_has_dnssec(const knot_pkt_t *pkt); +KR_EXPORT uint16_t kr_pkt_qclass(const knot_pkt_t *pkt); +KR_EXPORT uint16_t kr_pkt_qtype(const knot_pkt_t *pkt); +KR_EXPORT uint32_t kr_rrsig_sig_inception(const knot_rdata_t *rdata); +KR_EXPORT uint32_t kr_rrsig_sig_expiration(const knot_rdata_t *rdata); +KR_EXPORT uint16_t kr_rrsig_type_covered(const knot_rdata_t *rdata); + +KR_EXPORT time_t kr_file_mtime (const char* fname); +/** Return filesystem size in bytes. */ +KR_EXPORT long long kr_fssize(const char *path); +/** Simply return de->dname. (useful from Lua) */ +KR_EXPORT const char * kr_dirent_name(const struct dirent *de); + |