/****************************************************************************
 * 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: conv_trick.c,v 3.47 2002/12/11 16:39:49 dillema Exp $>
 */

#include "totd.h"

static RRset *conv_trick_rrset (RRset *, uint16_t, int);

static RRset *conv_trick_rrset (RRset *rrs_a, uint16_t qtype, int pref) {
        const char *fn = "conv_trick_rrset()";
	RR_List *rrl = NULL, *rrl_tmp;
	RRset *rrs_new = NULL;
	RR *rr_a = NULL, *rr_new = NULL;
	u_char *rd, *rd_new = NULL;

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

	/* parse A resource record set */
	rrl = rr_list_of_rrset (rrs_a);
	if (!rrl)
		return NULL;


	/* convert each RR_List data */
	for (rrl_tmp = rrl; rrl_tmp->next; rrl_tmp = rrl_tmp->next) {
		int rdlen;
		u_char *rd_addr;

		rr_a = rrl_tmp->rrp;
		rrl_tmp->rrp = NULL; /* kept in rr_a */
		rd = rr_rdata (rr_a);

		rdlen = sizeof(struct in6_addr);
		if (qtype == RT_A6)
			rdlen++; /* size of prefix length */

		rd_new = malloc (rdlen);
		if (!rd_new)
			break;

		rd_addr = (qtype == RT_AAAA) ? rd_new : (rd_new + 1);

		memcpy (rd_addr, T.prefix[pref], 12);
		memcpy (rd_addr + 12, rd, 4);

		if (qtype == RT_A6)
			*rd_new = 0; /* plen is 0 */

		rr_new = rr_alloc (rr_a->ttl, rdlen, rd_new);
		if (!rr_new)
			break;

		free (rd_new);
		rd_new = NULL;	/* kept in rr_new */
		rrl_tmp->rrp = rr_new;
		rr_new = NULL;	/* kept in rrl_tmp->rrp */

		free (rr_a);
		rr_a = NULL;
	}

	/* assemble A6/AAAA rrset */
	rrs_new = rrset_create (qtype, rrs_a->key.info->r_class,
				rrs_a->key.info->owner_len,
				rrset_owner (rrs_a), rrl);

	/* cleanup */
	if (rr_a)
		free (rr_a);
	if (rr_new)
		free (rr_new);
	if (rrl)
		rr_list_free (rrl);
	if (rd_new)
		free (rd_new);

	return rrs_new;
}

/* target_rtype should be either RT_A6 or RT_AAAA */
void conv_trick_list (G_List *rrsl, int target_rtype, int add) {
	const char *fn = "conv_trick_list()";
	RRset *rrsp, *rrsp_aaaa;
	u_char str[MAX_DNAME];
	G_List *gl;

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

	rrsl->list_data = NULL;
	for (gl = rrsl->next; gl->list_data; gl = gl->next) {
		RRset *dup;
		char *name;
		int len;

		rrsp = (RRset *) gl->list_data;
		if (rrsp->key.info->r_type != RT_A)
			continue;

		name = rrset_owner(rrsp);
		len = rrsp->key.info->owner_len;

		dup = search_name(rrsl, name, len, target_rtype);
		if (dup) {
			if (T.debug > 3) {
				dname_decompress (str, MAX_DNAME, name, 0,0,0);
				syslog (LOG_DEBUG, "%s: duplicate %s", fn, str);
			}
			rrset_free(dup);
			continue;
		}

		/* convert A record into faked target_rtype record */
		if (T.debug > 3) {
			dname_decompress (str, MAX_DNAME, name, 0,0,0);
			syslog (LOG_DEBUG, "%s: converting: %s", fn, str);
		}
		rrsp_aaaa = conv_trick_rrset(rrsp, target_rtype,
					     T.current_prefix);
		if (!rrsp_aaaa) {
			syslog (LOG_ERR, "%s: Can't convert A to AAAA", fn);
			continue;
		}

		if (T.debug) {
			dname_decompress (str, MAX_DNAME, name, 0,0,0);
			syslog (LOG_DEBUG, "%s: %s %s", fn,
				add ? "add" : "replace by", str);
		}

		if (add) {
			list_add (rrsl, rrsp_aaaa);
		} else {
			rrset_free (rrsp);
			gl->list_data = rrsp_aaaa;
		}
	}

	return;
}

