
/* udp.c - User Datagram Protocol handler */
 
 
#include "config.h" 
#include "packet.h"
#include "header.h"

char *receive_udp(msglen)
int	*msglen;
{
	struct ip *ipptr;
	struct iudp *iudpptr;
	struct dbugpkt *dbugptr;
	int cmdlen;
	unsigned short	save;

	ipptr = (struct ip *)evars.packet.ep_data;
	iudpptr = (struct iudp *) ipptr->i_data;
	dbugptr = (struct dbugpkt *) iudpptr->data;
	evars.framechar = dbugptr->frameno;

	if (evars.connected) {
		if ((net2hs(iudpptr->dstprt) == evars.monport) &&
		    ( ipptr->i_src == evars.host_ipaddr) ) {

			if (Estrncmp(dbugptr->data, "CON ", 4)) {
				/*
				 * CON when already connected, most probably
				 * for disconnecting.
				 */
				do_con_cmd();
			} else {
				jscan(dbugptr->len, msglen, 2);
				return((char *)dbugptr->data);
			}
		} else {
			/*
			 * Someone tries to connect. If this is
			 * someone from the same host, requesting
			 * connection at the same addresses, let it in.
			 * Otherwise, sorry, I'm busy.
			 */

			if (Estrncmp(dbugptr->data, "CON ", 4)) {
				getconparams();
				if (ipptr->i_src == evars.host_ipaddr) {
					if ((evars.monport == evars.con.ec_monport) &&
					    (evars.hostport == evars.con.ec_hostport)) {
								do_con_cmd();
					} else {
								goto reject;
				    }
				} else {

					reject:

					prepare_err_udp();
					send_messB("E CON");
					send_messB("*");
				}
			}
		}
	} else {
		if (net2hs(iudpptr->dstprt) == MAGIC_PORT)
		{
			/*
			 * Accept only CON for new connection
			 */
			if (Estrncmp(dbugptr->data, "CON ", 4))
				do_con_cmd();
		}
	}
	*msglen = 0;
	return((char *)0);
}

send_udp(data_size)
{
	struct ip *ipptr;
	struct iudp *iudpptr;
	int	transmit_size;

	ipptr = (struct ip *)evars.packet.ep_data;
	iudpptr = (struct iudp *) ipptr->i_data;
	iudpptr->length = hs2net(UDPHLEN + data_size);
	iudpptr->cksum = 0; /*apparently our net does not 
				support checksum*/
	ipptr->i_verlen = 0x45;
	ipptr->i_svctyp = 0x0;
	ipptr->i_paclen = hs2net(IPHLEN + UDPHLEN + data_size);
	ipptr->i_id = 0x9999;
	ipptr->i_fragoff = 0x0000;
	ipptr->i_tim2liv = 0xFF;
	ipptr->i_proto = IPRO_UDP; /* don't have to swap - only a single byte */
	ipptr->i_src = evars.ipaddr;
	ipptr->i_cksum = 0;
	ipptr->i_cksum = cksum(ipptr, IPHLEN/2); 

	evars.packet.ep_hdr.rstatus = 1;
	evars.packet.ep_hdr.npp = 0;
	transmit_size = EHLEN + IPHLEN + UDPHLEN + data_size;
	if (transmit_size < MINTRANSMIT)
		transmit_size = MINTRANSMIT;
	evars.packet.ep_hdr.rbc= transmit_size;
	evars.packet.ep_hdr.e_ptype = hs2net(EP_IP);

	if(!ewrite()) evars.fail_transmit++;
	return(0);
}

/*
 */

