/****************************************************************************
 * 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: request.c,v 3.99 2005/07/04 09:09:22 dillema Exp $>
 */

#include "totd.h"

#ifdef STF
static void collect_ns_socks (Context *);
#endif
static void cname_without_crecord (G_List *, u_char *);

/*
 * This routine is called after a request has been received by the proxy,
 * from {udp|tcp}_response_start(). There a context was created for us, and
 * the request message received has been copied into it.
 *
 * Here we then parse the request received, and we do the forwarding of it
 * or of a slightly modifed question (depending on how the proxy is
 * configured). The actual forwarded request becomes a child context of
 * the original request we recevied.
 */
int request_start (Context *cont, int tcp) {
	const char *fn = "request_start()";
	uint16_t qclass, qtype;
	u_char *cp, qname[MAX_DNAME], str[MAX_DNAME];
	struct in6_addr a6;
	int len;

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

	/*
	 * get q-stuff from original message we received
	 */
#define QNAME (cont->mesg.p + sizeof (Mesg_Hdr))
	cp = mesg_skip_dname (QNAME, cont->mesg.p + cont->mesg_len);
	if (!cp) {
		syslog(LOG_INFO, "%s: malformed question", fn);
		return (1);
	}

	len = cp - QNAME;
	if (len >= MAX_DNAME) {
		syslog (LOG_INFO, "%s: malformed question", fn);
		return (1);
	}

	GETSHORT (qtype, cp);
	cont->q_type = qtype;

	GETSHORT (qclass, cp);
	cont->q_class = qclass;

	memcpy (cont->qname, QNAME, len);
	memcpy (qname, QNAME, len);
	/* be sure to always keep it NULL terminated */
	qname[len] = '\0';

	dname_decompress (str, MAX_DNAME, qname, NULL, NULL, NULL);
	syslog (LOG_INFO, "Query name: %s type: %s", str, string_rtype(qtype));

        if (*qname == EDNS0_ELT_BITLABEL) {
		/* We will and may not use qname as string further */
		qname[0] = '\0';
	} else if (len != strlen(qname) + 1) {
		/* We will and may not use qname as string further */
		qname[0] = '\0';
	}

	/*
	 * Now we extracted all we need from the original
	 * message in order to decide what we need to do as proxy.
	 */

	cont->work_state = FORWARD_REQUEST;

	/*
	 * If the original request is a reverse name lookup for
	 * an address that was produced by us, i.e. a `tricked' fake address,
	 * or a scoped address that we need to convert to its
	 * global counterpart.
	 */
	if (qtype == RT_PTR && conv_trick_is_tot_newptr (QNAME, &a6)) {
		cont->work_state = NEWPTR_TRICK_REQUEST;
		conv_trick_newptr_rq(qname, &a6);
		if (T.debug) {
			dname_decompress (str, MAX_DNAME, qname, 0, 0, 0);
			syslog (LOG_DEBUG, "%s: converted new ptr name: %s",
				fn, str);
		}
	}

	if (qtype == RT_PTR && conv_trick_is_tot_ptr (qname)) {
		cont->work_state = PTR_TRICK_REQUEST;
		conv_trick_ptr_rq(qname);
		if (T.debug) {
			dname_decompress (str, MAX_DNAME, qname, 0, 0, 0);
			syslog (LOG_DEBUG, "%s: converted ptr name: %s",
				fn, str);
		}
	}

#ifdef SCOPED_REWRITE
	if (T.scoped_prefixes && conv_is_scoped_ptr (qname, 1) != -1) {
		cont->work_state = PTR_SCOPED_TRICK_REQUEST;
		conv_scoped_ptr_rq(qname);
		if (T.debug) {
			dname_decompress (str, MAX_DNAME, qname, 0, 0, 0);
			syslog (LOG_DEBUG, "%s: converted scoped qname: %s",
			fn, str);
		}
	}
#endif

#ifdef STF
	if (T.stf && (qtype == RT_NS || qtype == RT_PTR))
		if (conv_stf_is_stf_ptr (cont->qname)) {
			cont->work_state = STF_REQUEST;
			if (T.debug) {
				dname_decompress (str, MAX_DNAME, qname,0,0,0);
				syslog (LOG_DEBUG, "%s: 6to4 rev query", fn);
			}
	}
#endif

#undef QNAME

	/*
	 * initialize list of nameservers to current forwarder
	 */
	cont->nameservers = fwd_socketlist();
	cont->current_ns = cont->nameservers->next;
	if (!cont->current_ns || !cont->current_ns->list_data) {
		syslog (LOG_ERR, "%s: no forwarders available!", fn);
		return -1;
	}

       	syslog (LOG_INFO, "Selected forwarder: %s",
		sprint_inet((struct sockaddr *)
			(cont->current_ns->list_data), str));

	/* forward the actual (modified) query ww want to make */
	return (do_forward (cont, qname, qclass, qtype, tcp));
}


