/******************************************************************************
 * This file is part of a software distribution, which is furnished under the *
 * terms of a license.  Use of this software  by any means is subject to this *
 * license  and  signifies  the  acceptance of  the  licensing  terms  stated *
 * therein. Please see  the file LICENSE in the  top-level directory  of this *
 * software  distribution  for detailed copyright  disclaimers  and licensing *
 * terms.                                                                     *
 ******************************************************************************
 * Copryight (c) by Andreas S. Wetzel - All rights reserved.                  *
 ******************************************************************************/

/* $Id: network.c,v 1.2 2001/03/19 14:54:01 mickey Exp $ */

#include <vchat.h>
#include <proto_common.h>

#include <netdb.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#if HAVE_SYS_UIO_H
  #include <sys/uio.h>
#endif

#ifndef O_NONBLOCK
  #include <sys/ioctl.h>
#endif

/*** Externals ***/

int uchannel = 0;

extern VP vp;
extern VCONN sconn;
extern TMSTAT tmstat;

extern char *prog_name;
extern char nlstr[];

extern u_char is_handler;

extern VIDENT		*ident(void);
extern VLOGINDAT	*genvldat(void);
extern void		vquit(char *fmt, ...);
extern void		ring_my_bell(char *dat, size_t size);

/*** Globals ***/

ULIST_ITEM		*ulist_base;
int			ulist_cnt	= 0;

/*** Code ***/

/*****************************************************************************
 * Connect server at <addr> on port <cport>, requesting magicnumber <magic>. *
 * If the server asks for a password to be supplied send <passwd> to the     *
 * server to authenticate us.                                                *
 * Connection data is placed in the buffer pointed to by <vcn>               *
 * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *
 * Return values:                                                            *
 * -1        => Error has occured. See errno                                 *
 * CONN_OK   => Connection established                                       *
 * CONN_E... => Error occured (see param.h)                                  *
 *****************************************************************************/

