/******************************************************************************
* 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