/****************************************************************************
 * Copyright (C) 1998 WIDE Project. All rights reserved.
 * Copyright (C) 1999,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_scoped.c,v 1.31 2003/01/27 16:22:46 dillema Exp $>
 */

#include "totd.h"

#ifdef SCOPED_REWRITE

static RRset *conv_scoped_rrset (RRset *);

int conv_scoped_query(Context *context) {
	struct sockaddr_in6 *me;
	struct sockaddr_in6 *peer;
	static int warned = 0;
	Nia *ni;

	syslog(LOG_DEBUG, "conv_scoped_query(): start");

	ni = context->netifaddr;
	if (!ni || !ni->sa_p)
		return 0;

	me =(struct sockaddr_in6 *)ni->sa_p;
	if (me->sin6_family != AF_INET6)
		return 0;

	if (IN6_ARE_ADDR_EQUAL(&me->sin6_addr, &in6addr_any)) {
		/* me is wildcard, so lookup our address */
		if (!warned)
			syslog(LOG_WARNING, "Scoped rewriting not \
implemented for wildcard sockets");
		warned = 1;
		return 0;
	}

	peer = (struct sockaddr_in6 *)context->peer;
	if (peer->sin6_family != AF_INET6)
		return 0;

	if (IN6_IS_ADDR_LINKLOCAL(&me->sin6_addr) &&
	    IN6_IS_ADDR_LINKLOCAL(&peer->sin6_addr)) {
#ifdef HAVE_SIN6_SCOPE_ID
	    	if (me->sin6_scope_id == peer->sin6_scope_id)
			return 1; /* good */
		if (me->sin6_scope_id)
			return 0;
		/*
		 * XXX this is horrible hack to handle case where
		 * scope_id is in sockaddr, and not in sin6_scope_id
		 * due to problem with SIOCGIFCONF ioctl. I have this
		 * problem on my test machine, so I added this code
		 * to be able to test it all. XXX This code should
		 * be removed again in the future.
		 */
		if (!warned)
			syslog(LOG_WARNING, "Need hack around your \
sin6_scope_id for SIOCGIFCONF!");
		warned = 1;

	    	if ((&me->sin6_addr)->s6_addr[3] == peer->sin6_scope_id)
			return 1; /* good */
		return 0;
#endif
		/* when we don not know, do rewrite */
		return 1;
	}

	if (IN6_IS_ADDR_SITELOCAL(&me->sin6_addr) &&
	    IN6_IS_ADDR_SITELOCAL(&peer->sin6_addr)) {
#ifdef HAVE_SIN6_SCOPE_ID
	    	if (me->sin6_scope_id == peer->sin6_scope_id)
			return 1; /* good */
		if (me->sin6_scope_id)
			return 0;
		/*
		 * XXX this is horrible hack to handle case where
		 * scope_id is in sockaddr, and not in sin6_scope_id
		 * due to problem with SIOCGIFCONF ioctl. I have this
		 * problem on my test machine, so I added this code
		 * to be able to test it all. XXX This code should
		 * be removed again in the future.
		 */
		if (!warned)
			syslog(LOG_WARNING, "Need hack around your \
sin6_scope_id for SIOCGIFCONF!");
		warned = 1;

	    	if ((&me->sin6_addr)->s6_addr[3] == peer->sin6_scope_id)
			return 1; /* good */
		return 0;
#endif
		/* when we don not know, do rewrite */
		return 1;
	}

	return 0;
}

