Beispiel #1
0
/**************************************************************************************************
	AXFR_ZONE
	DNS-based zone transfer.
**************************************************************************************************/
static void
axfr_zone(TASK *t, MYDNS_SOA *soa) {

  /* Check optional "xfer" column and initialize reply */
  check_xfer(t, soa);
  reply_init(t);

  /* Send opening SOA record */
  rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin);
  axfr_reply(t);

  /*
  **  Get all resource records for zone (if zone ID is nonzero, i.e. not manufactured)
  **  and transmit each resource record.
  */
  if (soa->id) {
    MYDNS_RR *ThisRR = NULL, *rr = NULL;

    if (mydns_rr_load_active(sql, &ThisRR, soa->id, DNS_QTYPE_ANY, NULL, soa->origin) == 0) {
      for (rr = ThisRR; rr; rr = rr->next) {
	/* If 'name' doesn't end with a dot, append the origin */
	if (!*MYDNS_RR_NAME(rr) || LASTCHAR(MYDNS_RR_NAME(rr)) != '.') {
	  mydns_rr_name_append_origin(rr, soa->origin);
	}

#if ALIAS_ENABLED
	/*
	 * If we have been compiled with alias support
	 * and the current record is an alias pass it to alias_recurse()
	 */
	if (rr->alias != 0)
	  alias_recurse(t, ANSWER, MYDNS_RR_NAME(rr), soa, NULL, rr);
	else
#endif
	  rrlist_add(t, ANSWER, DNS_RRTYPE_RR, (void *)rr, MYDNS_RR_NAME(rr));
	/* Transmit this resource record */
	axfr_reply(t);
      }
      mydns_rr_free(ThisRR);
    }
  }

  /* Send closing SOA record */
  rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin);
  axfr_reply(t);

  mydns_soa_free(soa);
}
Beispiel #2
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);
}
Beispiel #3
0
/**************************************************************************************************
	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);
}