int do_forward(struct context *parent, u_char *qname, uint16_t qclass,
	       uint16_t qtype, int tcp) {
	const char *fn = "do_forward()";
        static u_char buf[MAX_STREAM];     /* Buffer for TCP/UDP messages */
	u_char str[MAX_DNAME];
	Context *cont;

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

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

	/* we'll hook it up as a child of the given (parent) context */
	cont->parent = parent;
	cont->parent->child = cont;
	cont->timeout = SEARCH_REMOTE_TIMEOUT;
	cont->current_ns = cont->parent->current_ns;

	strlcpy (cont->qname, qname, MAX_DNAME);
	cont->q_class = qclass;
	cont->q_type = qtype;
	cont->q_id = mesg_id ();

	syslog (LOG_DEBUG, "%s: constructing query", fn);

	/*
	 * if we have an empty query string in qname, get the qname
	 * straight from the received request such that we forward
	 * it unmodified (binary or not)
	 */
	if (*qname == '\0')
		qname = (parent->mesg.p + sizeof (Mesg_Hdr));

	cont->mesg_len = mesg_make_query (qname, qtype, qclass, cont->q_id, 1,
					  buf, sizeof(buf));

	if (cont->mesg_len < 0) {
                syslog (LOG_ERR, "%s: failed to keep query in %zd bytes",
			fn, sizeof(buf));
		return (request_abort (cont, -1));
	}

        cont->mesg.p = malloc (cont->mesg_len);
	if (!cont->mesg.p) {
		syslog (LOG_ERR, "%s: Cannot allocate memory", fn);
		return (request_abort (cont, -1));
	}

	memcpy (cont->mesg.p, buf, cont->mesg_len);
        cont->wp = cont->mesg.p;

	if (tcp == 0) {
		dname_decompress (str, MAX_DNAME, cont->qname, 0, 0, 0);
		syslog (LOG_INFO, "Forward %s query for %s over UDP",
			string_rtype(cont->q_type), str);
		tcp = udp_request_start (cont);
	}

	if (tcp == 1) {
		dname_decompress (str, MAX_DNAME, cont->qname, 0, 0, 0);
		syslog (LOG_INFO, "Forward %s query for %s over TCP",
			string_rtype(cont->q_type), str);
		tcp = tcp_request_start (cont);
	}

	return tcp;
}

int request_retry (Context *cont) {
	char astr[MAX_DNAME], bstr[MAX_DNAME];

	cont->retry_count++;
	cont->timeout = cont->timeout * 2;

	/* mark current forwarder as unresponsive */
	fwd_mark (cont->current_ns->list_data, 1);

	if (cont->retry_count > SEARCH_REMOTE_RETRY) {
		syslog (LOG_INFO, "Exceeded retry limit");
		return -2;
	}

	/* select next nameserver in the list, if any */
	if (!cont->current_ns->next || !cont->current_ns->next->list_data)
		syslog (LOG_DEBUG, "Cycled through list of nameservers");
	else
		cont->current_ns = cont->current_ns->next;

	dname_decompress (astr, MAX_DNAME, cont->qname, 0, 0, 0);
       	syslog (LOG_INFO, "Retrying query for %s to nameserver: %s", astr,
		sprint_inet((struct sockaddr *) (cont->current_ns->list_data),
			    bstr));

	/*
	 * Note that we keep timout time high, even when we
	 * switched to another forwarder/nameserver
	 */

	syslog (LOG_DEBUG, "Retry %d of %d+%d will time out in %d seconds",
		cont->retry_count, SEARCH_REMOTE_RETRY, SEARCH_REMOTE_RETRY,
		cont->timeout);

	return 0;
}