void conv_trick_ptr (G_List *rrsl, u_char *qname) {
        const char *fn = "conv_trick_ptr()";
	U_Key rk, rk_new;
	G_List *gl;

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

	if (*qname == '\0')
		return;

	rrsl->list_data = NULL;
	for (gl = rrsl->next; gl->list_data; gl = gl->next) {
		rk = ((RRset *) gl->list_data)->key;
		if (rk.info->r_type == RT_PTR) {
			/* convert PTR record from in-addr.arpa to ip6.int */
			rk_new.p = malloc (KEYINFO_HEAD_LEN +
					   strlen((char *)qname) + 1);
			if (rk_new.p) {
				rk_new.info->r_type = RT_PTR;
				rk_new.info->r_class = C_IN;
				strcpy((char *)(rk_new.p + KEYINFO_HEAD_LEN),
				       (char *)qname);
				free (rk.p);
				((RRset *) gl->list_data)->key = rk_new;
			} else
				syslog (LOG_ERR, "Cannot allocate memory");
		}
	}

	return;
}

void conv_trick_newptr (G_List *rrsl, u_char *qname) {
        const char *fn = "conv_trick_newptr()";
	G_List *gl;
	U_Key rk, rk_new;
	size_t qlen;

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

	if (*qname == '\0')
		return;

	rrsl->list_data = NULL;
	for (gl = rrsl->next; gl->list_data;
	     gl = gl->next) {
		rk = ((RRset *) gl->list_data)->key;
		if (rk.info->r_type == RT_PTR) {
			/*
			 * convert PTR record from in-addr.arpa to
			 * ip6.arpa.
			 */
			qlen = strlen(qname) + 1;
			rk_new.p = malloc (KEYINFO_HEAD_LEN + qlen);
			if (rk_new.p) {
				rk_new.info->r_type = RT_PTR;
				rk_new.info->r_class = C_IN;
				memcpy(rk_new.p+KEYINFO_HEAD_LEN, qname, qlen);
				free (rk.p);
				((RRset *) gl->list_data)->key = rk_new;
			} else
				syslog (LOG_ERR, "Out of memory");
		}
	}

	return;
}

int conv_trick_conf (u_char *v6addr) {
        const char *fn = "conv_trick_conf()";

	if (T.prefixnum >= MAXPREFIXES)
		syslog (LOG_ERR, "%s: max number of %d prefixes exceeded",
			fn, MAXPREFIXES);

	if (inet_pton (AF_INET6, (char *)v6addr,
		       (void *)T.prefix[T.prefixnum]) == 1) {
		syslog (LOG_DEBUG, "%s: %s", fn, v6addr);

		/* SUCCESS */
		T.prefixnum++;
		return (0);
	} else
        	syslog (LOG_ERR, "%s: invalid IPv6 prefix: %s", fn, v6addr);

	/* FAILURE */
	return (-1);
}

/* 
 * returns 1 if netprefix of qname equals one of tot prefixes, else returns 0
 */
int conv_trick_is_tot_ptr (u_char *qname) {
	struct in6_addr a6;
	u_char *qname6, *q6;
	u_char *p, buf[3];
	unsigned int val;
	int i;
	const int cmpsiz = sizeof(struct in6_addr) - sizeof(struct in_addr);

	if (!T.prefixnum || *qname == '\0')
		return 0;

	if (!(strstr((char *)qname, "INT") || strstr((char *)qname, "int")))
		return 0;

	qname6 = (u_char *) strstr((char *)qname, "IP6");
	if (!qname6)
		qname6 = (u_char *) strstr((char *)qname, "ip6");
	if (!qname6)
		return 0;

	memset(&a6, 0, sizeof(a6));
	qname6--;
	if (qname6 - qname != 64)
		return 0;
	qname6--;
	q6 = qname6;
	p = &a6.s6_addr[0];
	while (q6 - 4 >= qname) {
		buf[0] = *q6--;
		if (*q6-- != 1)
			return 0;
		buf[1] = *q6--;
		if (*q6-- != 1)
			return 0;
		buf[2] = '\0';
		sscanf((char *)buf, "%x", &val);
		*p++ = val & 0xff;
	}

	for (i = 0; i < T.prefixnum; i++) {
		if (memcmp(&a6, &T.prefix[i], cmpsiz) == 0)
			return 1; /* got a match */
	}
	return 0;
}


