static void test_packet_from_file(const char* filename, bool canonical) { _cleanup_free_ char *data = NULL; size_t data_size, packet_size, offset; assert_se(read_full_file(filename, &data, &data_size) >= 0); assert_se(data); assert_se(data_size > 8); log_info("============== %s %s==============", filename, canonical ? "canonical " : ""); for (offset = 0; offset < data_size; offset += 8 + packet_size) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL, *p2 = NULL; _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL, *rr2 = NULL; const char *s, *s2; uint64_t hash1, hash2; packet_size = le64toh( *(uint64_t*)(data + offset) ); assert_se(packet_size > 0); assert_se(offset + 8 + packet_size <= data_size); assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0) >= 0); assert_se(dns_packet_append_blob(p, data + offset + 8, packet_size, NULL) >= 0); assert_se(dns_packet_read_rr(p, &rr, NULL, NULL) >= 0); s = dns_resource_record_to_string(rr); assert_se(s); puts(s); hash1 = hash(rr); assert_se(dns_resource_record_to_wire_format(rr, canonical) >= 0); assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, 0) >= 0); assert_se(dns_packet_append_blob(p2, rr->wire_format, rr->wire_format_size, NULL) >= 0); assert_se(dns_packet_read_rr(p2, &rr2, NULL, NULL) >= 0); s2 = dns_resource_record_to_string(rr); assert_se(s2); assert_se(streq(s, s2)); hash2 = hash(rr); assert_se(hash1 == hash2); } }
static void test_dns_packet_new(void) { size_t i; _cleanup_(dns_packet_unrefp) DnsPacket *p2 = NULL; for (i = 0; i <= DNS_PACKET_SIZE_MAX; i++) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, i, DNS_PACKET_SIZE_MAX) == 0); log_debug("dns_packet_new: %zu → %zu", i, p->allocated); assert_se(p->allocated >= MIN(DNS_PACKET_SIZE_MAX, i)); if (i > DNS_PACKET_SIZE_START + 10 && i < DNS_PACKET_SIZE_MAX - 10) i = MIN(i * 2, DNS_PACKET_SIZE_MAX - 10); } assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, DNS_PACKET_SIZE_MAX + 1, DNS_PACKET_SIZE_MAX) == -EFBIG); }
static int dns_stub_make_reply_packet( DnsPacket **p, size_t max_size, DnsQuestion *q, DnsAnswer *answer, bool *ret_truncated) { bool truncated = false; DnsResourceRecord *rr; unsigned c = 0; int r; assert(p); /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence * roundtrips aren't expensive. */ if (!*p) { r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0, max_size); if (r < 0) return r; r = dns_packet_append_question(*p, q); if (r < 0) return r; DNS_PACKET_HEADER(*p)->qdcount = htobe16(dns_question_size(q)); } DNS_ANSWER_FOREACH(rr, answer) { r = dns_question_matches_rr(q, rr, NULL); if (r < 0) return r; if (r > 0) goto add; r = dns_question_matches_cname_or_dname(q, rr, NULL); if (r < 0) return r; if (r > 0) goto add; continue; add: r = dns_packet_append_rr(*p, rr, 0, NULL, NULL); if (r == -EMSGSIZE) { truncated = true; break; } if (r < 0) return r; c++; }
static int dns_stub_make_reply_packet( uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *answer, bool add_opt, /* add an OPT RR to this packet */ bool edns0_do, /* set the EDNS0 DNSSEC OK bit */ bool ad, /* set the DNSSEC authenticated data bit */ DnsPacket **ret) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; DnsResourceRecord *rr; unsigned c = 0; int r; /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence * roundtrips aren't expensive. */ r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); if (r < 0) return r; /* If the client didn't do EDNS, clamp the rcode to 4 bit */ if (!add_opt && rcode > 0xF) rcode = DNS_RCODE_SERVFAIL; DNS_PACKET_HEADER(p)->id = id; DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( 1 /* qr */, 0 /* opcode */, 0 /* aa */, 0 /* tc */, 1 /* rd */, 1 /* ra */, ad /* ad */, 0 /* cd */, rcode)); r = dns_packet_append_question(p, q); if (r < 0) return r; DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q)); DNS_ANSWER_FOREACH(rr, answer) { r = dns_question_matches_rr(q, rr, NULL); if (r < 0) return r; if (r > 0) goto add; r = dns_question_matches_cname_or_dname(q, rr, NULL); if (r < 0) return r; if (r > 0) goto add; continue; add: r = dns_packet_append_rr(p, rr, NULL, NULL); if (r < 0) return r; c++; }
int main(int argc, char **argv) { char *main_config_file = argv[1] != NULL ? argv[1] : "config.pdns"; g_message("picodns starting..."); int read_config_result = 0; if((read_config_result = read_config(main_config_file)) != 0) { g_error("Could not read the main configuration file %s", main_config_file); } else { g_message("Read main configuration file %s", main_config_file); } g_message("loading LUT from file %s...", main_records_file); //load the lookup table dns_lut lut = dns_lut_new(main_records_file); g_message("LUT loaded (%d entries)", g_hash_table_size(lut.table)); int sockfd; struct addrinfo hints, *servinfo, *p; int rv; int numbytes; struct sockaddr_storage their_addr; socklen_t addr_len; char s[INET6_ADDRSTRLEN]; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; // use my IP char port[7] = {0}; sprintf(port, "%d", udp_port); if ((rv = getaddrinfo(NULL, port, &hints, &servinfo)) == -1) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return 1; } // loop through all the results and bind to the first we can for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("listener: socket"); continue; } if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); perror("listener: bind"); continue; } break; } if (p == NULL) { g_error("could not bind to socket"); } g_message("picodns loaded, waiting for requests"); addr_len = sizeof their_addr; while(1) { //create the gbytearray for storage of incoming udp packet GByteArray *packet_data = g_byte_array_sized_new(max_incoming_udp_packet_size); if ((numbytes = recvfrom(sockfd, packet_data->data, max_incoming_udp_packet_size, 0, (struct sockaddr *)&their_addr, &addr_len)) == -1) { g_warning("Error receiving data: "); perror("recvfrom"); continue; } g_byte_array_set_size(packet_data, numbytes); //set s to contain string rep of src address inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s); g_debug("dns request from %s", s); if(localhost_only == TRUE) { if((strcmp(s, "127.0.0.1") != 0) && (strcmp(s, "::1") != 0) && (strcmp(s, "::ffff:127.0.0.1") != 0)) { //this is not from localhost, and only localhost is allowed, // so ignore it continue; } } //now we parse apart he packet into its respective parts. dns_packet packet = dns_packet_parse(packet_data); //dns_packet_print(&packet); //extract the target hostname from the packet if(packet.QuestionRRCount > 1) { g_warning("got a dns packet with more than one question in it, ignoring"); continue; } else if (packet.QuestionRRCount == 0) { g_warning("got a dns packet without a question, ignoring"); continue; } dns_rr question = g_array_index(packet.QuestionRRs, dns_rr, 0); gchar *target_host = g_strconcat(dns_name_to_ascii(question.name), ":", dns_type_to_ascii(question.Type), NULL); g_message("got a query for %s", target_host); //now, we resolve the packet into a responce packet dns_resolver_record *resolved = dns_lut_lookup(lut, target_host); //make a new dns_packet with the question as well as the answer dns_packet reply = dns_packet_new(); reply.TransactionID = packet.TransactionID; //flags start as a mirror of the recv'd packets flags reply.flags = packet.flags; reply.flags.Responce = DNS_RESPONCE; reply.flags.Authoritative = 0; reply.flags.Truncated = 0; reply.flags.RecursionAvailable = 0; reply.flags.AnswerAuthenticated = 0; reply.flags.ReplyCode = 0; //add the query reply.QuestionRRCount = 1; dns_rr question_rr = g_array_index(packet.QuestionRRs, dns_rr, 0); g_array_append_val(reply.QuestionRRs, question_rr); if(resolved == NULL) { g_message("DNS LUT has no entry for %s", target_host); } else { //if NonAuthOK is 0, then Authentication is required - in this //case, we can only reply if the resolver_record states that //request_authenticated is set. If it isnt, then the record isnt //considered authenticated, and isnt an appropriate responce. if((resolved->request_authenticated && !packet.flags.NonAuthOK) || (packet.flags.NonAuthOK) || (!packet.flags.NonAuthOK && resolved->ignore_non_auth_ok)) { //set the requested flags reply.flags.Authoritative = (resolved->request_authoratative)?1:0; reply.flags.AnswerAuthenticated = (resolved->request_authenticated)?1:0; //now, insert each one of the answers into the reply packet reply.AnswerRRCount = resolved->Answers->len; g_array_append_vals(reply.AnswerRRs, resolved->Answers->data, resolved->Answers->len); g_message("DNS LUT has %d answer(s) matching %s", resolved->Answers->len, target_host); } else { g_message("DNS LUT has no appropriate entry for %s", target_host); } } //now, pack the responce packet into a GByteArray GByteArray *reply_data = dns_packet_pack(&reply); //now, we transmit the responce packet if ((numbytes = sendto(sockfd, reply_data->data, reply_data->len, 0, (struct sockaddr *)&their_addr, addr_len)) == -1) { perror("talker: sendto"); exit(1); } //now we clean up! g_byte_array_free(packet_data, TRUE); } /* end main server loop */ freeaddrinfo(servinfo); //close the DNS socket file descriptor close(sockfd); free_config(); return 0; }
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_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', "(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 ++; } }