static void free_iq(IQ *q) { free_iarr_data(&q->IR); RELEASE(IQ_NAME(q)); RELEASE(q); }
taskexec_t ixfr(TASK * t, datasection_t section, dns_qtype_t qtype, char *fqdn, int truncateonly) { MYDNS_SOA *soa = NULL; uchar *query = (uchar*)t->query; int querylen = t->len; uchar *src = query + DNS_HEADERSIZE; IQ *q = NULL; task_error_t errcode = 0; #if DEBUG_ENABLED && DEBUG_IXFR DebugX("ixfr", 1, "%s: ixfr(%s, %s, \"%s\", %d)", desctask(t), resolve_datasection_str[section], mydns_qtype_str(qtype), fqdn, truncateonly); #endif if (!dns_ixfr_enabled) { dnserror(t, DNS_RCODE_REFUSED, ERR_IXFR_NOT_ENABLED); return (TASK_FAILED); } /* * Authority section contains the SOA record for the client's version of the zone * only trust the serial number. */ if (mydns_soa_load(sql, &soa, fqdn) < 0) { dnserror(t, DNS_RCODE_SERVFAIL, ERR_DB_ERROR); return (TASK_FAILED); } if (!soa) { dnserror(t, DNS_RCODE_REFUSED, ERR_ZONE_NOT_FOUND); return (TASK_FAILED); } #if DEBUG_ENABLED && DEBUG_IXFR DebugX("ixfr", 1, _("%s: DNS IXFR: SOA id %u"), desctask(t), soa->id); DebugX("ixfr", 1, _("%s: DNS IXFR: QDCOUNT=%d (Query)"), desctask(t), t->qdcount); DebugX("ixfr", 1, _("%s: DNS IXFR: ANCOUNT=%d (Answer)"), desctask(t), t->ancount); DebugX("ixfr", 1, _("%s: DNS IXFR: AUCOUNT=%d (Authority)"), desctask(t), t->nscount); DebugX("ixfr", 1, _("%s: DNS IXFR: ADCOUNT=%d (Additional data)"), desctask(t), t->arcount); #endif if (!t->nscount) return formerr(t, DNS_RCODE_FORMERR, ERR_NO_AUTHORITY, _("ixfr query contains no authority data")); if (t->nscount != 1) return formerr(t, DNS_RCODE_FORMERR, ERR_MULTI_AUTHORITY, _("ixfr query contains multiple authority records")); if (!t->qdcount) return formerr(t, DNS_RCODE_FORMERR, ERR_NO_QUESTION, _("ixfr query does not contain question")); if (t->qdcount != 1) return formerr(t, DNS_RCODE_FORMERR, ERR_MULTI_QUESTIONS, _("ixfr query contains multiple questions")); if (t->ancount || t->arcount) return formerr(t, DNS_RCODE_FORMERR, ERR_MALFORMED_REQUEST, _("ixfr query has answer or additional data")); q = allocate_iq(); if (!(IQ_NAME(q) = name_unencode2(query, querylen, &src, &errcode))) { free_iq(q); return formerr(t, DNS_RCODE_FORMERR, errcode, NULL); } DNS_GET16(q->type, src); DNS_GET16(q->class, src); if (!(src = ixfr_gobble_authority_rr(t, query, querylen, src, &q->IR))) { free_iq(q); return (TASK_FAILED); } /* Get the serial number from the RR record in the authority section */ #if DEBUG_ENABLED && DEBUG_IXFR DebugX("ixfr", 1, _("%s: DNS IXFR Question[zone %s qclass %s qtype %s]" " Authority[zone %s qclass %s qtype %s ttl %u " "mname %s rname %s serial %u refresh %u retry %u expire %u minimum %u]"), desctask(t), q->name, mydns_class_str(q->class), mydns_qtype_str(q->type), q->IR.name, mydns_class_str(q->IR.class), mydns_qtype_str(q->IR.type), q->IR.ttl, q->IR.mname, q->IR.rname, q->IR.serial, q->IR.refresh, q->IR.retry, q->IR.expire, q->IR.minimum); #endif /* * As per RFC 1995 we have 3 options for a response if a delta exists. * * We can send a full zone transfer if it will fit in a UDP packet and is smaller * than sending deltas * * We can send a delta transfer if it will fit into a single UDP packet and we can calculate * one for the difference between the client and the current serial * * We can send a packet with a single SOA record for the latest SOA. This will force the client * to initiate an AXFR. * * We can calculate the size of the response by either building both messages * or by an estimation technique. In either case we need to look at the data. * * I have chosen to check for altered records within the database first. * * First check is to make sure that the serial held by the client is not the current one * * Next check to see if out incremental data for the transition from client serial * to current serial has not expired. * * Then retrieve the updated records between the client serial and the latest serial. * and retrieve the entire zone ... a record count is the first check. * * If the number of delta records is larger than the number of zone records then send the zone * * Calculate the size of the variable parts of the record and compare. * We assume that name encoding will have an equal effect on the data. * So having chosen to send either the zone or the deltas construct the packet. * * Check that the packet has not overflowed the UDP limit and send. If it has * that abandon the packet and send one containing just the latest SOA. * */ if (soa->serial == q->IR.serial) { /* Tell the client to do no zone transfer */ rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; } else { /* Do we have incremental information in the database */ if (!truncateonly && mydns_rr_use_active && mydns_rr_use_stamp && mydns_rr_use_serial) { /* We can do incrementals */ /* Need to send an IXFR if available */ /* * Work out when the client SOA came into being */ MYDNS_RR *ThisRR = NULL, *rr = NULL; char *deltafilter = NULL; int deletecount, activecount, zonesize; size_t deltasize, fullsize; /* For very large zones we do not want to load all of the records just to give up */ sql_build_query(&deltafilter, "serial > %u", q->IR.serial); /* * Compare counts of changes from full zone data * ... assumes records are about the same size * approximate zone size by 2 * deleted count === actual number of delta records */ deletecount = mydns_rr_count_deleted_filtered(sql, soa->id, DNS_QTYPE_ANY, NULL, soa->origin, deltafilter); activecount = mydns_rr_count_active_filtered(sql, soa->id, DNS_QTYPE_ANY, NULL, soa->origin, deltafilter); zonesize = mydns_rr_count_active(sql, soa->id, DNS_QTYPE_ANY, NULL, soa->origin); deltasize = deletecount + activecount + 4; fullsize = zonesize + 2; if ((deletecount < 0) || (activecount < 0) || (zonesize < 0)) { RELEASE(deltafilter); dnserror(t, DNS_RCODE_SERVFAIL, ERR_DB_ERROR); return (TASK_FAILED); } if (deletecount || activecount) { if (deltasize >= fullsize) { /* Send a full zone transfer */ /* Current Serial first */ rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; if (mydns_rr_load_active(sql, &ThisRR, soa->id, DNS_QTYPE_ANY, NULL, soa->origin) == 0) { for (rr = ThisRR; rr; rr = rr->next) { char *name = mydns_rr_append_origin(MYDNS_RR_NAME(rr), soa->origin); rrlist_add(t, ANSWER, DNS_RRTYPE_RR, (void *)rr, name); if (name != MYDNS_RR_NAME(rr)) RELEASE(name); } t->sort_level++; mydns_rr_free(ThisRR); rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; } } else { int latest_serial = soa->serial; rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; soa->serial = q->IR.serial; rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; soa->serial = latest_serial; if (mydns_rr_load_deleted_filtered(sql, &ThisRR, soa->id, DNS_QTYPE_ANY, NULL, soa->origin, deltafilter) == 0) { for (rr = ThisRR; rr; rr = rr->next) { char *name = mydns_rr_append_origin(MYDNS_RR_NAME(rr), soa->origin); rrlist_add(t, ANSWER, DNS_RRTYPE_RR, (void *)rr, name); if (name != MYDNS_RR_NAME(rr)) RELEASE(name); } t->sort_level++; mydns_rr_free(ThisRR); } rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; if (mydns_rr_load_active_filtered(sql, &ThisRR, soa->id, DNS_QTYPE_ANY, NULL, soa->origin, deltafilter) == 0) { for (rr = ThisRR; rr; rr = rr->next) { char *name = mydns_rr_append_origin(MYDNS_RR_NAME(rr), soa->origin); rrlist_add(t, ANSWER, DNS_RRTYPE_RR, (void *)rr, name); if (name != MYDNS_RR_NAME(rr)) RELEASE(name); } t->sort_level++; mydns_rr_free(ThisRR); rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; } RELEASE(deltafilter); } goto FINISHEDIXFR; } } } /* Tell the client to do a full zone transfer or not at all */ rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; FINISHEDIXFR: mydns_soa_free(soa); free_iq(q); t->hdr.aa = 1; return (TASK_EXECUTED); }
void iq_schedule (buffer_desc_t ** bufs, int n) { int inx; int is_reads = 0; buf_sort (bufs, n, (sort_key_func_t) bd_phys_page_key); for (inx = 0; inx < n; inx++) { if (bufs[inx]->bd_iq) GPF_T1 ("buffer added to iq already has a bd_iq"); bufs[inx]->bd_iq = db_io_queue (bufs[inx]->bd_storage, bufs[inx]->bd_physical_page); } DO_SET (io_queue_t *, iq, &mti_io_queues) { int n_added = 0; buffer_desc_t * ipoint; int was_empty; IN_IOQ (iq); inx = 0; ipoint = iq->iq_first; was_empty = (iq->iq_first == NULL); while (inx < n) { buffer_desc_t * buf = bufs[inx]; if (!buf || buf->bd_iq != iq) { inx++; continue; } is_reads = buf->bd_being_read; if (buf->bd_iq_next || buf->bd_iq_prev) GPF_T1 ("can't schedule same buffer twice"); bufs[inx] = NULL; next_ipoint: if (!ipoint) { L2_PUSH_LAST (iq->iq_first, iq->iq_last, buf, bd_iq_); n_added++; inx++; } else if (BUF_SORT_DP (ipoint) < BUF_SORT_DP (buf)) { ipoint = ipoint->bd_iq_next; goto next_ipoint; } else if (BUF_SORT_DP (ipoint) == BUF_SORT_DP (buf)) GPF_T1 ("the same buffer can't be scheduled twice for io"); else { L2_INSERT (iq->iq_first, iq->iq_last, ipoint, buf, bd_iq_); n_added++; inx++; } if (!buf->bd_being_read) { page_leave_outside_map (buf); } } LEAVE_IOQ (iq); if (n_added && !is_reads) { dbg_printf (("IQ %s %d %s added, %s.\n", IQ_NAME (iq), n_added, is_reads ? "reads" : "writes", was_empty ? "starting" : "running")); } if (n_added && was_empty) semaphore_leave (iq->iq_sem); } END_DO_SET (); if (n) { if (is_reads) mti_reads_queued += n; else mti_writes_queued += n; } }