int connect_server(VCONN *vcn, struct in_addr *addr, u_short cport, u_long magic, u_char *passwd)
{
	u_char	state;

	/*
	 * Initialize VCONN structure
	 */

	BZERO(vcn, sizeof(VCONN));

	/*
	 * Connect to vchat server
	 */

	sprintf(vcn->host, "%s", iptoname(*addr));
	vcn->saddr.sin_port = htons(cport);
	vcn->saddr.sin_family = AF_INET;
	vcn->saddr.sin_addr = *addr;
	
	if((vcn->fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
		return(-1);

	if(connect(vcn->fd, (struct sockaddr *)&vcn->saddr, sizeof(vcn->saddr)) < 0)
	{
		close(vcn->fd);
		return(-1);
	}

	/*
	 * Get server identification packet
	 */

	if(read(vcn->fd, &vcn->vid, sizeof(VIDENT)) != sizeof(VIDENT))
	{
		close(vcn->fd);
		return(-1);
	}

	vcn->vid.ident[VPIDENTSIZE] = '\0';
	vcn->vid.magic = ntohl(vcn->vid.magic);
	vcn->vid.proto = ntohl(vcn->vid.proto);
	vcn->vid.type = ntohl(vcn->vid.type);

	/*
	 * Check server magic number
	 */

	if(vcn->vid.magic != magic)
	{
		close(vcn->fd);
		return(CONN_EMAGIC);
	}

	/*
	 * Check for server messages in VIDENT packet
	 */

	if(vcn->vid.type != CONN_OK)
	{
		close(vcn->fd);
		return((int)vcn->vid.type);
	}

	/*
	 * Send out our identification
	 */

	if(write(vcn->fd, (void *)ident(), sizeof(VIDENT)) != sizeof(VIDENT))
	{
		close(vcn->fd);
		return(-1);
	}

	/*
	 * Await status
	 */

	if(read(vcn->fd, &state, 1) != 1)
	{
		close(vcn->fd);
		return(-1);
	}

	if(state != CONN_OK)
	{
		close(vcn->fd);
		return((int)state);
	}

	/*
	 * Send client data structure
	 */

	if(write(vcn->fd, (void *)genvldat(), sizeof(VLOGINDAT)) != sizeof(VLOGINDAT))
	{
		close(vcn->fd);
		return(-1);
	}

	/*
	 * Await status
	 */

	if(read(vcn->fd, &state, 1) != 1)
	{
		close(vcn->fd);
		return(-1);
	}

	/*
	 * Send password if authorization is required by the server
	 */

	if(state == CONN_AUTHREQ)
	{
		VPASSWD	vpwd;

		BZERO(&vpwd, sizeof(VPASSWD));

		if(passwd)
			strncpy(vpwd.passwd, passwd, VPPASSWDSIZE);

		if(write(vcn->fd, &vpwd, sizeof(VPASSWD)) != sizeof(VPASSWD))
		{
			close(vcn->fd);
			return(-1);
		}

		/*
		 * Await status
		 */

		if(read(vcn->fd, &state, 1) != 1)
		{
			close(vcn->fd);
			return(-1);
		}
	}

	/*
	 * Set socket owner
	 */

	if(state == CONN_OK)
	{
		fcntl(vcn->fd, F_SETOWN, getpid());
	}
	else
	{
		close(vcn->fd);
	}

	return((int)state);
}

void rcv_sv_msg(void)
{
	static u_char rcvbuf[SVMSGBUF];
	static u_char *rcvptr = (u_char *)&rcvbuf;

	u_char *dat;

	register int i;

	int amount;
	int segment;
	int arg;

#ifndef O_NONBLOCK
	int on = 0xff;
	int off = 0;
#endif
	VMSG *xsv;
	TMREPORT *tmr;
	ULIST_ITEM *u;

	/*
	 * Keep vlock from modifying signal mask state
	 */

	is_handler = 1;

	/*
	 * Set filedesc to nonblocking I/O
	 */

#ifndef O_NONBLOCK
	ioctl(sconn.fd, FIONBIO, &on);
#else
	arg = fcntl(sconn.fd, F_GETFL, 0);

	arg |= O_NONBLOCK;

	fcntl(sconn.fd, F_SETFL, arg);
#endif

	/*
	 * Read in any data waiting
	 */

	if((amount = read(sconn.fd, rcvptr, (sizeof(rcvbuf) - (rcvptr - &rcvbuf[0])))) > 0)
	{
		rcvptr += amount;
	}
	else if(!amount)
	{
		/*** Broken pipe ***/

		vquit("GASP! Server connection has deceased -- exiting.");
	}
	else
	{
		switch(errno)
		{
			case EINTR:
#if !(EWOULDBLOCK == EAGAIN)
			case EWOULDBLOCK:
#endif
			case EAGAIN:		break;
			default:		vsleep(1,0);
						vquit("Error while reading from socket (%s) -- exiting.%s",
							strerror(errno), nlstr);
						break;
		}
	}

	/*
	 * Reset nonblocking I/O on filedesc
	 */

#ifndef O_NONBLOCK
	ioctl(sconn.fd, FIONBIO, &off);
#else
	arg &= ~O_NONBLOCK;

	fcntl(sconn.fd, F_SETFL, arg);
#endif

	/*
	 * Process any completed command
	 * packets waiting in the buffer
	 */

	xsv = (VMSG *)rcvbuf;
	amount = (rcvptr - rcvbuf);

	while(amount >= sizeof(VMSG) && amount >= (segment = (sizeof(VMSG) + ntohl(xsv->len))))
	{
		dat = ((u_char *)xsv + sizeof(VMSG));

		switch(ntohl(xsv->cmd))
		{
			case RTCMP_ACK:		timestamp(&tmstat.endmark);
						if(((RTCMP *)dat)->seq > 1)
						{
							((RTCMP *)dat)->seq--;
							store_tm_value(diff_t(&((RTCMP *)dat)->stamp, &tmstat.endmark));
							timestamp(&((RTCMP *)dat)->stamp);
							snd_serv(CMD_RTCMP, (char *)((RTCMP *)dat), sizeof(RTCMP));
						}
						else
						{
							store_tm_value(diff_t(&((RTCMP *)dat)->stamp, &tmstat.endmark));
							tmr = report_stat();
							cprintf(chat, "\n* %d bytes from %s%s%s (%s%s%s) - round-trip-delay:",
								(sizeof(RTCMP) + sizeof(VMSG)), S_BOLD, inet_ntoa(vp.sv_ip), S_OFF,
								S_UL, sconn.host, S_OFF);
							cprintf(chat, "* (min/avg/max) %s%s%s / %s%s%s / %s%s%s",
								S_FG_YEL, pr_time(&tmr->_min), S_OFF,
								S_FG_CYN, pr_time(&tmr->_avg), S_OFF,
								S_FG_MAG, pr_time(&tmr->_max), S_OFF);
							clear_tm_stats();
						}
						break;

			case SERV_MSG:		cputnchars(dat, ntohl(xsv->len));
						break;

			case SUBMIT_TOPIC:	submit_topic();
						break;

			case WAKEUP:		ring_my_bell(dat, ntohl(xsv->len));
						break;

			case NICK_ACK:		BCOPY(&vp.tmpnick, vp.nick, sizeof(vp.nick));
						alter_status(STAT_NICK, "%s ", vp.nick);
						vsleep(0,300000);
						update_status(0);
						break;

			case CHAN_ACK:		alter_status(STAT_CHAN, "%d ", ntohl(*((signed long *)dat)));
						uchannel = ntohl(*((signed long *)dat));
						vsleep(0,300000);
						update_status(0);
						break;

			case ULIST_CLEAR:	if(ulist_base)
							free(ulist_base);

						ulist_base = NULL;
						ulist_cnt = 0;
						break;

			case ULIST_ADD:		if(ulist_base)
						{
							if((u = (ULIST_ITEM *)realloc(ulist_base, sizeof(ULIST_ITEM) * (ulist_cnt + 1))) == NULL)
							{
								cprintf(chat, "* Memory reallocation error (%s%s%s)",
									S_FG_RED, strerror(errno), S_OFF);
								ulist_base = NULL;
								ulist_cnt = 0;
								break;
							}

							ulist_base = u;
							u = ulist_base + ulist_cnt;
						}
						else
						{
							if((u = (ULIST_ITEM *)malloc(sizeof(ULIST_ITEM))) == NULL)
							{
								cprintf(chat, "* Memory allocation error (%s%s%s)",
									S_FG_RED, strerror(errno), S_OFF);
								ulist_base = NULL;
								ulist_cnt = 0;
								break;
							}

							ulist_base = u;
							ulist_cnt = 0;
						}

						BZERO(u->nick, VPNICKSIZE + 1);
						BCOPY(dat, u->nick, (ntohl(xsv->len) <= VPNICKSIZE) ? ntohl(xsv->len) : VPNICKSIZE);

						++ulist_cnt;
						break;

			case ULIST_REMOVE:
			{
				u_char lookup[VPNICKSIZE+1];
				BZERO(lookup, sizeof(lookup));
				BCOPY(dat, lookup, (ntohl(xsv->len) > VPNICKSIZE) ? VPNICKSIZE : ntohl(xsv->len));

				for(i = 0; i < ulist_cnt; i++)
				{
					u = ulist_base + i;

					if(!strcmp(u->nick, lookup))
					{
						if(i < (ulist_cnt - 1))
							memmove((char *)u, (char *)u + sizeof(ULIST_ITEM), (ulist_cnt - (i + 1)) * sizeof(ULIST_ITEM));

						if((u = realloc(ulist_base, (ulist_cnt - 1) * sizeof(ULIST_ITEM))) == NULL)
						{
							cprintf(chat, "* Memory reallocation error (%s%s%s)",
								S_FG_RED, strerror(errno), S_OFF);
							ulist_base = NULL;
							ulist_cnt = 0;
						}

						ulist_base = u;
						--ulist_cnt;

						break;
					}
				}
			}
			break;

			default:		break;
		}

		amount -= segment;

#if SUPPORT_ODD_ADDRS
		xsv = (VMSG *)((u_char *)xsv + segment);
#else
		if(amount)
			memmove(rcvbuf, &rcvbuf[segment], amount);
#endif
	}

#if SUPPORT_ODD_ADDRS
	if((u_char *)xsv > rcvbuf)
	{
		if(amount)
			memmove(rcvbuf, xsv, amount);

		rcvptr = &rcvbuf[amount];
	}
#else
	rcvptr = &rcvbuf[amount];
#endif

	/*
	 * Release vlock
	 */

	is_handler = 0;
}

void snd_serv(u_short cmd, char *data, size_t siz)
{
	snd_conn(sconn.fd, cmd, data, siz);
}

void snd_conn(int fd, u_short cmd, char *data, size_t siz)
{
#if HAVE_SYS_UIO_H
	struct iovec iov[2];

	VMSG svmsg;

	svmsg.cmd = htonl(cmd);

	iov[0].iov_base = (char *)&svmsg;
	iov[0].iov_len = sizeof(VMSG);

	if(data && siz)
	{
		svmsg.len = htonl(siz);
		iov[1].iov_base = data;
		iov[1].iov_len = siz;
		writev(fd, (struct iovec *)&iov, 2);
	}
	else
	{
		svmsg.len = htonl(0);
		writev(fd, (struct iovec *)&iov, 1);
	}
#else
	u_char	sndbuf[sizeof(VMSG) + siz];
	VMSG	*svmsg;
	char	*datbuf;

	svmsg = (VMSG *)&sndbuf;
	datbuf = (char *)&sndbuf[sizeof(VMSG)];

	svmsg->cmd = htonl(cmd);

	if(data && siz)
	{
		svmsg->len = htonl(siz);
		BCOPY(data, datbuf, siz);
		write(fd, (char *)&sndbuf, sizeof(VMSG) + siz);
	}
	else
	{
		svmsg->len = htonl(0);
		write(fd, (char *)&sndbuf, sizeof(VMSG));
	}
#endif
}

char *iptoname(struct in_addr ip)
{
	#define	SIZE 128

	static char namebuf[1024];
	static char *bufptr;
	static int index = 0;
	struct hostent *ht;

	if(++index > 7)
		index = 0;

	bufptr = (char *) &namebuf[(index * SIZE)];

	if((ht = gethostbyaddr((char *)&ip, sizeof(ip), AF_INET)) == NULL)
	{
		sprintf(bufptr, "%s", inet_ntoa(ip));
	}
	else
	{
		strcpy(bufptr, ht->h_name);
	}

	return(bufptr);
}


syntax highlighted by Code2HTML, v. 0.9.1