void conv_trick_ptr_rq (u_char *qname) {
    u_char *qname6, tmpaddr[8];
    int len, i, tmp;

    if (*qname == '\0')
	return;

    len = 0;
    qname6 = qname;
    while (len < 8) {
	strncpy ((char *) tmpaddr + len, (char *) qname6 + 1, (int) *qname6);
	len += (int) *qname6;
	qname6 += ((int) *qname6) + 1;
    }
    for (i = 0; i < 8; i++) {
	if (isdigit (tmpaddr[i]))
	    tmpaddr[i] -= '0';
	else if (isalpha (tmpaddr[i]))
	    tmpaddr[i] -= isupper (tmpaddr[i]) ? 'A' - 10 : 'a' - 10;
    }
    for (qname6 = qname, i = 0; i < 8; i += 2) {
	tmp = tmpaddr[i] + 16 * tmpaddr[i + 1];
	len = snprintf ((char *) qname6 + 1, 4, "%d", tmp);
	*qname6 = len;
	qname6 += len + 1;
    }
    *qname6 = '\0'; /* null terminate */
    strlcat ((char *) qname, "\007in-addr\004arpa", MAX_DNAME);

    return;
}

/*
 * Similar routines as above, but for bitstring label + ip6.arpa cases.
 * We assume qname has already been validated.
 */
int conv_trick_is_tot_newptr (u_char *qname, struct in6_addr *a6p) {
	const int cmpsiz = sizeof(struct in6_addr) - sizeof(struct in_addr);
	int ip6len, arpalen, i;

	if (!T.prefixnum || *qname == '\0')
		return 0;

	ip6len = strlen("IP6");
	arpalen = strlen("ARPA");

	/* we consider "\[x.../128\].ip6.arpa." only */
	if (*qname != EDNS0_ELT_BITLABEL || *(qname + 1) != 128)
		return 0;

	/*
	 * Copy the 128 bits (16 bytes) ipv6 binary address label for
	 * later use by caller.
	 */
	memcpy(a6p, qname + 2, sizeof(*a6p));

	/* check the `tail', does it end in IP6.ARPA? */
	qname += (2 + sizeof(struct in6_addr)); /* ELT + bitlen + address */

	if (*qname != ip6len || (strncasecmp((char *)qname + 1, "IP6", ip6len)
	    && strncasecmp((char *)qname + 1, "ip6", ip6len)))
		return 0;
	else
		qname += ip6len + 1;

	if (*qname != arpalen ||
	    (strncasecmp((char *)qname + 1, "ARPA", arpalen) &&
	     strncasecmp((char *)qname + 1, "arpa", arpalen)))
		return 0;

	qname += arpalen + 1;
	if (*qname != '\0')
		return 0;

	/* name is OK, now check our list of prefixes */
	for (i = 0; i < T.prefixnum; i++) {
		if (!memcmp(a6p, &T.prefix[i], cmpsiz))
			return 1; /* got a match */
	}

	return 0;
}

void conv_trick_newptr_rq (u_char *qname, struct in6_addr *a6) {
	int len, i;

	if (*qname == '\0')
		return;

	/*
	 * we take the lower 32 bits (4 bytes) of the ipv6 address as
	 * ipv4 address, and construct a IPv4 PTR record from it
	 */
	for (i = 0; i < 4; i++) {
		len = snprintf((char *)qname + 1, 4, "%d", a6->s6_addr[15 - i]);
		*qname = len;
		qname += len + 1;
	}
	strcpy ((char *)qname, "\007in-addr\004arpa\0");

	return;
}

/*
 * Search name record in specified record list for given NS record,
 * i.e. find the address record, if any, for the given nameserver.
 */
RRset *search_name (G_List *rr_list, char *name, int len, int r_type_search) {
        const char *fn = "search_name()";
	u_char *tmp_domain;
	G_List* gl;
	RRset *rrs;

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

	/*
	 * search for address record (A or AAAA) of the nameserver
	 * (of NS record) in AR list
	 */
	rr_list->list_data = NULL; /* extra termination insurance */
	for (gl = rr_list->next; gl->list_data; gl = gl->next) {
		rrs = (RRset*)gl->list_data;
		tmp_domain =  rrset_owner(rrs);
		if (rrs->key.info->r_type == r_type_search &&
		    rrs->key.info->owner_len == len &&
		    !mesg_dname_cmp(NULL, tmp_domain, name))
			return rrset_copy(rrs);
	}

	/* no record found */
	return NULL;
}


syntax highlighted by Code2HTML, v. 0.9.1