static RRset *conv_scoped_rrset(RRset *rrs_old) {
	RR_List *rrl = NULL, *rrl_tmp = NULL;
	RRset *ret_val = NULL, *rrs_new = NULL;
	u_char *rd = NULL, *rd_new = NULL;
	RR_List sentinel, *cur;
	int i;

	syslog(LOG_DEBUG, "conv_scoped_rrset: start");

	if (!T.scoped_prefixes) {
		ret_val = NULL;
		goto finish;
	}

	memset(&sentinel, 0, sizeof(sentinel));
	cur = &sentinel;

	if (rrs_old->key.info->r_type != RT_AAAA) {
		ret_val = NULL;
		goto finish;
	}
	/* parse A rrset */
	rrl = rr_list_of_rrset(rrs_old);
	if (!rrl) {
		ret_val = NULL;
		goto finish;
	}
	/* convert each RR_List data */
	for (rrl_tmp = rrl; rrl_tmp->next; rrl_tmp = rrl_tmp->next) {
		rd = rr_rdata(rrl_tmp->rrp);

		for (i = 0; i < T.scoped_prefixes; i++) {
			if (!memcmp(rd, &T.scoped_from[i], T.scoped_plen[i]/8))
				break;
		}
		if (i >= T.scoped_prefixes)
			continue;

		/* make a duplicate */
		cur->next = rr_list_alloc();
		*(cur->next) = *rrl_tmp;
		cur->next->rrp = NULL;
		cur->next->next = NULL;

		rd_new = malloc(sizeof(struct in6_addr));
		if (!rd_new) {
			rr_list_free(cur->next);
			cur->next = NULL;
			continue;
		}

		memcpy(rd_new, &T.scoped_to[i], T.scoped_plen[i] / 8);
		memcpy(rd_new + T.scoped_plen[i] / 8, rd + T.scoped_plen[i] / 8,
		       sizeof(struct in6_addr) - T.scoped_plen[i] / 8);

		cur->next->rrp = rr_alloc(rrl_tmp->rrp->ttl,
					  sizeof(struct in6_addr), rd_new);

		if (rd_new)
			free (rd_new);
		rd_new = NULL;

		if (!cur->next->rrp) {
			rr_list_free(cur->next);
			cur->next = NULL;
			continue;
		}

		while (cur && cur->next)
			cur = cur->next;
	}

	cur->next = rrl;

	/* why do we need to count like this? */
	i = 0;
	for (cur = sentinel.next; cur; cur = cur->next)
		i++;
	i -= 2;
	for (cur = sentinel.next; cur; cur = cur->next)
		cur->cnt = i--;

	/* assemble AAAA rrset */
	rrs_new = rrset_create(RT_AAAA, rrs_old->key.info->r_class,
				 rrs_old->key.info->owner_len,
				 rrset_owner(rrs_old), sentinel.next);
	if (!rrs_new) {
		ret_val = NULL;
		goto finish;
	}

	/* set return value */
	ret_val = rrs_new;
	rrs_new = NULL;

finish:
	if (rrs_new)
		rrset_free(rrs_new);
	if (sentinel.next)
		rr_list_free(sentinel.next);
	if (rd_new)
		free(rd_new);

	syslog(LOG_DEBUG, "conv_scoped_rrset: return");
	return ret_val;
}