send_messB(resptr)
char *resptr;
{
	struct ip *ipptr_in;
	struct iudp *iudpptr_in;
	unsigned short i;
	int	plen, ack;
	struct dbugpkt *dp;
	short tempsum,  *shp,  checksum();

	ipptr_in = (struct ip *)evars.packet.ep_data;
	iudpptr_in = (struct iudp *) ipptr_in->i_data;
	dp = (struct dbugpkt *) iudpptr_in->data;

	i = 0;
	while ( (*resptr != '\r' ) && (*resptr) ) {
		dp->data[i++] = *(resptr++);
	} 
	ack = (i == 1) && (dp->data[0] == '*');
	plen = &dp->data[0] - &dp->seqno[0] + i;
	/*
	 * savemsg means that this is a regular message
	 * The intruder's message is replyed with seq_num = 0
	 */
	if (evars.savemsg)
		addhex(&dp->seqno[0], ++evars.seq_num & 0xffff, 4);
	else
		addhex(&dp->seqno[0], 0, 4);
	dp->stx = STX;
	dp->frameno = evars.framechar;
	addhex(&dp->len[0], i, 2);
	tempsum = checksum(&dp->frameno, i+sizeof(dp->frameno)
						+sizeof(dp->len));
	addhex(&dp->data[i], tempsum ,4);
	dp->data[i+4] = '\r';
	if (evars.savemsg) {
		evars.savemsg_len = plen + 4;
		bcopy(dp, &evars.savemsg_buff[0], evars.savemsg_len);
	}
	send_udp(plen+4);
	if (! ack) {
		/*
		send_udp(plen+4);
		*/
		evars.prevmsg_len[evars.save_num] = plen + 4;
		bcopy(dp, evars.prevmsg_buff[evars.save_num], plen + 4);
		evars.save_num = (evars.save_num + 1) % SAVECOUNT;
	}
}

resend()
{
	struct ip *ipptr_in;
	struct iudp *iudpptr_in;
	int	plen;
	struct dbugpkt *dp;

	if (! evars.connected)
		return;
	ipptr_in = (struct ip *)evars.packet.ep_data;
	iudpptr_in = (struct iudp *) ipptr_in->i_data;
	dp = (struct dbugpkt *) iudpptr_in->data;

	bcopy(&evars.savemsg_buff[0], dp, evars.savemsg_len);
	send_udp(evars.savemsg_len);
}

/*
 * resend the previous packet
 * this is done when the debugger complains about lost packet.
 */

recover_packet(lost)
unsigned short lost;
{
	unsigned int seqno , n;
	struct dbugpkt *sp,*dp;
	struct ip *ipptr_in;
	struct iudp *iudpptr_in;

	for (n = (evars.save_num + 1) % SAVECOUNT; n != evars.save_num; 
	     n = (n +1) % SAVECOUNT){
	    sp = (struct dbugpkt *) evars.prevmsg_buff[n];

	    jscan(sp->seqno, &seqno, 4);
	    if (seqno == lost) 
		break;
	}
	/*
	 * make sure that the next packet is sent
	 * with the sequence number == lost
	 */
	if (seqno != lost) {
		evars.seq_num = lost - 1;
		mon_error("?");
		return;
	}
	evars.seq_num = lost;
	ipptr_in = (struct ip *)evars.packet.ep_data;
	iudpptr_in = (struct iudp *) ipptr_in->i_data;
	dp = (struct dbugpkt *) iudpptr_in->data;
	evars.framechar = sp->frameno;
	prepare_udp();
	bcopy( sp,dp,evars.prevmsg_len[n]);
	send_udp(evars.prevmsg_len[n]);
	send_udp(evars.prevmsg_len[n]);
	send_udp(evars.prevmsg_len[n]);
}

/*
 * protocol B checksum
 */

short checksum(sptr, cnt)
char *sptr;
int  cnt;
{
	unsigned int i, c, q;
	int crc = 0;

	for (i=0; i<cnt; i++)
	{
		c = *sptr++;
		q = (crc ^ c) & 0xf;
		crc = (crc >>4) ^ (q * 0x1081);
		q = (crc ^ (c >> 4)) & 0xf;
		crc = (crc >>4) ^ (q * 0x1081);
	}
	return (crc & 0xFFFF);
} /* end of checksum */

do_con_cmd()
{
	int cmdlen,  tmp;
	struct ip *ipptr;
	struct iudp *iudpptr;
	struct dbugpkt *dbugptr;

	ipptr = (struct ip *)evars.packet.ep_data;
	iudpptr = (struct iudp *) ipptr->i_data;
	dbugptr = (struct dbugpkt *) iudpptr->data;
	getconparams();
	dumpconreq(ipptr);

	if (evars.con.ec_monport < 255 || evars.con.ec_hostport < 255) {
		mon_error("?");
		return;
	}
	if ((evars.con.ec_monport & 0xFFFF) == 0xFFFF) {
		evars.connected = FALSE;
		evars.monport = 0xFFFF;
		evars.seq_num = 0;
		evars.host_ipaddr = 0;
		reset_host_eaddr();
		tmp = 0;
	} else {
		evars.connected = TRUE;
		evars.monport = evars.con.ec_monport & 0xFFFF;
		evars.host_ipaddr = ipptr->i_src;
		evars.ipaddr = ipptr->i_dest;
		set_host_eaddr();
		tmp = evars.ipaddr;
	}
	evars.hostport = evars.con.ec_hostport & 0xFFFF;
	prepare_udp();
	send_messB("*");
	evars.ipaddr = tmp;
	return;
}