/*
 * After making and sending a request with request_start(), do_forward() and
 * {udp|tcp}_request_start(), we get back here to process the response
 * (only if we received one of course).
 *
 * Note that it is the child that calls us, before it cleans itself, i.e. we
 * are called by {tcp|udp}_request_finish(). The response message is in our
 * child context and has already been parsed. The resource records found in
 * it, are then also available as lists in the child.
 */
int recursive_process (Context *cont) {
        const char *fn = "recursive_process()";
	u_char qname[MAX_DNAME], astr[MAX_DNAME];
	uint16_t qclass, qtype;
	int switch_to_tcp;
	uint16_t rtype;

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

	/*
	 * The child context contains the response message, which we look at
	 * here directly to decide what we need to do. Note that the child
	 * context keeps describing the original request we forwarded, not 
	 * the response! (I.e. only the actual message buffer got replaced).
	 */

	if (cont->child) {
		u_char *cp;

		if (cont->child->mesg.hdr->rcode != RC_OK &&
	    	    cont->child->mesg.hdr->rcode != RC_NXDOMAIN &&
		    cont->child->mesg.hdr->rcode != RC_SERVERERR) {
			/* RC_SERVERERR is handled below to allow hack */
			cont->mesg.hdr->rcode = cont->child->mesg.hdr->rcode;
			return 1;
		}

		cp = mesg_skip_dname (cont->child->mesg.p + sizeof (Mesg_Hdr),
			cont->child->mesg.p + cont->child->mesg_len);
		if (cp) {
			/*
		 	 * we need the resource type of the response message
		 	 */
			GETSHORT (rtype, cp);
		} else {
			syslog(LOG_INFO, "%s: malformed message", fn);
			return -1;
		}
	} else {
		syslog (LOG_DEBUG, "%s: child died", fn);
		return -1;
	}

	/* initialize vars for additional request we may make */
	qclass = cont->q_class;
	qtype = cont->q_type;
	strlcpy(qname, cont->qname, MAX_DNAME);

	syslog (LOG_DEBUG, "%s: work state: %d", fn, cont->work_state);

	/*
	 * In case of a truncated response, we'll try the same request 
	 * again using TCP.
	 */
	switch_to_tcp = 0;
	if (cont->child->mesg.hdr->tc == 1) {
		syslog (LOG_DEBUG, "%s: Truncated Message", fn);
		if (cont->child->conn_sock < 0) {
			switch_to_tcp = 1;
			syslog (LOG_DEBUG, "%s: Switch to TCP", fn);
			/* skip state machine, just redo current child query */
			goto do_work;
		} else {
			/* out of options, truncated TCP message */
			syslog (LOG_DEBUG, "%s: TCP message too big", fn);
			return -1;
		}
	}

	/*
	 * Some misbehaving old nameservers simply fail to process a 
	 * AAAA query and return a RC_SERVERERR (aka. SERVFAIL)
	 * error response. If such is the case, we hack around it
	 * and let totd pretend it got a proper NXDOMAIN response.
	 */
	if (cont->child->mesg.hdr->rcode == RC_SERVERERR) { 
	    	if (cont->work_state == FORWARD_REQUEST &&
		    (qtype == RT_AAAA || qtype == RT_A6)) {
			cont->child->mesg.hdr->rcode = RC_NXDOMAIN;
		} else {
			/* behave as normal in case of failure (see above) */
                        cont->mesg.hdr->rcode = cont->child->mesg.hdr->rcode;
                        return 1;
		}
	}

	/*
	 * we changed a AAAA or A6 query into a A query, now construct
	 * AAAA or A6 replies with prefix to match original request.
	 */
	if (cont->work_state == TRICK_REQUEST) {
		if (T.prefixnum && rtype == RT_A &&
		    (qtype == RT_AAAA || qtype == RT_A6)) {
			conv_trick_list (cont->child->an_list, qtype, 0);
			conv_trick_list (cont->child->ns_list, qtype, 0);
			/* circulate through prefixes */
                        T.current_prefix = (T.current_prefix + 1) % T.prefixnum;
			syslog (LOG_DEBUG, "%s: %s query changed into A query",
				fn, qtype == RT_AAAA ? "AAAA" : "A6");
		}
		cont->work_state = WORK_DONE;
	}

	if (cont->work_state == PTR_TRICK_REQUEST) {
		conv_trick_ptr (cont->child->an_list, cont->qname);
		if (T.debug) {
			dname_decompress (astr, MAX_DNAME, cont->qname, 0,0,0);
			syslog (LOG_DEBUG, "%s: converted PTR response %s",
				fn, astr);
		}
		cont->work_state = WORK_DONE;
	}

	if (cont->work_state == NEWPTR_TRICK_REQUEST) {
		conv_trick_newptr(cont->child->an_list, cont->qname);
		if (T.debug) {
			dname_decompress (astr, MAX_DNAME, cont->qname, 0,0,0);
			syslog (LOG_DEBUG, "%s: converted new PTR response: %s",
				fn, astr);
		}
		cont->work_state = WORK_DONE;
	}

	/* 
	 * If we got no answers to our query, we may need to do our trick. 
	 * I.e. we may need to send another query and use it to fabricate
	 * some answers...
	 */
	if (cont->work_state == FORWARD_REQUEST) {
		if (T.prefixnum && (cont->child->mesg.hdr->rcode == RC_NXDOMAIN ||
		    (cont->child->mesg.hdr->rcode == RC_OK && (!cont->child->mesg.hdr ||
                         cont->child->mesg.hdr->ancnt == htons(0))))) {
			/* We got no answers in the reply we got to our query. 
		 	 * We check here whether we need to modify the query
		 	 * to get answers we can transform into (pseudo-)
		 	 * answer records for the original query.
		 	 */
			if (rtype == RT_AAAA || rtype == RT_A6) {
				syslog (LOG_DEBUG, "Changed target to A");

				/* same query, but IPv4 address type */
				strlcpy(qname, cont->qname, MAX_DNAME);
				qtype = RT_A;

				cont->work_state = TRICK_REQUEST;
			}
		}
	}

	/* if we are just forwarding without tricks, then we are done */
	if (cont->work_state == FORWARD_REQUEST)
		cont->work_state = WORK_DONE;

#ifdef STF
	/* 
	 * State Machine for reverse lookup (PTR) of 6to4 address.
	 */

	if (cont->work_state == STF_REQUEST) {
		/*
	 	 * We forwarded 6to4 PTR request as normal, if we did not get
		 * answers from that we start our 6to4 PTR trick. First we need
		 * to find the nameservers for the IPv4 reverse zone that is
		 * embedded in the 6to4 address.
	 	 */
		if (cont->child->mesg.hdr->rcode == RC_NXDOMAIN) {
			/*
			 * 6to4 address is like 2002:AABB:CCDD:...
			 * PTR request is the like:
			 *	 ...d.d.c.c.b.b.a.a.2.0.0.2.ip6.int.
			 *
			 * Here, we are only interested in the final part of
			 * the PTR request that contains the embedded IPv4
			 * address.
			 * The last 48 bits of the address in the PTR record
			 * are 32 characters (digit and length bytes).
			 */
			int len = strlen(cont->qname) -
				(strlen(cont->qname) > 32) ? 32 : strlen(qname);
			strlcpy(cont->stfname, cont->qname + len , MAX_DNAME);
			cont->work_state = STF_NSLIST;
		} else
			cont->work_state = STF_DONE;
	}

	syslog (LOG_DEBUG, "%s: work state: %d", fn, cont->work_state);

	/*
	 * We forward our 6to4 request to each nameserver in the list untill we
	 * find one that gives us an answer.
	 */
	if (cont->work_state == STF_FORWARDING &&
	    cont->child->mesg.hdr->ancnt != htons(0))
		cont->work_state = STF_DONE;

	syslog (LOG_DEBUG, "%s: work state: %d", fn, cont->work_state);

	if (cont->work_state == STF_NSLIST) {
		/*
		 * we are trying to find the nameservers for the embedded IPv4
		 * zone.
		 * if we found these for IPv4 PTR zone, then start forwarding
	 	 * to those to find the 6to4 PTR record. if not, move up the PTR
	 	 * hierarchy, and query again for nameservers
	 	 */
		if (cont->child->mesg.hdr->ancnt == htons(0)) {
			/* move up pointer hierarchy (two nybbles == 4 chars) */
			memmove(cont->stfname, cont->stfname+4, MAX_DNAME-4);
			/* if no IPv4 address zone left, give up */
			if (strlen(cont->stfname) < 16) 
				cont->work_state = STF_DONE;
		} else if (cont->q_type == RT_PTR) {
			collect_ns_socks(cont);
			/* set current ns to first one in the list */
			cont->current_ns = cont->nameservers;
			cont->work_state = STF_FORWARDING;
		} else {
			/* RT_NS request for 6to4
		 	 * We transform it into a nameserver
			 * pseudo-record here for the 6to4 address.
	 	 	 * This pseudo-record is marked non-authorative. 
		 	 */

			/* mark non-authorative */
			cont->child->mesg.hdr->aa = 0;
			conv_stf_ns_list(cont->child->an_list);
			conv_stf_ns_list(cont->child->ns_list);
			conv_stf_ns_list(cont->child->ar_list);
			if (T.debug) {
				dname_decompress(astr, MAX_DNAME, qname, 0,0,0);
				syslog (LOG_DEBUG, "Converted NS record for \
6to4 NS query: %s", astr);
			}

			cont->work_state = STF_DONE;
		}
	}

	syslog (LOG_DEBUG, "%s: work state: %d", fn, cont->work_state);

	switch (cont->work_state) {
	case STF_NSLIST:
		/* get embedded IPv4 address in 6to4 address */
		strlcpy(qname, cont->stfname, MAX_DNAME);
		if (conv_stf_ptr (qname) < 0)
			return -1;

		if (T.debug) {
			dname_decompress(astr, MAX_DNAME, cont->qname, 0,0,0);
			syslog (LOG_DEBUG, "Converted 6to4 PTR: %s", astr);
		}
		qtype = RT_NS;
		break;
	case STF_FORWARDING:
		cont->current_ns = cont->current_ns->next;

		if (!cont->current_ns->list_data) {
			if (T.debug) {
				dname_decompress(astr, MAX_DNAME, cont->qname,
						 0,0,0);
				syslog (LOG_DEBUG, "%s: tried all nameservers \
found for 6to4 PTR name: %s", fn, astr);
			}
			cont->work_state = STF_DONE;
		}

		if (T.debug) {
			dname_decompress(astr, MAX_DNAME, cont->qname, 0,0,0);
			syslog (LOG_DEBUG, "%s: 6to4 PTR req to: %s", fn, astr);
		}

		strlcpy(qname, cont->qname, MAX_DNAME);
		qtype = RT_PTR;
		break;
	default:
		break;
	}
#endif

	syslog (LOG_DEBUG, "%s: current work state: %d", fn, cont->work_state);

	if (cont->work_state == WORK_DONE) {
		/*
		 * We should be finished then... unless we only got a CNAME
		 * record in our answers and no address record with it to
		 * resolve it. In that case, we need to do another query to
		 * our forwarder to resolve it properly.
		 *
		 * Note that we only follow CNAME's for the actual answer
		 * record (and assume there's only one), not for the ns or ar
		 * records.
		 */

		/* unresolved CNAME in our answer list? */
		cname_without_crecord (cont->child->an_list, qname);
		if (*qname) {
			/*
			 * if so, should we follow it? standard (RFC) only
			 * allows a single CNAME, but CNAME chaining is rather
			 * common in practice. We follow chains a bit, but
			 * avoid following a CNAME loop by limiting its length.
			 */
			if (cont->cname_links++ > SEARCH_CNAME_LEVEL) {
				syslog (LOG_DEBUG, "%s: exceeded max number \
of CNAME links: depth %d. A loop perhaps?", fn, cont->cname_links);
				return -1;
			}

			/* change target and start again */
			syslog (LOG_DEBUG, "Changed target to CNAME name");
			cont->work_state = FORWARD_REQUEST;
		}
	}

	/*
	 * if we decided somewhere above that we need to do another query to
	 * formulate our response, we do so here
	 */
do_work:
	if (cont->work_state != WORK_DONE) {
        	char astr[MAX_DNAME];

		/* Now just forget about old child. child will free itself */
		cont->child->parent = NULL;
		cont->child = NULL;

		if (cont->current_ns->list_data)
        		syslog (LOG_INFO, "Selected nameserver: %s",
                    		sprint_inet((struct sockaddr *)
					(cont->current_ns->list_data), astr));

		/*
		 * send out query in a new child context, use same
		 * transport as before.
		 */
		if (cont->conn_sock < 0 && !switch_to_tcp)
			return do_forward (cont, qname, qclass, qtype, 0);
		else 
			return do_forward (cont, qname, qclass, qtype, 1);
	}

	/*
	 * Now we are ready to formulate our answer to the original query.
	 * We do the final manipulations, if any, here.
	 */

	/* rewrite additional section if needed */
	if (T.prefixnum) {
		if (cont->q_type == RT_ALL) {
			syslog (LOG_DEBUG, "Add IPv6 addresses to answers");
			conv_trick_list(cont->child->an_list, RT_AAAA, 1);
		}

		syslog (LOG_DEBUG, "Add IPv6 addresses to additional section");
		conv_trick_list(cont->child->ar_list, RT_AAAA, 1);
		/* conv_trick_list(cont->child->ar_list, RT_A6, 1); */

		/* circulate through prefixes */
		T.current_prefix =
			(T.current_prefix + 1) % T.prefixnum;
	}

#ifdef SCOPED_REWRITE
	if (qtype == RT_AAAA && T.scoped_prefixes && conv_scoped_query(cont)) {
		conv_scoped_list(cont->child->an_list);
		conv_scoped_list(cont->child->ns_list);
		conv_scoped_list(cont->child->ar_list);
        	syslog (LOG_DEBUG, "%s: checked whether to rewrite global \
into scoped address", fn);
	}

	if (cont->work_state == PTR_SCOPED_TRICK_REQUEST) {
		if (conv_scoped_query(cont)) {
			conv_scoped_ptr(cont->child->an_list,
					cont->child->qname);
			if (T.debug) {
				dname_decompress(astr, MAX_DNAME,
						 cont->child->qname, 0,0,0);
				syslog (LOG_DEBUG, "Converted query name: %s",
					astr);
			}
		}
	}
#endif

	/*
	 * Ok, end of sleazy tricks. If and when we get here, everything is
	 * quite normal. We made request and the response to it has been parsed
	 * and available in our child context. We copy the resulting resource
	 * records to our (the parent) context, and return to the caller
	 * {udp|tcp}_recursive_process() which is responsible for
	 * using the acquired results to send a response to the client.
	 */

	list_destroy (cont->an_list, rrset_freev);
	cont->an_list = cont->child->an_list;
	cont->child->an_list = NULL;

	list_destroy (cont->ns_list, rrset_freev);
	cont->ns_list = cont->child->ns_list;
	cont->child->ns_list = NULL;

	list_destroy (cont->ar_list, rrset_freev);
	cont->ar_list = cont->child->ar_list;
	cont->child->ar_list = NULL;

	syslog (LOG_DEBUG, "%s: finish", fn);
	return 1;		/* recursive query finished */
}