void conv_scoped_list(G_List *rrsl) {
	const char *fn = "conv_scoped_list()";
        RRset *rrsp, *rrsp_new;
	G_List *gl_tmp;

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

	rrsl->list_data = NULL;
	for (gl_tmp = rrsl->next; gl_tmp->list_data; gl_tmp = gl_tmp->next) {
		rrsp =(RRset *)gl_tmp->list_data;
		if (rrsp->key.info->r_type != RT_AAAA)
			continue;

		/* add scoped address if necessary */
		rrsp_new = conv_scoped_rrset(rrsp);
		if (!rrsp_new) {
			syslog(LOG_ERR, "Can't add scoped address");
		} else {
			rrset_free(rrsp);
			gl_tmp->list_data = rrsp_new;
			rrsp_new = NULL;
		}
	}

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

int conv_scoped_conf(const char *from, const char *to, int plen) {
	const char *fn = "conv_scoped_conf()";
	const int max = sizeof(T.scoped_from)/sizeof(T.scoped_from[0]);

	if (T.scoped_prefixes >= max) {
		syslog(LOG_ERR, "%s: max number of %d prefixes exceeded",
		       fn, max);
		return(-1);
	}
	if (plen % 8) {
		syslog(LOG_ERR, "%s: plen needs to be multiple of 8", fn);
		return(-1);
	}

	if (inet_pton(AF_INET6, from, &T.scoped_from[T.scoped_prefixes]) != 1) {
		syslog(LOG_ERR, "invalid format: from %s", from);
		return(-1);
	}
	if (inet_pton(AF_INET6, to, &T.scoped_to[T.scoped_prefixes]) != 1) {
		syslog(LOG_ERR, "invalid format: to %s", to);
		return(-1);
	}
	T.scoped_plen[T.scoped_prefixes] = plen;

	syslog(LOG_DEBUG, "%s: %s %s", fn, from, to);

	T.scoped_prefixes++;
	return(0);
}

void conv_scoped_ptr(G_List *rrsl, u_char *qname) {
	G_List *gl_tmp;
	U_Key rk;
	int idx;
	u_char *p, *q;
	int off;

	syslog(LOG_DEBUG, "conv_scoped_ptr: start.");

	if (!T.scoped_prefixes) {
		syslog (LOG_ERR, "No scoped prefix configured!");
		return;
	}

	idx = conv_is_scoped_ptr(qname, 1);
	if (idx == -1)
		return;

	rrsl->list_data = NULL;
	for (gl_tmp = rrsl->next; gl_tmp->list_data; gl_tmp = gl_tmp->next) {
		rk = ((RRset *) gl_tmp->list_data)->key;
		if (rk.info->r_type != RT_PTR)
			continue;

		/* convert PTR record from in-addr.arpa to ip6.int */
		p = rk.p + KEYINFO_HEAD_LEN;
		q = &T.scoped_to[idx].s6_addr[0];
		for (off = 0; off < T.scoped_plen[idx] / 8; off++) {
			p[64 - 4 - off * 4 + 3] = hex[(q[off] >> 4) & 0x0f];
			p[64 - 4 - off * 4 + 1] = hex[q[off] & 0x0f];
		}
	}

	syslog (LOG_DEBUG, "conv_scoped_ptr: return");
	return;
}

/* 
 * returns index of scoped rewrite, if netprefix of qname equals one of scoped
 * prefixes.  returns -1 if not.
 *
 * to argument: 0: from 1: to
 */
int conv_is_scoped_ptr(u_char *qname, int to) {
	u_char *qname6, *q6;
	struct in6_addr a6;
	u_char *p;
	unsigned int val;
	char buf[3];
	int i;

	if (!T.scoped_prefixes || *qname == '\0')
		return -1;

	if (!(strstr((char *)qname, "INT") || strstr((char *)qname, "int")))
		return -1;
	qname6 = (u_char *)strstr((char *)qname, "IP6");
	if (!qname6)
		qname6 = (u_char *)strstr((char *)qname, "ip6");
	if (!qname6)
		return -1;

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

		p++;
	}

	for (i = 0; i < T.scoped_prefixes; i++) {
		if (!memcmp(&a6, to ? &T.scoped_to[i] : &T.scoped_from[i],
				T.scoped_plen[i] / 8))
			return i;
	}
	return -1;
}

void conv_scoped_ptr_rq(u_char *qname) {
	u_char *p, *q;
	int off, idx;

	idx = conv_is_scoped_ptr(qname, 1);
	if (idx == -1)
		return;

	/* convert PTR record from in-addr.arpa to ip6.int */
	p = qname;
	q = &T.scoped_from[idx].s6_addr[0];
	for (off = 0; off < T.scoped_plen[idx] / 8; off++) {
		p[64 - 4 - off * 4 + 3] = hex[(q[off] >> 4) & 0x0f];
		p[64 - 4 - off * 4 + 1] = hex[q[off] & 0x0f];
	}
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1