mon_error(msg)
char *msg;
{
	if (evars.connected) {
		prepare_udp();
		send_messB(msg);
		resend();
		resend();
		send_messB("*");
		resend();
		resend();
	}
	return;
}

/*
 * prepare an udp message to be sent to
 * the port `hostport' on the host to which we are connected
 */

prepare_udp()
{
	struct ip *ipptr;
	struct iudp *iudpptr;

	ipptr = (struct ip *)evars.packet.ep_data;
	iudpptr = (struct iudp *) ipptr->i_data;

	iudpptr->srcprt = hs2net(evars.monport&0xFFFF);
	iudpptr->dstprt = hs2net(evars.hostport&0xFFFF);
	if (has_host_eaddr()) {
		blkcopy(evars.packet.ep_hdr.e_dest, evars.host_eaddr, 3);
		ipptr->i_dest = evars.host_ipaddr;
	}
	else {
		blkcopy(evars.packet.ep_hdr.e_dest, evars.packet.ep_hdr.e_src, 3);
		ipptr->i_dest = ipptr->i_src;
	}
	evars.savemsg = 1;
}

/*
 * prepare an udp message to be sent to
 * the port `hostport' on the host which unsuccessfully tried
 * to establish connection with us.
 */
prepare_err_udp()
{
	struct ip *ipptr;
	struct iudp *iudpptr;

	ipptr = (struct ip *)evars.packet.ep_data;
	iudpptr = (struct iudp *) ipptr->i_data;

	iudpptr->srcprt = hs2net(evars.con.ec_monport&0xFFFF);
	iudpptr->dstprt = hs2net(evars.con.ec_hostport&0xFFFF);
	blkcopy(evars.packet.ep_hdr.e_dest, evars.packet.ep_hdr.e_src, 3);
	ipptr->i_dest = ipptr->i_src;
	evars.savemsg = 0;
}

getconparams()
{
	int cmdlen,  tmpsize;
	struct ip *ipptr;
	struct iudp *iudpptr;
	struct dbugpkt *dbugptr;
	int	tmp1, tmp2;

	ipptr = (struct ip *)evars.packet.ep_data;
	iudpptr = (struct iudp *) ipptr->i_data;
	dbugptr = (struct dbugpkt *) iudpptr->data;

	jscan(dbugptr->len, &cmdlen, 2);
	tmpsize = jscan(dbugptr->data+4, &tmp1, cmdlen);
	jscan(dbugptr->data+5+tmpsize, &tmp2, cmdlen-5-tmpsize);
	evars.con.ec_monport = tmp1 & 0xffff;
	evars.con.ec_hostport = tmp2 & 0xffff;
}

dumpconreq(ipptr)
struct ip *ipptr;
{
	PUTSTR("connect request from ipaddr: "); PUTNUM(ipptr->i_src);
	PUTSTR("\r\n");
	PUTSTR("connect parameters: monport "); 
	PUTNUM(evars.con.ec_monport);
	PUTSTR(" hostport: ");
	PUTNUM(evars.con.ec_hostport);
	PUTSTR("\r\n");
	PUTSTR("monport: "); PUTNUM(evars.monport& 0xffff);
	PUTSTR(" hostport: "); PUTNUM(evars.hostport& 0xffff);
	PUTSTR("\r\n");
}

set_host_eaddr()
{
	blkcopy(evars.host_eaddr, evars.packet.ep_hdr.e_src, 3);
}

reset_host_eaddr()
{
	register int i;

	for (i = 0; i < 5; i ++)
		evars.host_eaddr[i] = 0xff;
}

has_host_eaddr()
{
	register int i;
	for (i = 0; i < 5; i ++)
		if (evars.host_eaddr[i] != 0xff)
			return(1);
	return(0);
}
