/****************************************************************************
 * Copyright (C) 1998 WIDE Project. All rights reserved.
 * Copyright (C) 1999,2000,2001,2002 University of Tromso. All rights reserved.
 * Copyright (C) 2002 Invenia Innovation AS. All rights reserved.
 *
 * Author: Feike W. Dillema, feico@pasta.cs.uit.no.
 *         based on newbie code by Yusuke DOI, Keio Univ. Murai Lab.
 ****************************************************************************/

/*
 * <$Id: udp_response.c,v 3.57 2002/03/04 12:34:11 dillema Exp $>
 */

#include "totd.h"

/*
 * When a new query is received over a UDP socket from a client, the
 * udp_response state machine is started. See also ev_udp_in_read(),
 * which is the only place from where udp_response_start() is called.
 *
 * udp_response_start() starts a new transaction. First creates a new
 * root context for it, and starts the forwarding of the original query.
 *
 *	forwarding is performed by starting a new child request, which
 *	if all goes well result in some resource records in our context.
 *
 * when the forwarded request finished successfully, udp_response_finish()
 * is responsible for interpreting it and sending a response back to the client.
 *
 */
int udp_response_start (u_char *mesg_buf, int mesg_len, struct sockaddr *sa_p,
			Nia *inif) {
	const char *fn = "udp_response_start()";
	Context *cont;

	syslog (LOG_DEBUG, "%s: start", fn);

	/* create context */
	cont = context_create();
	if (!cont)
		return (response_abort (cont, -1));

	cont->mesg.p = mesg_buf;
	cont->mesg_len = mesg_len;
	cont->wp = mesg_buf + mesg_len;	/* just after the answer section */
	cont->q_id = cont->mesg.hdr->id;
	cont->netifaddr = nia_copy (inif);
	memcpy (cont->peer, sa_p, SOCKADDR_SIZEOF(*sa_p));

	if (cont->mesg.hdr->opcode == OP_QUERY) {
		syslog (LOG_DEBUG, "%s: OPCODE = OP_QUERY", fn);

		/* query-response specific variables */
		cont->process = udp_response_recursive_process;
		cont->retry = udp_response_recursive_retry;

		/* do the forwarding, send request to forwarder */
		switch (request_start (cont, QUERY_TCP)) {
		case 0:
			/* We got a response */
			return (0);
		case 1:
			/* Something wrong with the query */
			cont->mesg.hdr->rcode = RC_FMTERR;
			return (udp_response_finish (cont));
		default:
			/* We failed ourselves somehow */
			cont->mesg.hdr->rcode = RC_SERVERERR;
			return (udp_response_finish (cont));
		}
	} else {
		syslog (LOG_NOTICE, "%s: OPCODE unknown(%d)", fn,
			cont->mesg.hdr->opcode);
		cont->mesg.hdr->rcode = RC_NIMP;
		return udp_response_finish (cont);
	}

	/* NOTREACHED */
	return 0;
}

int udp_response_recursive_process (Context *cont) {
	switch (recursive_process (cont)) {
	case 0:
		/* continue, not ready yet */
		return 0;
	case 1:
		/* finished with success */
		return udp_response_finish (cont);
	default:
		/* we failed ourselves */
		cont->mesg.hdr->rcode = RC_SERVERERR;
		return udp_response_finish (cont);
	}
}

int udp_response_recursive_retry (Context *cont) {
	syslog (LOG_ERR, "udp_response_recursive_retry should not be called.");
	return response_abort (cont, 0);
}

int udp_response_finish (Context *cont) {
        const char *fn = "udp_response_finish()";
	int len;

	syslog (LOG_DEBUG, "%s: start", fn);

	/*
	 * We trim the response down if it doesn't fit in a default UDP
	 * data in a somewhat crude way. Could be smarter, like trim
	 * A records from response for pure IPv6 queries and so. See, if
	 * it is needed often. Maybe better to support EDNS0 option instead.
	 *
	 * RFC1035 says we should truncate the message to 512 bytes and set the
	 * tr bit in the header, but it is really vague on when a response does
	 * not fit. RFC2181 clarifies the use of the tr bit in section 9.
	 *
	 * Note that similar logic is still present in mesg_assemble() in
	 * ne_mesg.c (may disappear if we add the logic to assemble_response()
	 * instead).
	 */
	assemble_response (cont);
	if (cont->mesg_len < 0 || cont->mesg_len > MAX_PACKET) {
		/* put the message on a diet */
		list_destroy (cont->ar_list, rrset_freev);
		cont->ar_list = NULL;
		syslog (LOG_DEBUG, "Overweight, dropping additional section");
		assemble_response (cont);
	}
	if (cont->mesg_len < 0 || cont->mesg_len > MAX_PACKET) {
		/*
		 * ok, water adn bread then then. shuold check
		 * RFCs, it may be better to return error msg
		 * instead of slimmed answer.
		 */
		list_destroy (cont->ns_list, rrset_freev);
		cont->ns_list = NULL;
		syslog (LOG_DEBUG, "Overweight, dropping authority section");
		assemble_response (cont);
	}
	if (cont->mesg_len < 0 || cont->mesg_len > MAX_PACKET) {
		/* ok, nothing viable left, so die */
		list_destroy (cont->an_list, rrset_freev);
		cont->an_list = NULL;
		assemble_response (cont);
		cont->mesg.hdr->tc = 1;
		syslog (LOG_WARNING, "Obese, answers too big for UDP");
	}
	if (cont->mesg_len < 0 || cont->mesg_len > MAX_PACKET) {
		syslog (LOG_ERR, "Even error msg is too big for UDP");
		return (response_abort (cont, 1));
	}

	/* send the answer */
	len = net_mesg_send (cont->netifaddr, cont->mesg.p, cont->mesg_len,
			     cont->peer);

	if (len < cont->mesg_len) {
		syslog (LOG_NOTICE, "failed to send message.");
		return (response_abort (cont, -1));
	}

        context_destroy (cont);
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1