int request_abort (Context *cont, int status) {

	if (cont && status == -2) {
		/* we timed out, do not send error response */
		cont->parent->child = NULL;
		response_abort(cont->parent, -2);
		cont->parent = NULL;
	}

	/* if parent exists, process parent */
	if (cont && cont->parent) {
		cont->parent->child = NULL;	/* indicates child failure */
		cont->parent->process (cont->parent);
		cont->parent = NULL;
	}
	/* free all resources */
	context_destroy (cont);

	return (status);
}

int request_finish (Context *cont) {
	char *fn = "request_finish()";

	/*
	 * We have successfully sent a request and have received a
	 * response to it. Here we finish the request by deciding
	 * whether the response is ok or not.
	 */

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

	/* re-initialize answer, nameserver and additional record lists */
	list_destroy (cont->an_list, rrset_freev);
	list_destroy (cont->ns_list, rrset_freev);
	list_destroy (cont->ar_list, rrset_freev);
	cont->an_list = list_init ();
	cont->ns_list = list_init ();
	cont->ar_list = list_init ();
	if (!cont->an_list || !cont->ns_list || !cont->ar_list)
		return request_abort (cont, -1);

	/* Try to parse the response message */
	if (cont->mesg.hdr->tc == 0) {
		if (mesg_parse (cont->mesg.p, cont->mesg_len, cont->an_list,
			     cont->ns_list, cont->ar_list)) {
			syslog (LOG_WARNING, "%s: can't parse answer data", fn);
			return request_abort (cont, -1);
		}
	}

	/* mark success to forwarder */
	fwd_mark (cont->current_ns->list_data, -1);

	/*
	 * if we have a parent, finish processing the parent
	 *
	 * Note: the parent will not cleanup its children...
	 */
	if (cont->parent) {
		syslog (LOG_DEBUG, "%s: process parent context", fn);
		cont->parent->process (cont->parent);
	}

	/* ... so we cleanup ourselves in any case */
	context_destroy (cont);

	syslog (LOG_DEBUG, "%s: return success", fn);
	return 0; /* SUCCESS */
}

