diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/resolve-host/resolve-host.c | 149 | ||||
| -rw-r--r-- | src/resolve/resolved-bus.c | 163 | ||||
| -rw-r--r-- | src/resolve/resolved-dns-rr.c | 233 | ||||
| -rw-r--r-- | src/resolve/resolved-dns-rr.h | 5 | 
4 files changed, 470 insertions, 80 deletions
| diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 122c2622cf..1b1edaf6ec 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -31,10 +31,14 @@  #include "af-list.h"  #include "build.h" +#include "resolved-dns-packet.h" +  #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)  static int arg_family = AF_UNSPEC;  static int arg_ifindex = 0; +static uint16_t arg_type = 0; +static uint16_t arg_class = 0;  static int resolve_host(sd_bus *bus, const char *name) { @@ -286,6 +290,103 @@ static int parse_address(const char *s, int *family, union in_addr_union *addres          return 0;  } +static int resolve_record(sd_bus *bus, const char *name) { + +        _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL; +        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; +        unsigned n = 0; +        int r; + +        assert(name); + +        log_debug("Resolving %s %s %s.", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type)); + +        r = sd_bus_message_new_method_call( +                        bus, +                        &req, +                        "org.freedesktop.resolve1", +                        "/org/freedesktop/resolve1", +                        "org.freedesktop.resolve1.Manager", +                        "ResolveRecord"); +        if (r < 0) +                return bus_log_create_error(r); + +        r = sd_bus_message_set_auto_start(req, false); +        if (r < 0) +                return bus_log_create_error(r); + +        r = sd_bus_message_append(req, "sqq", name, arg_class, arg_type); +        if (r < 0) +                return bus_log_create_error(r); + +        r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); +        if (r < 0) { +                log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r)); +                return r; +        } + +        r = sd_bus_message_enter_container(reply, 'a', "(qqay)"); +        if (r < 0) +                return bus_log_parse_error(r); + +        while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) { +                _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; +                _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; +                _cleanup_free_ char *s = NULL; +                uint16_t c, t; +                const void *d; +                size_t l; + +                r = sd_bus_message_read(reply, "qq", &c, &t); +                if (r < 0) +                        return bus_log_parse_error(r); + +                r = sd_bus_message_read_array(reply, 'y', &d, &l); +                if (r < 0) +                        return bus_log_parse_error(r); + +                r = sd_bus_message_exit_container(reply); +                if (r < 0) +                        return bus_log_parse_error(r); + +                r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); +                if (r < 0) +                        return log_oom(); + +                r = dns_packet_append_blob(p, d, l, NULL); +                if (r < 0) +                        return log_oom(); + +                r = dns_packet_read_rr(p, &rr, NULL); +                if (r < 0) { +                        log_error("Failed to parse RR."); +                        return r; +                } + +                r = dns_resource_record_to_string(rr, &s); +                if (r < 0) { +                        log_error("Failed to format RR."); +                        return r; +                } + +                printf("%s\n", s); +                n++; +        } +        if (r < 0) +                return bus_log_parse_error(r); + +        r = sd_bus_message_exit_container(reply); +        if (r < 0) +                return bus_log_parse_error(r); + +        if (n == 0) { +                log_error("%s: no records found", name); +                return -ESRCH; +        } + +        return 0; +} +  static void help(void) {          printf("%s [OPTIONS...]\n\n"                 "Resolve IPv4 or IPv6 addresses.\n\n" @@ -294,6 +395,8 @@ static void help(void) {                 "  -4                    Resolve IPv4 addresses\n"                 "  -6                    Resolve IPv6 addresses\n"                 "  -i INTERFACE          Filter by interface\n" +               "  -t --type=TYPE        Query RR with DNS type\n" +               "  -c --class=CLASS      Query RR with DNS class\n"                 , program_invocation_short_name);  } @@ -305,15 +408,17 @@ static int parse_argv(int argc, char *argv[]) {          static const struct option options[] = {                  { "help",        no_argument,       NULL, 'h'           },                  { "version",     no_argument,       NULL, ARG_VERSION   }, +                { "type",        no_argument,       NULL, 't'           }, +                { "class",       no_argument,       NULL, 'c'           },                  {}          }; -        int c; +        int c, r;          assert(argc >= 0);          assert(argv); -        while ((c = getopt_long(argc, argv, "h46i:", options, NULL)) >= 0) { +        while ((c = getopt_long(argc, argv, "h46i:t:c:", options, NULL)) >= 0) {                  switch(c) {                  case 'h': @@ -337,7 +442,23 @@ static int parse_argv(int argc, char *argv[]) {                          arg_ifindex = if_nametoindex(optarg);                          if (arg_ifindex <= 0) {                                  log_error("Unknown interfaces %s: %m", optarg); -                                return -EINVAL; +                                return -errno; +                        } +                        break; + +                case 't': +                        r = dns_type_from_string(optarg, &arg_type); +                        if (r < 0) { +                                log_error("Failed to parse RR record type %s", optarg); +                                return r; +                        } +                        break; + +                case 'c': +                        r = dns_class_from_string(optarg, &arg_class); +                        if (r < 0) { +                                log_error("Failed to parse RR record class %s", optarg); +                                return r;                          }                          break; @@ -349,6 +470,14 @@ static int parse_argv(int argc, char *argv[]) {                  }          } +        if (arg_type == 0 && arg_class != 0) { +                log_error("--class= may only be used in conjunction with --type="); +                return -EINVAL; +        } + +        if (arg_type != 0 && arg_class == 0) +                arg_class = DNS_CLASS_IN; +          return 1 /* work to do */;  } @@ -379,11 +508,15 @@ int main(int argc, char **argv) {                  int family, ifindex, k;                  union in_addr_union a; -                k = parse_address(argv[optind], &family, &a, &ifindex); -                if (k >= 0) -                        k = resolve_address(bus, family, &a, ifindex); -                else -                        k = resolve_host(bus, argv[optind]); +                if (arg_type != 0) +                        k = resolve_record(bus, argv[optind]); +                else { +                        k = parse_address(argv[optind], &family, &a, &ifindex); +                        if (k >= 0) +                                k = resolve_address(bus, family, &a, ifindex); +                        else +                                k = resolve_host(bus, argv[optind]); +                }                  if (r == 0)                          r = k; diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index d0c21894e3..0b8ecf1b82 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -157,13 +157,13 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {                  for (i = 0; i < answer->n_rrs; i++) {                          r = dns_question_matches_rr(q->question, answer->rrs[i]);                          if (r < 0) -                                goto parse_fail; +                                goto finish;                          if (r == 0) {                                  /* Hmm, if this is not an address record,                                     maybe it's a cname? If so, remember this */                                  r = dns_question_matches_cname(q->question, answer->rrs[i]);                                  if (r < 0) -                                        goto parse_fail; +                                        goto finish;                                  if (r > 0)                                          cname = dns_resource_record_ref(answer->rrs[i]); @@ -204,7 +204,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {                  for (i = 0; i < answer->n_rrs; i++) {                          r = dns_question_matches_rr(q->question, answer->rrs[i]);                          if (r < 0) -                                goto parse_fail; +                                goto finish;                          if (r == 0)                                  continue; @@ -230,6 +230,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {                                  r = sd_bus_reply_method_errno(q->request, -r, NULL);                                  goto finish;                          } +                          return;                  }          } @@ -245,14 +246,12 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {                  goto finish;          r = sd_bus_send(q->manager->bus, reply, NULL); -        goto finish; - -parse_fail: -        r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");  finish: -        if (r < 0) -                log_error("Failed to send bus reply: %s", strerror(-r)); +        if (r < 0) { +                log_error("Failed to send hostname reply: %s", strerror(-r)); +                sd_bus_reply_method_errno(q->request, -r, NULL); +        }          dns_query_free(q);  } @@ -356,7 +355,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {                  for (i = 0; i < answer->n_rrs; i++) {                          r = dns_question_matches_rr(q->question, answer->rrs[i]);                          if (r < 0) -                                goto parse_fail; +                                goto finish;                          if (r == 0)                                  continue; @@ -382,14 +381,12 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {                  goto finish;          r = sd_bus_send(q->manager->bus, reply, NULL); -        goto finish; - -parse_fail: -        r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");  finish: -        if (r < 0) -                log_error("Failed to send bus reply: %s", strerror(-r)); +        if (r < 0) { +                log_error("Failed to send address reply: %s", strerror(-r)); +                sd_bus_reply_method_errno(q->request, -r, NULL); +        }          dns_query_free(q);  } @@ -469,10 +466,144 @@ static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void          return 1;  } +static void bus_method_resolve_record_complete(DnsQuery *q) { +        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; +        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; +        unsigned added = 0, i; +        int r; + +        assert(q); + +        if (q->state != DNS_QUERY_SUCCESS) { +                r = reply_query_state(q); +                goto finish; +        } + +        r = sd_bus_message_new_method_return(q->request, &reply); +        if (r < 0) +                goto finish; + +        r = sd_bus_message_open_container(reply, 'a', "(qqay)"); +        if (r < 0) +                goto finish; + +        if (q->answer) { +                answer = dns_answer_ref(q->answer); + +                for (i = 0; i < answer->n_rrs; i++) { +                        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; +                        size_t start; + +                        r = dns_question_matches_rr(q->question, answer->rrs[i]); +                        if (r < 0) +                                goto finish; +                        if (r == 0) +                                continue; + +                        r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); +                        if (r < 0) +                                goto finish; + +                        r = dns_packet_append_rr(p, answer->rrs[i], &start); +                        if (r < 0) +                                goto finish; + +                        r = sd_bus_message_open_container(reply, 'r', "qqay"); +                        if (r < 0) +                                goto finish; + +                        r = sd_bus_message_append(reply, "qq", answer->rrs[i]->key->class, answer->rrs[i]->key->type); +                        if (r < 0) +                                goto finish; + +                        r = sd_bus_message_append_array(reply, 'y', DNS_PACKET_DATA(p) + start, p->size - start); +                        if (r < 0) +                                goto finish; + +                        r = sd_bus_message_close_container(reply); +                        if (r < 0) +                                goto finish; + +                        added ++; +                } +        } + +        if (added <= 0) { +                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", q->request_hostname); +                goto finish; +        } + +        r = sd_bus_message_close_container(reply); +        if (r < 0) +                goto finish; + +        r = sd_bus_send(q->manager->bus, reply, NULL); + +finish: +        if (r < 0) { +                log_error("Failed to send record reply: %s", strerror(-r)); +                sd_bus_reply_method_errno(q->request, -r, NULL); +        } + +        dns_query_free(q); +} + +static int bus_method_resolve_record(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { +        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; +        _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; +        _cleanup_free_ char *reverse = NULL; +        Manager *m = userdata; +        DnsQuery *q; +        int r; +        uint16_t class, type; +        const char *name; + +        assert(bus); +        assert(message); +        assert(m); + +        r = sd_bus_message_read(message, "sqq", &name, &class, &type); +        if (r < 0) +                return r; + +        question = dns_question_new(1); +        if (!question) +                return -ENOMEM; + +        key = dns_resource_key_new(class, type, name); +        if (!key) +                return -ENOMEM; + +        r = dns_question_add(question, key); +        if (r < 0) +                return r; + +        r = dns_query_new(m, &q, question); +        if (r < 0) +                return r; + +        q->request = sd_bus_message_ref(message); +        q->request_hostname = name; +        q->complete = bus_method_resolve_record_complete; + +        r = dns_query_go(q); +        if (r < 0) { +                dns_query_free(q); + +                if (r == -ESRCH) +                        sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); + +                return r; +        } + +        return 1; +} +  static const sd_bus_vtable resolve_vtable[] = {          SD_BUS_VTABLE_START(0),          SD_BUS_METHOD("ResolveHostname", "si", "a(iayi)s", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),          SD_BUS_METHOD("ResolveAddress", "iayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), +        SD_BUS_METHOD("ResolveRecord", "sqq", "a(qqay)", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),          SD_BUS_VTABLE_END,  }; diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 8b8858848c..e1200c9bc0 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -159,6 +159,31 @@ int dns_resource_key_compare_func(const void *a, const void *b) {          return 0;  } +int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) { +        char cbuf[DECIMAL_STR_MAX(uint16_t)], tbuf[DECIMAL_STR_MAX(uint16_t)]; +        const char *c, *t; +        char *s; + +        c = dns_class_to_string(key->class); +        if (!c) { +                sprintf(cbuf, "%i", key->class); +                c = cbuf; +        } + +        t = dns_type_to_string(key->type); +        if (!t){ +                sprintf(tbuf, "%i", key->type); +                t = tbuf; +        } + +        s = strjoin(DNS_RESOURCE_KEY_NAME(key), " ", c, " ", t, NULL); +        if (!s) +                return -ENOMEM; + +        *ret = s; +        return 0; +} +  DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {          DnsResourceRecord *rr; @@ -267,16 +292,24 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor          if (r <= 0)                  return r; -        if (IN_SET(a->key->type, DNS_TYPE_PTR, DNS_TYPE_NS, DNS_TYPE_CNAME)) +        switch (a->key->type) { + +        case DNS_TYPE_PTR: +        case DNS_TYPE_NS: +        case DNS_TYPE_CNAME:                  return dns_name_equal(a->ptr.name, b->ptr.name); -        else if (a->key->type == DNS_TYPE_HINFO) -                return strcasecmp(a->hinfo.cpu, b->hinfo.cpu) == 0 && -                       strcasecmp(a->hinfo.os, b->hinfo.os) == 0; -        else if (a->key->type == DNS_TYPE_A) + +        case DNS_TYPE_HINFO: +                return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) && +                       strcaseeq(a->hinfo.os, b->hinfo.os); + +        case DNS_TYPE_A:                  return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0; -        else if (a->key->type == DNS_TYPE_AAAA) + +        case DNS_TYPE_AAAA:                  return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0; -        else if (a->key->type == DNS_TYPE_SOA) { + +        case DNS_TYPE_SOA:                  r = dns_name_equal(a->soa.mname, b->soa.mname);                  if (r <= 0)                          return r; @@ -289,83 +322,171 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor                         a->soa.retry   == b->soa.retry &&                         a->soa.expire  == b->soa.expire &&                         a->soa.minimum == b->soa.minimum; -        } else +        default:                  return a->generic.size == b->generic.size &&                          memcmp(a->generic.data, b->generic.data, a->generic.size) == 0; +        }  } -const char *dns_class_to_string(uint16_t class) { +int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) { +        _cleanup_free_ char *k = NULL; +        char *s; +        int r; -        switch (class) { +        assert(rr); -        case DNS_CLASS_IN: -                return "IN"; +        r = dns_resource_key_to_string(rr->key, &k); +        if (r < 0) +                return r; -        case DNS_CLASS_ANY: -                return "ANY"; -        } +        switch (rr->key->type) { -        return NULL; -} +        case DNS_TYPE_PTR: +        case DNS_TYPE_NS: +        case DNS_TYPE_CNAME: +                s = strjoin(k, " ", rr->ptr.name, NULL); +                if (!s) +                        return -ENOMEM; -const char *dns_type_to_string(uint16_t type) { +                break; -        switch (type) { +        case DNS_TYPE_HINFO: +                s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL); +                if (!s) +                        return -ENOMEM; +                break; -        case DNS_TYPE_A: -                return "A"; +        case DNS_TYPE_A: { +                _cleanup_free_ char *x = NULL; -        case DNS_TYPE_NS: -                return "NS"; +                r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x); +                if (r < 0) +                        return r; -        case DNS_TYPE_CNAME: -                return "CNAME"; +                s = strjoin(k, " ", x, NULL); +                if (!s) +                        return -ENOMEM; +                break; +        } -        case DNS_TYPE_SOA: -                return "SOA"; +        case DNS_TYPE_AAAA: { +                _cleanup_free_ char *x = NULL; -        case DNS_TYPE_PTR: -                return "PTR"; +                r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &x); +                if (r < 0) +                        return r; -        case DNS_TYPE_HINFO: -                return "HINFO"; +                s = strjoin(k, " ", x, NULL); +                if (!s) +                        return -ENOMEM; +                break; +        } -        case DNS_TYPE_MX: -                return "MX"; +        case DNS_TYPE_SOA: +                r = asprintf(&s, "%s %s %s %u %u %u %u %u", +                             k, +                             strna(rr->soa.mname), +                             strna(rr->soa.rname), +                             rr->soa.serial, +                             rr->soa.refresh, +                             rr->soa.retry, +                             rr->soa.expire, +                             rr->soa.minimum); +                if (r < 0) +                        return -ENOMEM; +                break; + +        default: { +                _cleanup_free_ char *x = NULL; + +                x = hexmem(rr->generic.data, rr->generic.size); +                if (!x) +                        return -ENOMEM; + +                s = strjoin(k, " ", x, NULL); +                if (!s) +                        return -ENOMEM; +                break; +        }} + +        *ret = s; +        return 0; +} -        case DNS_TYPE_TXT: -                return "TXT"; +const char *dns_class_to_string(uint16_t class) { -        case DNS_TYPE_AAAA: -                return "AAAA"; +        switch (class) { -        case DNS_TYPE_SRV: -                return "SRV"; +        case DNS_CLASS_IN: +                return "IN"; -        case DNS_TYPE_SSHFP: -                return "SSHFP"; +        case DNS_CLASS_ANY: +                return "ANY"; +        } -        case DNS_TYPE_DNAME: -                return "DNAME"; +        return NULL; +} -        case DNS_TYPE_ANY: -                return "ANY"; +int dns_class_from_string(const char *s, uint16_t *class) { +        assert(s); +        assert(class); + +        if (strcaseeq(s, "IN")) +                *class = DNS_CLASS_IN; +        else if (strcaseeq(s, "ANY")) +                *class = DNS_TYPE_ANY; +        else +                return -EINVAL; -        case DNS_TYPE_OPT: -                return "OPT"; +        return 0; +} -        case DNS_TYPE_TKEY: -                return "TKEY"; +static const struct { +        uint16_t type; +        const char *name; +} dns_types[] = { +        { DNS_TYPE_A,     "A"     }, +        { DNS_TYPE_NS,    "NS"    }, +        { DNS_TYPE_CNAME, "CNAME" }, +        { DNS_TYPE_SOA,   "SOA"   }, +        { DNS_TYPE_PTR,   "PTR"   }, +        { DNS_TYPE_HINFO, "HINFO" }, +        { DNS_TYPE_MX,    "MX"    }, +        { DNS_TYPE_TXT,   "TXT"   }, +        { DNS_TYPE_AAAA,  "AAAA"  }, +        { DNS_TYPE_SRV,   "SRV"   }, +        { DNS_TYPE_SSHFP, "SSHFP" }, +        { DNS_TYPE_DNAME, "DNAME" }, +        { DNS_TYPE_ANY,   "ANY"   }, +        { DNS_TYPE_OPT,   "OPT"   }, +        { DNS_TYPE_TKEY,  "TKEY"  }, +        { DNS_TYPE_TSIG,  "TSIG"  }, +        { DNS_TYPE_IXFR,  "IXFR"  }, +        { DNS_TYPE_AXFR,  "AXFR"  }, +}; -        case DNS_TYPE_TSIG: -                return "TSIG"; -        case DNS_TYPE_IXFR: -                return "IXFR"; +const char *dns_type_to_string(uint16_t type) { +        unsigned i; -        case DNS_TYPE_AXFR: -                return "AXFR"; -        } +        for (i = 0; i < ELEMENTSOF(dns_types); i++) +                if (dns_types[i].type == type) +                        return dns_types[i].name;          return NULL;  } + +int dns_type_from_string(const char *s, uint16_t *type) { +        unsigned i; + +        assert(s); +        assert(type); + +        for (i = 0; i < ELEMENTSOF(dns_types); i++) +                if (strcaseeq(dns_types[i].name, s)) { +                        *type = dns_types[i].type; +                        return 0; +                } + +        return -EINVAL; +} diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 50bb74c67b..524630071d 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -137,6 +137,7 @@ int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord  int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr);  unsigned long dns_resource_key_hash_func(const void *i, const uint8_t hash_key[HASH_KEY_SIZE]);  int dns_resource_key_compare_func(const void *a, const void *b); +int dns_resource_key_to_string(const DnsResourceKey *key, char **ret);  DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref);  DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key); @@ -145,7 +146,11 @@ DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr);  DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr);  int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);  int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b); +int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret);  DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);  const char *dns_type_to_string(uint16_t type); +int dns_type_from_string(const char *name, uint16_t *type); +  const char *dns_class_to_string(uint16_t type); +int dns_class_from_string(const char *name, uint16_t *class); | 
