/************************************************************************************************** FIND_ALIAS Find an ALIAS or A record for the alias. Returns the RR or NULL if not found. **************************************************************************************************/ MYDNS_RR * find_alias(TASK *t, char *fqdn) { register MYDNS_SOA *soa; register MYDNS_RR *rr; register char *label; char name[DNS_MAXNAMELEN+1]; /* Load the SOA for the alias name. */ memset(name, 0, sizeof(name)); if (!(soa = find_soa(t, fqdn, name))) return (NULL); /* Examine each label in the name, one at a time; look for relevant records */ for (label = name; ; label++) { if (label == name || *label == '.') { if (label[0] == '.' && label[1]) label++; /* Advance past leading dot */ #if DEBUG_ENABLED && DEBUG_ALIAS Debug("%s: label=`%s'", desctask(t), label); #endif /* Do an exact match if the label is the first in the list */ if (label == name) { #if DEBUG_ENABLED && DEBUG_ALIAS Debug("%s: trying exact match `%s'", desctask(t), label); #endif if ((rr = find_rr(t, soa, DNS_QTYPE_A, label))) return (rr); } /* No exact match. If the label isn't empty, replace the first part of the label with `*' and check for wildcard matches. */ if (*label) { uchar wclabel[DNS_MAXNAMELEN+1], *c; /* Generate wildcarded label, i.e. `*.example' or maybe just `*'. */ if (!(c = strchr(label, '.'))) wclabel[0] = '*', wclabel[1] = '\0'; else wclabel[0] = '*', strncpy(wclabel+1, c, sizeof(wclabel)-2); #if DEBUG_ENABLED && DEBUG_ALIAS Debug("%s: trying wildcard `%s'", desctask(t), wclabel); #endif if ((rr = find_rr(t, soa, DNS_QTYPE_A, wclabel))) return (rr); } } if (!*label) break; } return (NULL); }
/************************************************************************************************** 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")); } }
/************************************************************************************************** LOAD_BALANCE Use the 'aux' value to weight multiple A nodes. **************************************************************************************************/ static inline void load_balance(TASK *t, RRLIST *rrlist, datasection_t section, int sort_level) { register RR *node; /* Current node */ register int order = 1; /* Current order */ #if DEBUG_ENABLED && DEBUG_SORT Debug("%s: Load balancing A records in %s section", desctask(t), datasection_str[section]); #endif /* Hosts with 'aux' values > 50000 are always listed last */ for (node = rrlist->head; node; node = node->next) if (RR_IS_ADDR(node) && node->sort_level == sort_level && !node->sort1) if (((MYDNS_RR *)node->rr)->aux >= 50000) node->sort1 = 50000; for (;;) { register int found = 0; /* Number of records with this aux */ uint64_t weights = 0; /* Sum of aux */ register uint32_t rweight = 0; /* Random aux */ /* Compute the sum of the weights for all nodes where 'sort1' == 0 */ for (node = rrlist->head; node; node = node->next) if (RR_IS_ADDR(node) && node->sort_level == sort_level && !node->sort1) { found++; weights += ((MYDNS_RR *)node->rr)->aux; } if (!found) break; /* Set 'sort1' to 'order' for the first node found where the running sum value is greater than or equal to 'rweight' */ rweight = RAND(weights); for (weights = 0, node = rrlist->head; node; node = node->next) if (RR_IS_ADDR(node) && node->sort_level == sort_level && !node->sort1) { weights += ((MYDNS_RR *)node->rr)->aux; if (weights >= rweight) { node->sort1 = 65535 - order++; break; } } } }
/************************************************************************************************** _SORT_SRV_RECS Sorts SRV records at the specified sort level. 1. Sort by priority, lowest to highest. 2. Sort by weight; 0 means "almost never choose me", higher-than-zero yields increased likelihood of being first. **************************************************************************************************/ static inline void _sort_srv_recs(TASK *t, RRLIST *rrlist, datasection_t section, int sort_level) { register RR *node; /* Current node */ register int count; /* Number of SRV nodes on this level */ #if DEBUG_ENABLED && DEBUG_SORT Debug("%s: Sorting SRV records in %s section", desctask(t), datasection_str[section]); #endif /* Assign 'sort1' to the priority (aux) and 'sort2' to 0 if there's a zero weight, else random */ for (count = 0, node = rrlist->head; node; node = node->next) if (RR_IS_SRV(node) && node->sort_level == sort_level) { count++; node->sort1 = ((MYDNS_RR *)node->rr)->aux; if (((MYDNS_RR *)node->rr)->srv_weight == 0) node->sort2 = 0; else node->sort2 = RAND(4294967294U); } if (count < 2) /* Only one node here, don't bother */ return; t->reply_cache_ok = 0; /* Don't cache these replies */ /* Sort a first time, so that the list is ordered by priority/weight */ sort_rrlist(rrlist, sortcmp); /* Reset 'sort2' to zero for each SRV */ for (node = rrlist->head; node; node = node->next) if (RR_IS_SRV(node) && node->sort_level == sort_level) node->sort2 = 0; /* For each unique priority, sort by weight */ for (node = rrlist->head; node; node = node->next) if (RR_IS_SRV(node) && node->sort_level == sort_level && !node->sort2) { register int priority = node->sort1; register int order = 1; while (sort_srv_priority(t, rrlist, section, priority, sort_level, order++)) /* DONOTHING */; } }
/************************************************************************************************** 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); }
/************************************************************************************************** SORT_MX_RECS When there are multiple equal-preference MX records, randomize them to help keep the load equal. **************************************************************************************************/ void sort_mx_recs(TASK *t, RRLIST *rrlist, datasection_t section) { register RR *node; #if DEBUG_ENABLED && DEBUG_SORT Debug("%s: Sorting MX records in %s section", desctask(t), datasection_str[section]); #endif /* Set 'sort' to a random number */ for (node = rrlist->head; node; node = node->next) if (RR_IS_MX(node)) { node->sort1 = ((MYDNS_RR *)node->rr)->aux; node->sort2 = RAND(4294967294U); } return (sort_rrlist(rrlist, sortcmp)); }
/************************************************************************************************** AXFR DNS-based zone transfer. Send all resource records for in QNAME's zone to the client. **************************************************************************************************/ void axfr(TASK *t) { #if DEBUG_ENABLED && DEBUG_AXFR struct timeval start = { 0, 0}, finish = { 0, 0 }; /* Time AXFR began and ended */ #endif MYDNS_SOA *soa = NULL; /* SOA record for zone (may be bogus!) */ /* Do generic startup stuff; this is a child process */ signal(SIGALRM, axfr_timeout); alarm(AXFR_TIME_LIMIT); sql_close(sql); db_connect(); #if DEBUG_ENABLED && DEBUG_AXFR gettimeofday(&start, NULL); DebugX("axfr", 1,_("%s: Starting AXFR for task ID %u"), desctask(t), t->internal_id); #endif total_records = total_octets = 0; t->no_markers = 1; /* Get SOA for zone */ soa = axfr_get_soa(t); if (soa){ /* Transfer that zone */ axfr_zone(t, soa); } #if DEBUG_ENABLED && DEBUG_AXFR /* Report result */ gettimeofday(&finish, NULL); DebugX("axfr", 1,_("AXFR: %u records, %u octets, %.3fs"), (unsigned int)total_records, (unsigned int)total_octets, ((finish.tv_sec + finish.tv_usec / 1000000.0) - (start.tv_sec + start.tv_usec / 1000000.0))); #endif t->qdcount = 1; t->an.size = total_records; task_output_info(t, NULL); sockclose(t->fd); _exit(EXIT_SUCCESS); }
/************************************************************************************************** SORT_SRV_PRIORITY Sorts one record for a single specified priority. After calling this function, 'sort2' should be 'weight' for the node affected. Returns the number of nodes processed (0 means "done with this priority"). **************************************************************************************************/ static inline int sort_srv_priority(TASK *t, RRLIST *rrlist, datasection_t section, uint32_t priority, int sort_level, int order) { register RR *node; /* Current node */ register int found = 0; /* Number of records with this priority */ uint64_t weights = 0; /* Sum of weights */ register uint32_t rweight = 0; /* Random weight */ #if DEBUG_ENABLED && DEBUG_SORT Debug("%s: Sorting SRV records in %s section with priority %u", desctask(t), datasection_str[section], priority); #endif /* Compute the sum of the weights for all nodes with this priority where 'sort2' == 0 */ for (node = rrlist->head; node; node = node->next) if (RR_IS_SRV(node) && node->sort_level == sort_level && node->sort1 == priority && !node->sort2) { found++; weights += ((MYDNS_RR *)node->rr)->srv_weight; } if (!found) return (0); /* Set 'sort2' to 'order' for the first node found at this priority where the running sum value is greater than or equal to 'rweight' */ rweight = RAND(weights+1); for (weights = 0, node = rrlist->head; node; node = node->next) if (RR_IS_SRV(node) && node->sort_level == sort_level && node->sort1 == priority && !node->sort2) { weights += ((MYDNS_RR *)node->rr)->srv_weight; if (weights >= rweight) { node->sort2 = order; return (1); } } return (1); }
/************************************************************************************************** _SORT_A_RECS If the request is for 'A' or 'AAAA' and there are multiple A or AAAA records, sort them. Since this is an A or AAAA record, the answer section contains only addresses. If any of the RR's have nonzero "aux" values, do load balancing, else do round robin. **************************************************************************************************/ static inline void _sort_a_recs(TASK *t, RRLIST *rrlist, datasection_t section, int sort_level) { register RR *node; register int nonzero_aux = 0; register int count = 0; /* Number of nodes at this level */ /* If any addresses have nonzero 'aux' values, do load balancing */ for (count = 0, node = rrlist->head; node; node = node->next) if (RR_IS_ADDR(node) && node->sort_level == sort_level) { count++; if (((MYDNS_RR *)node->rr)->aux) nonzero_aux = 1; } if (count < 2) /* Only one node here, don't bother */ return; t->reply_cache_ok = 0; /* Don't cache load-balanced replies */ if (nonzero_aux) { load_balance(t, rrlist, section, sort_level); } else /* Round robin - for address records, set 'sort' to a random number */ { #if DEBUG_ENABLED && DEBUG_SORT Debug("%s: Sorting A records in %s section (round robin)", desctask(t), datasection_str[section]); #endif for (node = rrlist->head; node; node = node->next) if (RR_IS_ADDR(node) && node->sort_level == sort_level) node->sort1 = RAND(4294967294U); t->reply_cache_ok = 0; /* Don't cache load-balanced replies */ } }
static taskexec_t ixfr_purge_all_soas(TASK *t, void *data) { /* * Retrieve all zone id's that have deleted records. * * For each zone get the expire field and delete any records that have expired. * */ SQL_RES *res = NULL; SQL_ROW row = NULL; size_t querylen; const char *QUERY0 = "SELECT DISTINCT zone FROM %s WHERE active='%s'"; const char *QUERY1 = "SELECT origin FROM %s " "WHERE id=%u;"; const char *QUERY2 = "DELETE FROM %s WHERE zone=%u AND active='%s' " " AND stamp < DATE_SUB(NOW(),INTERVAL %u SECOND);"; char *query = NULL; /* * Reset task timeout clock to some suitable value in the future */ t->timeout = current_time + ixfr_gc_interval; /* Try again e.g. tomorrow */ querylen = sql_build_query(&query, QUERY0, mydns_rr_table_name, mydns_rr_active_types[2]); if (!(res = sql_query(sql, query, querylen))) ErrSQL(sql, "%s: %s", desctask(t), _("error loading zone id's for DELETED records")); RELEASE(query); while((row = sql_getrow(res, NULL))) { unsigned int id = atou(row[0]); char *origin = NULL; MYDNS_SOA *soa = NULL; SQL_RES *sres = NULL; querylen = sql_build_query(&query, QUERY1, mydns_soa_table_name, id); if (!(res = sql_query(sql, query, querylen))) ErrSQL(sql, "%s: %s", desctask(t), _("error loading zone from DELETED record zone id")); RELEASE(query); if (!(row = sql_getrow(res, NULL))) { Warnx(_("%s: no soa found for soa id %u"), desctask(t), id); continue; } origin = row[0]; if (mydns_soa_load(sql, &soa, origin) == 0) { querylen = sql_build_query(&query, QUERY2, mydns_rr_table_name, soa->id, mydns_rr_active_types[2], soa->expire); if (sql_nrquery(sql, query, querylen) != 0) WarnSQL(sql, "%s: %s %s", desctask(t), _("error deleting expired records for zone "), soa->origin); RELEASE(query); sql_free(sres); } } sql_free(res); RELEASE(query); return (TASK_CONTINUE); }
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); }
/************************************************************************************************** WRITE_UDP_REPLY **************************************************************************************************/ taskexec_t write_udp_reply(TASK *t) { int rv = 0; struct sockaddr *addr = NULL; int addrlen = 0; if (t->family == AF_INET) { addr = (struct sockaddr*)&t->addr4; addrlen = sizeof(struct sockaddr_in); #if HAVE_IPV6 } else if (t->family == AF_INET6) { addr = (struct sockaddr*)&t->addr6; addrlen = sizeof(struct sockaddr_in6); #endif } rv = sendto(t->fd, t->reply, t->replylen, 0, addr, addrlen); if (rv < 0) { if ( (errno == EINTR) #ifdef EAGAIN || (errno == EAGAIN) #else #ifdef EWOULDBLOCK || (errno == EWOULDBLOCK) #endif #endif ) { return (TASK_CONTINUE); /* Try again */ } if (errno != EPERM && errno != EINVAL) Warn("%s: %s", desctask(t), _("sendto (UDP)")); return (TASK_FAILED); } if (rv == 0) { /* * Should never happen as this implies the "other" end has closed the socket * and we do not have another end - this is UDP * However, we get this return when a route to the client does not exist * this happens over WAN connection and VPN connections that flap * so try again and see if the connection returns - this is going to * make the process run continuously .... */ return (TASK_EXECUTED); /* Err("%s: Send to (UDP) returned 0", desctask(t)); */ } if (rv != (int)t->replylen) { /* * This should never ever happen as we have sent a partial packet over UDP */ Err(_("%s: Send to (UDP) returned %d when writing %u"), desctask(t), rv, (unsigned int)t->replylen); } #if DEBUG_ENABLED && DEBUG_UDP DebugX("udp", 1, _("%s: WRITE %u UDP octets (id %u)"), desctask(t), (unsigned int)t->replylen, t->id); #endif return (TASK_COMPLETED); }
/************************************************************************************************** ALIAS_RECURSE If the task has a matching ALIAS record, recurse into it. Returns the number of records added. **************************************************************************************************/ int alias_recurse(TASK *t, datasection_t section, char *fqdn, MYDNS_SOA *soa, char *label, MYDNS_RR *alias) { uint32_t aliases[MAX_ALIAS_LEVEL]; char name[DNS_MAXNAMELEN+1]; register MYDNS_RR *rr; register int depth, n; if (LASTCHAR(alias->data) != '.') snprintf(name, sizeof(name), "%s.%s", alias->data, soa->origin); else strncpy(name, alias->data, sizeof(name)-1); for (depth = 0; depth < MAX_ALIAS_LEVEL; depth++) { #if DEBUG_ENABLED && DEBUG_ALIAS Debug("%s: ALIAS -> `%s'", desctask(t), name); #endif /* Are there any alias records? */ if ((rr = find_alias(t, name))) { /* We need an A record that is not an alias to end the chain. */ if (rr->alias == 0) { /* Override the id and name, because rrlist_add() checks for duplicates and we might have several records aliased to one */ rr->id = alias->id; strcpy(rr->name, alias->name); rrlist_add(t, section, DNS_RRTYPE_RR, (void *)rr, fqdn); t->sort_level++; mydns_rr_free(rr); return (1); } /* Append origin if needed */ int len = strlen(rr->data); if (len > 0 && rr->data[len - 1] != '.') { strcat(rr->data, "."); strncat(rr->data, soa->origin, sizeof(rr->name) - len - 1); } /* Check aliases list; if we are looping, stop. Otherwise add this to the list. */ for (n = 0; n < depth; n++) if (aliases[n] == rr->id) { /* ALIAS loop: We aren't going to find an A record, so we're done. */ Verbose("%s: %s: %s (depth %d)", desctask(t), _("ALIAS loop detected"), fqdn, depth); mydns_rr_free(rr); return (0); } aliases[depth] = rr->id; /* Continue search with new alias. */ strncpy(name, rr->data, sizeof(name)-1); mydns_rr_free(rr); } else { Verbose("%s: %s: %s -> %s", desctask(t), _("ALIAS chain is broken"), fqdn, name); return (0); } } Verbose("%s: %s: %s -> %s (depth %d)", desctask(t), _("max ALIAS depth exceeded"), fqdn, alias->data, depth); return (0); }
void axfr_fork(TASK *t) { int pfd[2] = { -1, -1 }; /* Parent/child pipe descriptors */ pid_t pid = -1, parent = -1; #if DEBUG_ENABLED && DEBUG_AXFR DebugX("axfr", 1,_("%s: axfr_fork called on fd %d"), desctask(t), t->fd); #endif if (pipe(pfd)) Err(_("pipe")); parent = getpid(); if ((pid = fork()) < 0) { close(pfd[0]); close(pfd[1]); Warn(_("%s: fork"), clientaddr(t)); return; } if (!pid) { /* Child: reset all signal handlers to default before we dive off elsewhere */ struct sigaction act; memset(&act, 0, sizeof(act)); sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_DFL; sigaction(SIGHUP, &act, NULL); sigaction(SIGUSR1, &act, NULL); sigaction(SIGUSR2, &act, NULL); sigaction(SIGALRM, &act, NULL); sigaction(SIGCHLD, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGQUIT, &act, NULL); sigaction(SIGABRT, &act, NULL); sigaction(SIGTERM, &act, NULL); #if DEBUG_ENABLED && DEBUG_AXFR DebugX("axfr", 1,_("%s: axfr_fork is in the child"), desctask(t)); #endif /* Let parent know I have started */ close(pfd[0]); if (write(pfd[1], "OK", 2) != 2) Warn(_("error writing startup notification")); close(pfd[1]); #if DEBUG_ENABLED && DEBUG_AXFR DebugX("axfr", 1,_("%s: axfr_fork child has told parent I am running"), desctask(t)); #endif /* Clean up parents resources */ free_other_tasks(t, 1); #if DEBUG_ENABLED && DEBUG_AXFR DebugX("axfr", 1,_("%s: AXFR child built"), desctask(t)); #endif /* Do AXFR */ axfr(t); } else { /* Parent */ char buf[5] = "\0\0\0\0\0"; int errct = 0; close(pfd[1]); for (errct = 0; errct < 5; errct++) { if (read(pfd[0], &buf, 4) != 2) Warn(_("%s (%d of 5)"), _("error reading startup notification"), errct+1); else break; } close(pfd[0]); #if DEBUG_ENABLED && DEBUG_AXFR DebugX("axfr", 1,_("AXFR: process started on pid %d for TCP fd %d, task ID %u"), pid, t->fd, t->internal_id); #endif } /* NOTREACHED*/ }