header_t * dns_header_decode(netif_t *netif, packet_t *packet, raw_packet_t *raw_packet, packet_offset_t offset) { dns_header_t *dns = dns_header_new(); packet_offset_t field_offset; if (raw_packet->len < (offset + DNS_HEADER_LEN)) { LOG_PRINTLN(LOG_HEADER_DNS, LOG_ERROR, ("decode DNS header: size too small (present=%u, required=%u)", raw_packet->len - offset, DNS_HEADER_LEN)); DNS_FAILURE_EXIT; } /* fetch header */ uint8_to_uint16(&(dns->id), &(raw_packet->data[offset + DNS_HEADER_OFFSET_ID])); uint8_to_uint16(&(dns->flags.raw), &(raw_packet->data[offset + DNS_HEADER_OFFSET_FLAGS])); uint8_to_uint16(&(dns->qd_count), &(raw_packet->data[offset + DNS_HEADER_OFFSET_QD_COUNT])); uint8_to_uint16(&(dns->an_count), &(raw_packet->data[offset + DNS_HEADER_OFFSET_AN_COUNT])); uint8_to_uint16(&(dns->ns_count), &(raw_packet->data[offset + DNS_HEADER_OFFSET_NS_COUNT])); uint8_to_uint16(&(dns->ar_count), &(raw_packet->data[offset + DNS_HEADER_OFFSET_AR_COUNT])); field_offset = offset + DNS_HEADER_LEN; /* question section */ if (dns->qd_count > 0) { dns->qd = dns_query_new(); if (!dns_header_decode_query(raw_packet, offset, &field_offset, dns->qd_count, dns->qd)) { dns_query_free(dns->qd); DNS_FAILURE_EXIT; } } /* answer records section */ if (dns->an_count > 0) { dns->an = dns_rr_new(); if (!dns_header_decode_rr(raw_packet, offset, &field_offset, dns->an_count, dns->an)) { dns_rr_free(dns->an); DNS_FAILURE_EXIT; } } /* authority records section */ if (dns->ns_count > 0) { dns->ns = dns_rr_new(); if (!dns_header_decode_rr(raw_packet, offset, &field_offset, dns->ns_count, dns->ns)) { dns_rr_free(dns->ns); DNS_FAILURE_EXIT; } } /* additional records section */ if (dns->ar_count > 0) { dns->ar = dns_rr_new(); if (!dns_header_decode_rr(raw_packet, offset, &field_offset, dns->ar_count, dns->ar)) { dns_rr_free(dns->ar); DNS_FAILURE_EXIT; } } return (header_t *) dns; }
static int bus_method_resolve_address(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; int family, ifindex; uint64_t flags; const void *d; DnsQuery *q; size_t sz; int r; assert(bus); assert(message); assert(m); r = sd_bus_message_read(message, "ii", &ifindex, &family); if (r < 0) return r; if (!IN_SET(family, AF_INET, AF_INET6)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); r = sd_bus_message_read_array(message, 'y', &d, &sz); if (r < 0) return r; if (sz != FAMILY_ADDRESS_SIZE(family)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size"); r = sd_bus_message_read(message, "t", &flags); if (r < 0) return r; r = check_ifindex_flags(ifindex, &flags, error); if (r < 0) return r; r = dns_name_reverse(family, d, &reverse); if (r < 0) return r; question = dns_question_new(1); if (!question) return -ENOMEM; key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse); if (!key) return -ENOMEM; reverse = NULL; r = dns_question_add(question, key); if (r < 0) return r; r = dns_query_new(m, &q, question, ifindex, flags); if (r < 0) return r; q->request = sd_bus_message_ref(message); q->request_family = family; memcpy(&q->request_address, d, sz); q->complete = bus_method_resolve_address_complete; r = dns_query_bus_track(q, bus, message); if (r < 0) return r; 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 int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; Manager *m = userdata; const char *hostname; int family, ifindex; uint64_t flags; DnsQuery *q; int r; assert(bus); assert(message); assert(m); r = sd_bus_message_read(message, "isit", &ifindex, &hostname, &family, &flags); if (r < 0) return r; if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); r = dns_name_normalize(hostname, NULL); if (r < 0) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname); r = check_ifindex_flags(ifindex, &flags, error); if (r < 0) return r; question = dns_question_new(family == AF_UNSPEC ? 2 : 1); if (!question) return -ENOMEM; if (family != AF_INET6) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname); if (!key) return -ENOMEM; r = dns_question_add(question, key); if (r < 0) return r; } if (family != AF_INET) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname); if (!key) return -ENOMEM; r = dns_question_add(question, key); if (r < 0) return r; } r = dns_query_new(m, &q, question, ifindex, flags); if (r < 0) return r; q->request = sd_bus_message_ref(message); q->request_family = family; q->request_hostname = hostname; q->complete = bus_method_resolve_hostname_complete; r = dns_query_bus_track(q, bus, message); if (r < 0) return r; 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 void bus_method_resolve_address_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_TRANSACTION_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_append(reply, "i", q->answer_ifindex); if (r < 0) goto finish; r = sd_bus_message_open_container(reply, 'a', "s"); if (r < 0) goto finish; if (q->answer) { answer = dns_answer_ref(q->answer); for (i = 0; i < answer->n_rrs; i++) { r = dns_question_matches_rr(q->question, answer->rrs[i]); if (r < 0) goto finish; if (r == 0) continue; r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name); if (r < 0) goto finish; added ++; } } if (added <= 0) { _cleanup_free_ char *ip = NULL; in_addr_to_string(q->request_family, &q->request_address, &ip); r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip); goto finish; } r = sd_bus_message_close_container(reply); if (r < 0) goto finish; r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); if (r < 0) goto finish; r = sd_bus_send(q->manager->bus, reply, NULL); finish: if (r < 0) { log_error_errno(r, "Failed to send address reply: %m"); sd_bus_reply_method_errno(q->request, -r, NULL); } dns_query_free(q); }
static void bus_method_resolve_hostname_complete(DnsQuery *q) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL; _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_TRANSACTION_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_append(reply, "i", q->answer_ifindex); if (r < 0) goto finish; r = sd_bus_message_open_container(reply, 'a', "(iay)"); if (r < 0) goto finish; if (q->answer) { answer = dns_answer_ref(q->answer); for (i = 0; i < answer->n_rrs; i++) { r = dns_question_matches_rr(q->question, answer->rrs[i]); if (r < 0) 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 finish; if (r > 0) cname = dns_resource_record_ref(answer->rrs[i]); continue; } r = append_address(reply, answer->rrs[i]); if (r < 0) goto finish; if (!canonical) canonical = dns_resource_record_ref(answer->rrs[i]); added ++; } } if (added <= 0) { if (!cname) { r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of requested type", q->request_hostname); goto finish; } /* This has a cname? Then update the query with the * new cname. */ r = dns_query_cname_redirect(q, cname->cname.name); if (r < 0) { if (r == -ELOOP) r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname); else r = sd_bus_reply_method_errno(q->request, -r, NULL); goto finish; } /* Before we restart the query, let's see if any of * the RRs we already got already answers our query */ for (i = 0; i < answer->n_rrs; i++) { r = dns_question_matches_rr(q->question, answer->rrs[i]); if (r < 0) goto finish; if (r == 0) continue; r = append_address(reply, answer->rrs[i]); if (r < 0) goto finish; if (!canonical) canonical = dns_resource_record_ref(answer->rrs[i]); added++; } /* If we didn't find anything, then let's restart the * query, this time with the cname */ if (added <= 0) { r = dns_query_go(q); if (r == -ESRCH) { r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); goto finish; } if (r < 0) { r = sd_bus_reply_method_errno(q->request, -r, NULL); goto finish; } return; } } r = sd_bus_message_close_container(reply); if (r < 0) goto finish; /* Return the precise spelling and uppercasing reported by the server */ assert(canonical); r = sd_bus_message_append(reply, "st", DNS_RESOURCE_KEY_NAME(canonical->key), SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); if (r < 0) goto finish; r = sd_bus_send(q->manager->bus, reply, NULL); finish: if (r < 0) { log_error_errno(r, "Failed to send hostname reply: %m"); sd_bus_reply_method_errno(q->request, -r, NULL); } dns_query_free(q); }