/*
 * Copyright (C) 2001 USAGI/WIDE Project.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "talkaddr.h"
#include "prot_talkd.h"
#include "ctlmsg.h"

#include "talkaddr.h"

static __inline__ sa_family_t sockaddr_family(const struct sockaddr *sa) {
	switch(sa->sa_family) {
	case AF_UNSPEC:
	case AF_INET:
		return AF_INET;
#ifdef INET6
	case AF_INET6:
		return (IN6_IS_ADDR_V4MAPPED(&((const struct sockaddr_in6*)sa)->sin6_addr) ? AF_INET : AF_INET6);
#endif
	}
	return AF_UNSPEC;
}

int cm_import(const char *msg, int msglen, struct CtlMessage *buf) {
	if (!msg || !buf)
		return -1;
	memset(buf, 0, sizeof(*buf));

	switch(msglen){
	case sizeof(CTL_MSG):
	    {
		const CTL_MSG *cm = (const CTL_MSG *)msg;
		buf->vers = cm->vers;
		buf->type = cm->type;
		buf->answer = cm->answer;
		buf->pad = cm->pad;
		buf->id_num = ntohl(cm->id_num);
		taddr2saddr((const struct talkaddr *)&cm->addr, (struct sockaddr *)&buf->addr);
		taddr2saddr((const struct talkaddr *)&cm->ctl_addr, (struct sockaddr *)&buf->ctl_addr);
		buf->pid = ntohl(cm->pid);
		memcpy(buf->l_name, &cm->l_name, sizeof(buf->l_name));
		memcpy(buf->r_name, &cm->l_name, sizeof(buf->r_name));
		memcpy(buf->r_tty, &cm->r_tty, sizeof(buf->r_tty));
		break;
	    }
#ifdef INET6
	case sizeof(CTL_MSG6):
	    {
		const CTL_MSG6 *cm = (const CTL_MSG6 *)msg;
		buf->vers = cm->vers;
		buf->type = cm->type;
		buf->answer = cm->answer;
		buf->pad = cm->pad;
		buf->id_num = ntohl(cm->id_num);
		taddr2saddr((const struct talkaddr *)&cm->addr, (struct sockaddr *)&buf->addr);
		taddr2saddr((const struct talkaddr *)&cm->ctl_addr, (struct sockaddr *)&buf->ctl_addr);
		memcpy(buf->l_name, &cm->l_name, sizeof(buf->l_name));
		memcpy(buf->r_name, &cm->l_name, sizeof(buf->r_name));
		memcpy(buf->r_tty, &cm->r_tty, sizeof(buf->r_tty));
		break;
	    }
#endif
	default:
		return -1;
	}
	return 0;
}

int cr_import(const char *msg, int msglen, struct CtlResponse *buf) {
	if (!msg || !buf)
		return -1;

	memset(buf, 0, sizeof(*buf));

	switch(msglen) {
	case sizeof(CTL_RESPONSE):
	    {
		const CTL_RESPONSE *cr = (const CTL_RESPONSE *)msg;
		buf->vers = cr->vers;
		buf->type = cr->type;
		buf->answer = cr->answer;
		buf->pad = cr->pad;
		buf->id_num = ntohl(cr->id_num);
		taddr2saddr((const struct talkaddr *)&cr->addr, (struct sockaddr *)&buf->addr);
		break;
	    }
#ifdef INET6
	case sizeof(CTL_RESPONSE6):
	    {
		const CTL_RESPONSE6 *cr = (const CTL_RESPONSE6 *)msg;
		buf->vers = cr->vers;
		buf->type = cr->type;
		buf->answer = cr->answer;
		buf->pad = cr->pad;
		buf->id_num = ntohl(cr->id_num);
		taddr2saddr((const struct talkaddr *)&cr->addr, (struct sockaddr *)&buf->addr);
		break;
	    }
#endif
	default:
		return -1;
	}
	return 0;
}

int cm_export(const struct CtlMessage *cm, char *buf, int buflen) {
	int type1 = sockaddr_family((const struct sockaddr *)&cm->addr);
	int type2 = sockaddr_family((const struct sockaddr *)&cm->ctl_addr);

	if (type1 == AF_INET && type2 == AF_INET) {
		CTL_MSG *cmbuf = (CTL_MSG *)buf;
		if (buflen < (int)sizeof(*cmbuf))
			return -1;
		cmbuf->vers = cm->vers;
		cmbuf->type = cm->type;
		cmbuf->answer = cm->answer;
		cmbuf->pad = cm->pad;
		cmbuf->id_num = ntohl(cm->id_num);
		saddr2taddr((const struct sockaddr *)&cm->addr, (struct talkaddr *)&cmbuf->addr);
		saddr2taddr((const struct sockaddr *)&cm->ctl_addr, (struct talkaddr *)&cmbuf->ctl_addr);
		cmbuf->pid = htonl(cm->pid);
		strcpy(cmbuf->l_name, cm->l_name);
		strcpy(cmbuf->r_name, cm->r_name);
		strcpy(cmbuf->r_tty, cm->r_tty);
		buflen = sizeof(*cmbuf);
#ifdef INET6
	} else if (type1 != AF_UNSPEC && type2 != AF_UNSPEC) {
		CTL_MSG6 *cmbuf = (CTL_MSG6 *)buf;
		cmbuf->vers = cm->vers;
		cmbuf->type = cm->type;
		cmbuf->answer = cm->answer;
		cmbuf->pad = cm->pad;
		cmbuf->id_num = ntohl(cm->id_num);
		saddr2taddr((const struct sockaddr *)&cm->addr, (struct talkaddr *)&cmbuf->addr);
		saddr2taddr((const struct sockaddr *)&cm->ctl_addr, (struct talkaddr *)&cmbuf->ctl_addr);
		cmbuf->pid = htonl(cm->pid);
		strcpy(cmbuf->l_name, cm->l_name);
		strcpy(cmbuf->r_name, cm->r_name);
		strcpy(cmbuf->r_tty, cm->r_tty);
		buflen = sizeof(*cmbuf);
#endif
	} else {
		return -1;	/* error */
	}
	return buflen;
}

int cr_export(const struct CtlResponse *cr, char *buf, int buflen) {
	int type1 = sockaddr_family((const struct sockaddr *)&cr->addr);

	if (type1 == AF_INET) {
		CTL_RESPONSE *crbuf = (CTL_RESPONSE *)buf;
		crbuf->vers = cr->vers;
		crbuf->type = cr->type;
		crbuf->answer = cr->answer;
		crbuf->pad = cr->pad;
		crbuf->id_num = htonl(cr->id_num);
		saddr2taddr((const struct sockaddr *)&cr->addr, (struct talkaddr *)&crbuf->addr);
		buflen = sizeof(*crbuf);
#ifdef INET6
	} else if (type1 == AF_INET6) {
		CTL_RESPONSE6 *crbuf = (CTL_RESPONSE6 *)buf;
		crbuf->vers = cr->vers;
		crbuf->type = cr->type;
		crbuf->answer = cr->answer;
		crbuf->pad = cr->pad;
		crbuf->id_num = htonl(cr->id_num);
		saddr2taddr((const struct sockaddr *)&cr->addr, (struct talkaddr *)&crbuf->addr);
		buflen = sizeof(*crbuf);
#endif
	} else {
		return -1;	/* error */
	}
	return buflen;
}