#ifdef STF
/*
 * Expects list of NS records in AN section of given context.
 * It searches for address records (A or AAAA) in the AR section of the
 * context matching these name servers. Each address thus found is put in
 * an appropriate sockaddr structure. These are then returned as
 * an array of sockaddr structs. (Caller's responsibility to free them).
 */
void collect_ns_socks (Context *cont) {
        const char *fn = "collect_ns_socks()";
	G_List* gl;

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

	/* `Steal' nameservers from child */
	list_destroy (cont->an_list, rrset_freev);
	list_destroy (cont->ns_list, rrset_freev);
	list_destroy (cont->ar_list, rrset_freev);
	cont->an_list = cont->child->an_list;
	cont->ns_list = cont->child->ns_list;
	cont->ar_list = cont->child->ar_list;

	/*
 	 * We, the parent, take over responsibility for
 	 * cleaning/freeing these lists we stole from the
 	 * child. 
 	 */
	cont->child->an_list = NULL;
	cont->child->ns_list = NULL;
	cont->child->ar_list = NULL;

	/* kill/free any old list */
	if (cont->nameservers)
		list_destroy(cont->nameservers, free);

	/* init the list of sockaddr structs */
	cont->nameservers = list_init();

	/*
	 * We cycle through the list of answers, which are
	 * NS (nameserver) records.
	 */
	for (gl = cont->an_list->next; gl->list_data; gl = gl->next) {
        	RRset *glue_record, *ns_record;
		struct sockaddr* sa_p;
		int ns, glue;

		ns_record = gl->list_data;

		for (ns = 0; ns < ns_record->data.d->data_cnt; ns++) {
			char *name;
			int len;
			RR* rr;

			rr = (RR*) (ns_record->data.p +
			 	data_offset(ns, ns_record->data.d));
			/*
			 * Extract the domainname and its length from the
			 * given record.
			 */
			len = rr->rd_len;
			name = rr_rdata(rr);

#ifdef USE_INET6
			/* try to find glue within AR record list */
			glue_record = search_name(cont->ar_list, name, len,
						  RT_AAAA);
			if (glue_record) {
				for (glue = 0; glue < glue_record->data.d->data_cnt; glue++) {
        				struct sockaddr_in6 *sin6_p;

					rr = (RR*) (glue_record->data.p
			   		 	 + data_offset(glue, glue_record->data.d));

					sa_p = (struct sockaddr *) malloc (sizeof(struct sockaddr_in6));
					if (!sa_p)
						return;

					memset ((void *) sa_p, 0, sizeof(struct sockaddr_in6));
        				sa_p->sa_family = AF_INET6;
#ifdef HAVE_SA_LEN_FIELD
        				sa_p->sa_len = sizeof (struct sockaddr_in6);
#endif
                			sin6_p = (struct sockaddr_in6 *) sa_p;
                			sin6_p->sin6_port = htons (PORT_TO);
                			sin6_p->sin6_flowinfo = htonl (0);
                			memcpy (sin6_p->sin6_addr.s6_addr, rr_rdata(rr),
						sizeof(sin6_p->sin6_addr.s6_addr));

					/* add sa_p to list */
					syslog (LOG_DEBUG, "%s: .adding nameserver address", fn);
					list_add_tail (cont->nameservers, sa_p);
				}
				rrset_free(glue_record);
			}
#endif /* USE_INET6 */

#ifdef USE_INET4
			glue_record = search_name(cont->ar_list, name, len, RT_A);
			if (glue_record) {
				for (glue = 0; glue < glue_record->data.d->data_cnt; glue++) {

					rr = (RR*)(glue_record->data.p
			   		 	 + data_offset(glue, glue_record->data.d));

#ifdef USE_INET6
					if (T.use_mapped) {
       						struct sockaddr_in6 *sin6_p;

						sa_p = (struct sockaddr *) malloc (sizeof(struct sockaddr_in6));
						if (!sa_p)
							return;
						memset ((void *) sa_p, 0, sizeof(struct sockaddr_in6));

       						sa_p->sa_family = AF_INET6;
#ifdef HAVE_SA_LEN_FIELD
        					sa_p->sa_len = sizeof (struct sockaddr_in6);
#endif
						sin6_p = (struct sockaddr_in6 *) sa_p;
       		       		  		sin6_p->sin6_port = htons (PORT_TO);
       		         			sin6_p->sin6_flowinfo = htonl (0);

						/* make IPv4 MAPPED IPv6 address */
						*(uint32_t *)(void *)(&sin6_p->sin6_addr.s6_addr[8]) = ntohl(0x0000ffff);
               					memcpy (&sin6_p->sin6_addr.s6_addr[12], rr_rdata(rr),
							sizeof(struct in_addr));
						/* add sa_p to list */
						syslog (LOG_DEBUG, "%s: ..adding nameserver mapped address", fn);
						list_add_tail (cont->nameservers, sa_p);
					}
#endif

					if (!T.use_mapped) {
       						struct sockaddr_in *sin_p;

						sa_p = (struct sockaddr *) malloc (sizeof(struct sockaddr_in));
						if (!sa_p)
							return;
						memset ((void *) sa_p, 0, sizeof(struct sockaddr_in));

       						sa_p->sa_family = AF_INET;
#ifdef HAVE_SA_LEN_FIELD
       						sa_p->sa_len = sizeof (struct sockaddr_in);
#endif
               					sin_p = (struct sockaddr_in *) sa_p;
               					sin_p->sin_port = htons (PORT_TO);
               					memcpy (&sin_p->sin_addr.s_addr, rr_rdata(rr),
							sizeof(sin_p->sin_addr.s_addr));
						/* add sa_p to list */
						syslog (LOG_DEBUG, "%s: ...adding nameserver address", fn);
						list_add_tail (cont->nameservers, sa_p);
					}
				} /* for */
				rrset_free(glue_record);
			} /* if (glue_record) */
#endif /* USE_INET4 */
		}
	}

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

  	return;
}
#endif

