static void
lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event) {
	isc_result_t result;
	isc_boolean_t want_restart;
	isc_boolean_t send_event;
	dns_name_t *name, *fname, *prefix;
	dns_fixedname_t foundname, fixed;
	dns_rdata_t rdata = DNS_RDATA_INIT;
	unsigned int nlabels;
	int order;
	dns_namereln_t namereln;
	dns_rdata_cname_t cname;
	dns_rdata_dname_t dname;

	REQUIRE(VALID_LOOKUP(lookup));

	LOCK(&lookup->lock);

	result = ISC_R_SUCCESS;
	name = dns_fixedname_name(&lookup->name);

	do {
		lookup->restarts++;
		want_restart = ISC_FALSE;
		send_event = ISC_TRUE;

		if (event == NULL && !lookup->canceled) {
			dns_fixedname_init(&foundname);
			fname = dns_fixedname_name(&foundname);
			INSIST(!dns_rdataset_isassociated(&lookup->rdataset));
			INSIST(!dns_rdataset_isassociated
						(&lookup->sigrdataset));
			/*
			 * If we have restarted then clear the old node.				 */
			if  (lookup->event->node != NULL) {
				INSIST(lookup->event->db != NULL);
				dns_db_detachnode(lookup->event->db,
						 &lookup->event->node);
			}
			if (lookup->event->db != NULL)
				dns_db_detach(&lookup->event->db);
			result = view_find(lookup, fname);
			if (result == ISC_R_NOTFOUND) {
				/*
				 * We don't know anything about the name.
				 * Launch a fetch.
				 */
				if  (lookup->event->node != NULL) {
					INSIST(lookup->event->db != NULL);
					dns_db_detachnode(lookup->event->db,
							 &lookup->event->node);
				}
				if (lookup->event->db != NULL)
					dns_db_detach(&lookup->event->db);
				result = start_fetch(lookup);
				if (result == ISC_R_SUCCESS)
					send_event = ISC_FALSE;
				goto done;
			}
		} else if (event != NULL) {
			result = event->result;
			fname = dns_fixedname_name(&event->foundname);
			dns_resolver_destroyfetch(&lookup->fetch);
			INSIST(event->rdataset == &lookup->rdataset);
			INSIST(event->sigrdataset == &lookup->sigrdataset);
		} else
			fname = NULL;	/* Silence compiler warning. */

		/*
		 * If we've been canceled, forget about the result.
		 */
		if (lookup->canceled)
			result = ISC_R_CANCELED;

		switch (result) {
		case ISC_R_SUCCESS:
			result = build_event(lookup);
			if (event == NULL)
				break;
			if (event->db != NULL)
				dns_db_attach(event->db, &lookup->event->db);
			if (event->node != NULL)
				dns_db_attachnode(lookup->event->db,
						  event->node,
						  &lookup->event->node);
			break;
		case DNS_R_CNAME:
			/*
			 * Copy the CNAME's target into the lookup's
			 * query name and start over.
			 */
			result = dns_rdataset_first(&lookup->rdataset);
			if (result != ISC_R_SUCCESS)
				break;
			dns_rdataset_current(&lookup->rdataset, &rdata);
			result = dns_rdata_tostruct(&rdata, &cname, NULL);
			dns_rdata_reset(&rdata);
			if (result != ISC_R_SUCCESS)
				break;
			result = dns_name_copy(&cname.cname, name, NULL);
			dns_rdata_freestruct(&cname);
			if (result == ISC_R_SUCCESS) {
				want_restart = ISC_TRUE;
				send_event = ISC_FALSE;
			}
			break;
		case DNS_R_DNAME:
			namereln = dns_name_fullcompare(name, fname, &order,
							&nlabels);
			INSIST(namereln == dns_namereln_subdomain);
			/*
			 * Get the target name of the DNAME.
			 */
			result = dns_rdataset_first(&lookup->rdataset);
			if (result != ISC_R_SUCCESS)
				break;
			dns_rdataset_current(&lookup->rdataset, &rdata);
			result = dns_rdata_tostruct(&rdata, &dname, NULL);
			dns_rdata_reset(&rdata);
			if (result != ISC_R_SUCCESS)
				break;
			/*
			 * Construct the new query name and start over.
			 */
			dns_fixedname_init(&fixed);
			prefix = dns_fixedname_name(&fixed);
			dns_name_split(name, nlabels, prefix, NULL);
			result = dns_name_concatenate(prefix, &dname.dname,
						      name, NULL);
			dns_rdata_freestruct(&dname);
			if (result == ISC_R_SUCCESS) {
				want_restart = ISC_TRUE;
				send_event = ISC_FALSE;
			}
			break;
		default:
			send_event = ISC_TRUE;
		}

		if (dns_rdataset_isassociated(&lookup->rdataset))
			dns_rdataset_disassociate(&lookup->rdataset);
		if (dns_rdataset_isassociated(&lookup->sigrdataset))
			dns_rdataset_disassociate(&lookup->sigrdataset);

	done:
		if (event != NULL) {
			if (event->node != NULL)
				dns_db_detachnode(event->db, &event->node);
			if (event->db != NULL)
				dns_db_detach(&event->db);
			isc_event_free(ISC_EVENT_PTR(&event));
		}

		/*
		 * Limit the number of restarts.
		 */
		if (want_restart && lookup->restarts == MAX_RESTARTS) {
			want_restart = ISC_FALSE;
			result = ISC_R_QUOTA;
			send_event = ISC_TRUE;
		}

	} while (want_restart);

	if (send_event) {
		lookup->event->result = result;
		lookup->event->ev_sender = lookup;
		isc_task_sendanddetach(&lookup->task,
				       (isc_event_t **)(void *)&lookup->event);
		dns_view_detach(&lookup->view);
	}

	UNLOCK(&lookup->lock);
}
isc_result_t
dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type,
		  dns_view_t *view, unsigned int options, isc_task_t *task,
		  isc_taskaction_t action, void *arg, dns_lookup_t **lookupp)
{
	isc_result_t result;
	dns_lookup_t *lookup;
	isc_event_t *ievent;

	lookup = isc_mem_get(mctx, sizeof(*lookup));
	if (lookup == NULL)
		return (ISC_R_NOMEMORY);
	lookup->mctx = NULL;
	isc_mem_attach(mctx, &lookup->mctx);
	lookup->options = options;

	ievent = isc_event_allocate(mctx, lookup, DNS_EVENT_LOOKUPDONE,
				    action, arg, sizeof(*lookup->event));
	if (ievent == NULL) {
		result = ISC_R_NOMEMORY;
		goto cleanup_lookup;
	}
	lookup->event = (dns_lookupevent_t *)ievent;
	lookup->event->ev_destroy = levent_destroy;
	lookup->event->ev_destroy_arg = mctx;
	lookup->event->result = ISC_R_FAILURE;
	lookup->event->name = NULL;
	lookup->event->rdataset = NULL;
	lookup->event->sigrdataset = NULL;
	lookup->event->db = NULL;
	lookup->event->node = NULL;

	lookup->task = NULL;
	isc_task_attach(task, &lookup->task);

	result = isc_mutex_init(&lookup->lock);
	if (result != ISC_R_SUCCESS)
		goto cleanup_event;

	dns_fixedname_init(&lookup->name);

	result = dns_name_copy(name, dns_fixedname_name(&lookup->name), NULL);
	if (result != ISC_R_SUCCESS)
		goto cleanup_lock;

	lookup->type = type;
	lookup->view = NULL;
	dns_view_attach(view, &lookup->view);
	lookup->fetch = NULL;
	lookup->restarts = 0;
	lookup->canceled = ISC_FALSE;
	dns_rdataset_init(&lookup->rdataset);
	dns_rdataset_init(&lookup->sigrdataset);
	lookup->magic = LOOKUP_MAGIC;

	*lookupp = lookup;

	lookup_find(lookup, NULL);

	return (ISC_R_SUCCESS);

 cleanup_lock:
	DESTROYLOCK(&lookup->lock);

 cleanup_event:
	ievent = (isc_event_t *)lookup->event;
	isc_event_free(&ievent);
	lookup->event = NULL;

	isc_task_detach(&lookup->task);

 cleanup_lookup:
	isc_mem_putanddetach(&mctx, lookup, sizeof(*lookup));

	return (result);
}
Exemplo n.º 3
0
isc_result_t
dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx,
		      dns_tsig_keyring_t *ring)
{
	isc_result_t result = ISC_R_SUCCESS;
	dns_rdata_tkey_t tkeyin, tkeyout;
	isc_boolean_t freetkeyin = ISC_FALSE;
	dns_name_t *qname, *name, *keyname, *signer, tsigner;
	dns_fixedname_t fkeyname;
	dns_rdataset_t *tkeyset;
	dns_rdata_t rdata;
	dns_namelist_t namelist;
	char tkeyoutdata[512];
	isc_buffer_t tkeyoutbuf;

	REQUIRE(msg != NULL);
	REQUIRE(tctx != NULL);
	REQUIRE(ring != NULL);

	ISC_LIST_INIT(namelist);

	/*
	 * Interpret the question section.
	 */
	result = dns_message_firstname(msg, DNS_SECTION_QUESTION);
	if (result != ISC_R_SUCCESS)
		return (DNS_R_FORMERR);

	qname = NULL;
	dns_message_currentname(msg, DNS_SECTION_QUESTION, &qname);

	/*
	 * Look for a TKEY record that matches the question.
	 */
	tkeyset = NULL;
	name = NULL;
	result = dns_message_findname(msg, DNS_SECTION_ADDITIONAL, qname,
				      dns_rdatatype_tkey, 0, &name, &tkeyset);
	if (result != ISC_R_SUCCESS) {
		/*
		 * Try the answer section, since that's where Win2000
		 * puts it.
		 */
		if (dns_message_findname(msg, DNS_SECTION_ANSWER, qname,
					 dns_rdatatype_tkey, 0, &name,
					 &tkeyset) != ISC_R_SUCCESS) {
			result = DNS_R_FORMERR;
			tkey_log("dns_tkey_processquery: couldn't find a TKEY "
				 "matching the question");
			goto failure;
		}
	}
	result = dns_rdataset_first(tkeyset);
	if (result != ISC_R_SUCCESS) {
		result = DNS_R_FORMERR;
		goto failure;
	}
	dns_rdata_init(&rdata);
	dns_rdataset_current(tkeyset, &rdata);

	RETERR(dns_rdata_tostruct(&rdata, &tkeyin, NULL));
	freetkeyin = ISC_TRUE;

	if (tkeyin.error != dns_rcode_noerror) {
		result = DNS_R_FORMERR;
		goto failure;
	}

	/*
	 * Before we go any farther, verify that the message was signed.
	 * GSSAPI TKEY doesn't require a signature, the rest do.
	 */
	dns_name_init(&tsigner, NULL);
	result = dns_message_signer(msg, &tsigner);
	if (result != ISC_R_SUCCESS) {
		if (tkeyin.mode == DNS_TKEYMODE_GSSAPI &&
		    result == ISC_R_NOTFOUND)
		       signer = NULL;
		else {
			tkey_log("dns_tkey_processquery: query was not "
				 "properly signed - rejecting");
			result = DNS_R_FORMERR;
			goto failure;
		}
	} else
		signer = &tsigner;

	tkeyout.common.rdclass = tkeyin.common.rdclass;
	tkeyout.common.rdtype = tkeyin.common.rdtype;
	ISC_LINK_INIT(&tkeyout.common, link);
	tkeyout.mctx = msg->mctx;

	dns_name_init(&tkeyout.algorithm, NULL);
	dns_name_clone(&tkeyin.algorithm, &tkeyout.algorithm);

	tkeyout.inception = tkeyout.expire = 0;
	tkeyout.mode = tkeyin.mode;
	tkeyout.error = 0;
	tkeyout.keylen = tkeyout.otherlen = 0;
	tkeyout.key = tkeyout.other = NULL;

	/*
	 * A delete operation must have a fully specified key name.  If this
	 * is not a delete, we do the following:
	 * if (qname != ".")
	 *	keyname = qname + defaultdomain
	 * else
	 *	keyname = <random hex> + defaultdomain
	 */
	if (tkeyin.mode != DNS_TKEYMODE_DELETE) {
		dns_tsigkey_t *tsigkey = NULL;

		if (tctx->domain == NULL && tkeyin.mode != DNS_TKEYMODE_GSSAPI) {
			tkey_log("dns_tkey_processquery: tkey-domain not set");
			result = DNS_R_REFUSED;
			goto failure;
		}

		dns_fixedname_init(&fkeyname);
		keyname = dns_fixedname_name(&fkeyname);

		if (!dns_name_equal(qname, dns_rootname)) {
			unsigned int n = dns_name_countlabels(qname);
			RUNTIME_CHECK(dns_name_copy(qname, keyname, NULL)
				      == ISC_R_SUCCESS);
			dns_name_getlabelsequence(keyname, 0, n - 1, keyname);
		} else {
			static char hexdigits[16] = {
				'0', '1', '2', '3', '4', '5', '6', '7',
				'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
			unsigned char randomdata[16];
			char randomtext[32];
			isc_buffer_t b;
			unsigned int i, j;

			result = isc_entropy_getdata(tctx->ectx,
						     randomdata,
						     sizeof(randomdata),
						     NULL, 0);
			if (result != ISC_R_SUCCESS)
				goto failure;

			for (i = 0, j = 0; i < sizeof(randomdata); i++) {
				unsigned char val = randomdata[i];
				randomtext[j++] = hexdigits[val >> 4];
				randomtext[j++] = hexdigits[val & 0xF];
			}
			isc_buffer_init(&b, randomtext, sizeof(randomtext));
			isc_buffer_add(&b, sizeof(randomtext));
			result = dns_name_fromtext(keyname, &b, NULL, 0, NULL);
			if (result != ISC_R_SUCCESS)
				goto failure;
		}

		if (tkeyin.mode == DNS_TKEYMODE_GSSAPI) {
			/* Yup.  This is a hack */
			result = dns_name_concatenate(keyname, dns_rootname,
						      keyname, NULL);
			if (result != ISC_R_SUCCESS)
				goto failure;
		} else {
			result = dns_name_concatenate(keyname, tctx->domain,
						      keyname, NULL);
			if (result != ISC_R_SUCCESS)
				goto failure;
		}

		result = dns_tsigkey_find(&tsigkey, keyname, NULL, ring);

		if (result == ISC_R_SUCCESS) {
			tkeyout.error = dns_tsigerror_badname;
			dns_tsigkey_detach(&tsigkey);
			goto failure_with_tkey;
		} else if (result != ISC_R_NOTFOUND)
			goto failure;
	} else