/*
File: read_data.c
Description: function to read data from a client or the server
*/
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "proxy_protos.h"
extern int errno;
void
read_data ( struct kevent *ke, struct mdata *md )
{
qdata *q;
int r;
int from;
int pn;
int fd;
float et;
struct kevent sk;
struct sockaddr_in sa;
socklen_t sl;
char *fr[2] = { "CLIENT", "SERVER" };
/*
if this is the first data we're reading over this connection,
record the start time.
*/
if ( md->r[ke->ident].bytes_read == 0 ) {
gettimeofday(&md->r[ke->ident].ti_read,NULL);
}
/* are we reading from the server or a client? */
from = md->r[ke->ident].type;
/* what's the relay file descriptor? */
r = md->r[ke->ident].relay;
/*
if we haven't read any data yet, and the server file descriptor
is still on status FD_CLOSE, we must be waiting for the connect()
to the server to finish. in order to find out if that connect()
actually did finish, let's call getpeername() and see if it works.
if it fails with errno = ENOTCONN, we're not connected yet.
*/
fd = (from == CLIENT) ? r : ke->ident;
if ( md->r[ke->ident].bytes_read == 0 &&
md->r[fd].status == FD_CLOSE ) {
sl = sizeof(sa);
pn = getpeername(fd,(struct sockaddr *)&sa,&sl);
gettimeofday(&md->r[fd].tf_connect,NULL);
if ( pn == -1 && errno == ENOTCONN ) {
/* we're still not connected. check how much time has passed */
et = diff_timeval(&md->r[fd].ti_connect,&md->r[fd].tf_connect);
if ( et > CONNECT_TIMEOUT ) {
/* we've been waiting too long. kill everything. */
log_msg(md,"Connect timeout exceeded on [fd %d]: %.3f seconds",fd,et);
close_connection(md,ke->ident);
close_connection(md,r);
}
/* go back to the event loop */
return;
} else {
/* we're connected. set the relay FD status to FD_OPEN */
md->r[fd].status = FD_OPEN;
log_msg(md,"Connect succeeded eventually on [fd %d]: "
"checked after %.6f seconds",
fd,diff_timeval(&md->r[fd].ti_connect,
&md->r[fd].tf_connect));
}
}
/* read from the file descriptor into a character buffer */
q = g_chunk_new(qdata,md->m);
/* check to see if we got the chunk OK */
if ( q == NULL ) {
/*
oops. no memory left. set a flag so we don't try and
accept any more connections until the situation eases.
*/
log_msg(md,"g_chunk_new failed (out of memory)");
md->out_of_memory = 1;
return;
} else if ( md->out_of_memory ) {
/*
great, we've got some memory back, so we can start accepting
connections again (hopefully).
*/
md->out_of_memory = 0;
}
q->l = read(ke->ident,q->s,QUEUE_BUFFER);
q->p = 0;
if ( q->l > 0 ) {
/* record the time */
gettimeofday(&md->r[ke->ident].tf_read,NULL);
if ( md->verbose ) {
log_msg(md,"[fd %d] read from %s [%d] [%d]",
ke->ident, fr[from], ke->data, q->l);
}
/* if data was actually read, record it. */
md->bytes_read[from] += q->l;
md->r[ke->ident].bytes_read += q->l;
}
/* is the relay connection still open? */
if ( md->r[r].status == FD_OPEN ) {
/*
yes, it is. append the data we just read to the relay queue
if the read was successful.
*/
if ( q->l >= 0 ) {
md->r[r].queue = g_slist_append(md->r[r].queue,q);
/* prepare to add a kevent for writing this data to the relay. */
sk.ident = md->r[ke->ident].relay;
sk.filter = EVFILT_WRITE;
sk.flags = EV_ADD | EV_ONESHOT;
sk.udata = write_data;
/* add the relay write event */
if ( kevent(md->kq,&sk,1,NULL,0,NULL) == -1 ) {
perror("kevent");
md->kevent_errors++;
}
} else {
/* read returned an error */
g_mem_chunk_free(md->m,q);
}
} else {
log_msg(md,"read_data from %s [%d]: relay to %s [%d] closed",
fr[from], ke->ident, fr[!from], r);
close_connection(md,ke->ident);
/* empty and free the relay queue. */
if ( md->r[r].queue ) {
g_slist_foreach(md->r[r].queue,chunk_free,md->m);
g_slist_free(md->r[r].queue);
md->r[r].queue = NULL;
}
}
/*
if the other end has closed the connection to us, and we
finished reading all the data from that end, we can
explicitly close that file descriptor and thereby remove
any further events on it.
*/
if ( ke->flags & EV_EOF &&
md->r[ke->ident].status == FD_OPEN &&
ke->data == q->l ) {
log_msg(md,"read_data from %s [%d] received EV_EOF: closing %d",
fr[from], ke->ident, ke->ident);
close_connection(md,ke->ident);
}
}
syntax highlighted by Code2HTML, v. 0.9.1