void cname_without_crecord (G_List *an_list, u_char *cname) {
        char *fn = "cname_without_crecord()";
	u_char *canonical_domain, *domain;
	int canonical_domain_len;
	RRset *rrs_tmp;
	RR *rr_tmp;
	G_List *gl;

	syslog (LOG_DEBUG, "%s: Searching for CNAME in answers", fn);

	/* start with nothing */
	*cname = '\0';
	canonical_domain = NULL;
	canonical_domain_len = 0;

	an_list->list_data = NULL; /* termination safety pin */
	for (gl = an_list->next; gl->list_data; gl = gl->next) {
		rrs_tmp = (RRset *) gl->list_data;
		if (rrs_tmp->key.info->r_type == RT_CNAME) {
			rr_tmp = (RR *) (rrs_tmp->data.p +
				 data_offset (0, rrs_tmp->data.p));
			canonical_domain = rr_rdata (rr_tmp);
			canonical_domain_len = rr_tmp->rd_len;
		}
	}

	if (!canonical_domain) 
		return;	/* no problem -- no cname record */

	an_list->list_data = NULL;
	for (gl = an_list->next; gl->list_data; gl = gl->next) {
		rrs_tmp = (RRset *) gl->list_data;
		domain = rrset_owner (rrs_tmp);

		if ((rrs_tmp->key.info->owner_len == canonical_domain_len)
		    && !mesg_dname_cmp (NULL, domain, canonical_domain))
			return;
	}

	syslog (LOG_DEBUG, "%s: Problem: CNAME without canonical record", fn);

	if (canonical_domain_len >= MAX_DNAME) {
		syslog (LOG_DEBUG, "%s: malformed message", fn);
		return;
	}

	memcpy(cname, canonical_domain, canonical_domain_len);
	cname[canonical_domain_len] = '\0'; /* terminate it as a string */

	return;
}


syntax highlighted by Code2HTML, v. 0.9.1