static void a4cb(struct dns_ctx *ctx, struct dns_rr_a4 *r, void *data) { struct rblookup *ipl = data; if (r) { ipl->addr = r; ++listed; if (do_txt) { if (dns_submit_a4dnsbl_txt(0, &ipl->key, ipl->zone, txtcb, ipl)) return; dnserror(ipl, "submit DNSBL TXT record"); } ++ipl->parent->listed; } else if (dns_status(ctx) != DNS_E_NXDOMAIN) dnserror(ipl, "lookup DNSBL A record"); else ipl->addr = notlisted; }
static void txtcb(struct dns_ctx *ctx, struct dns_rr_txt *r, void *data) { struct rblookup *ipl = data; if (r) { ipl->txt = r; ++ipl->parent->listed; } else if (dns_status(ctx) != DNS_E_NXDOMAIN) dnserror(ipl, "lookup DNSBL TXT record"); }
static void sockerrmsg(const char *m,int dns) { char buf[2048]; const char *se; size_t i; i=strlen(m); if (i>sizeof(buf)-3) return; strncpy(buf,m,i); buf[i]='\0'; strcat(buf,": "); if ((se=dns ? dnserror() : sockerror())) if (strlen(se)+i<sizeof(buf)) strcat(buf,se); clwarnx(buf); }
/************************************************************************************************** CHECK_XFER If the "xfer" column exists in the soa table, it should contain a list of wildcards separated by commas. In order for this zone transfer to continue, one of the wildcards must match the client's IP address. **************************************************************************************************/ static void check_xfer(TASK *t, MYDNS_SOA *soa) { SQL_RES *res = NULL; SQL_ROW row = NULL; char ip[256]; char *query = NULL; size_t querylen = 0; int ok = 0; memset(&ip, 0, sizeof(ip)); if (!mydns_soa_use_xfer) return; strncpy(ip, clientaddr(t), sizeof(ip)-1); querylen = sql_build_query(&query, "SELECT xfer FROM %s WHERE id=%u%s%s%s;", mydns_soa_table_name, soa->id, (mydns_rr_use_active)? " AND active='" : "", (mydns_rr_use_active)? mydns_rr_active_types[0] : "", (mydns_rr_use_active)? "'" : ""); res = sql_query(sql, query, querylen); RELEASE(query); if (!res) { ErrSQL(sql, "%s: %s", desctask(t), _("error loading zone transfer access rules")); } if ((row = sql_getrow(res, NULL))) { char *wild = NULL, *r = NULL; for (r = row[0]; !ok && (wild = strsep(&r, ",")); ) { if (strchr(wild, '/')) { if (t->family == AF_INET) ok = in_cidr(wild, t->addr4.sin_addr); } else if (wildcard_match(wild, ip)) ok = 1; } } sql_free(res); if (!ok) { dnserror(t, DNS_RCODE_REFUSED, ERR_NO_AXFR); axfr_reply(t); axfr_error(t, _("access denied")); } }
/************************************************************************************************** AXFR_GET_SOA Attempt to find a SOA record. If SOA id is 0, we made it up. **************************************************************************************************/ static MYDNS_SOA * axfr_get_soa(TASK *t) { MYDNS_SOA *soa = NULL; /* Try to load SOA */ if (mydns_soa_load(sql, &soa, t->qname) < 0) ErrSQL(sql, "%s: %s", desctask(t), _("error loading zone")); if (soa) { return (soa); } /* STILL no SOA? We aren't authoritative */ dnserror(t, DNS_RCODE_REFUSED, ERR_ZONE_NOT_FOUND); axfr_reply(t); axfr_error(t, _("unknown zone")); /* NOTREACHED */ return (NULL); }
static int submit_a_queries(struct ipcheck *ipc, int naddr, const struct in_addr *addr) { int z, a; struct rblookup *rl = ecalloc(sizeof(*rl), nzones * naddr); ipc->lookup = rl; ipc->naddr = naddr; for(a = 0; a < naddr; ++a) { for(z = 0; z < nzones; ++z) { rl->key = addr[a]; rl->zone = zones[z]; rl->parent = ipc; if (!dns_submit_a4dnsbl(0, &rl->key, rl->zone, a4cb, rl)) dnserror(rl, "submit DNSBL A query"); ++rl; } } return 0; }
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); }