/*
* readwrite.c - stream i/o reading/writing loop - implementation
*
* nc6 - an advanced netcat clone
* Copyright (C) 2001-2006 Mauro Tortonesi <mauro _at_ deepspace6.net>
* Copyright (C) 2002-2006 Chris Leishman <chris _at_ leishman.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "system.h"
#include "readwrite.h"
#include "misc.h"
#include "circ_buf.h"
#include "parser.h"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
RCSID("@(#) $Header: /ds6/cvs/nc6/src/readwrite.c,v 1.44 2006/01/19 22:46:23 chris Exp $");
/* ios1 is the remote stream, ios2 the local one */
int readwrite(io_stream_t *ios1, io_stream_t *ios2)
{
int rr, max_fd = -1;
int ios1_read_fd, ios1_write_fd;
int ios2_read_fd, ios2_write_fd;
fd_set read_fdset, write_fdset;
struct timeval tv1, tv2;
struct timeval *tvp1, *tvp2, *tvp;
bool timedout1 = false, timedout2 = false;
int retval = 0;
/* check function arguments */
assert(ios1 != NULL);
assert(ios2 != NULL);
/* setup all the stuff for the select loop */
/* here's the select loop.
*
* the loop continues until one of the following occurs:
*
* neither side needs to read or write
* OR
* either side times out
* OR
* a write error occurs
*
* note: by default, when the remote read side (ios1) is closed, it
* triggers a hold timeout immediately - which closes the local read
* side as well.
*/
for (;;) {
/* setup fdsets */
FD_ZERO(&read_fdset);
FD_ZERO(&write_fdset);
ios1_read_fd = ios_schedule_read(ios1);
ios1_write_fd = ios_schedule_write(ios1);
ios2_read_fd = ios_schedule_read(ios2);
ios2_write_fd = ios_schedule_write(ios2);
max_fd = -1;
if (ios1_read_fd >= 0) {
FD_SET(ios1_read_fd, &read_fdset);
max_fd = ios1_read_fd;
}
if (ios1_write_fd >= 0) {
FD_SET(ios1_write_fd, &write_fdset);
max_fd = MAX(ios1_write_fd, max_fd);
}
if (ios2_read_fd >= 0) {
FD_SET(ios2_read_fd, &read_fdset);
max_fd = MAX(ios2_read_fd, max_fd);
}
if (ios2_write_fd >= 0) {
FD_SET(ios2_write_fd, &write_fdset);
max_fd = MAX(ios2_write_fd, max_fd);
}
/* stop loop if nothing is to be read or written */
if (max_fd == -1)
break;
/* check timeouts */
tvp1 = tvp2 = NULL;
if (!timedout1) {
tvp1 = ios_next_timeout(ios1, &tv1);
/* handle timeouts */
if (ios_idle_timedout(ios1)) {
/* stop the readwrite loop */
retval = -1;
break;
}
if (ios_hold_timedout(ios1)) {
/* stop reading from the other endpoint */
ios_shutdown(ios2, SHUT_RD);
/* stop sending to this endpoint */
ios_shutdown(ios1, SHUT_WR);
timedout1 = true;
continue;
}
}
if (!timedout2) {
tvp2 = ios_next_timeout(ios2, &tv2);
/* handle timeouts */
if (ios_idle_timedout(ios2)) {
/* stop the readwrite loop */
retval = -1;
break;
}
if (ios_hold_timedout(ios2)) {
/* stop reading from the other endpoint */
ios_shutdown(ios1, SHUT_RD);
/* stop sending to this endpoint */
ios_shutdown(ios2, SHUT_WR);
timedout2 = true;
continue;
}
}
/* select smallest timeout for select */
if (tvp1 != NULL) {
if (tvp2 != NULL)
tvp = timercmp(tvp1, tvp2, <) ? tvp1 : tvp2;
else
tvp = tvp1;
} else {
tvp = tvp2; /* tvp2 may be NULL */
}
/* blocking select with timeout */
rr = select(max_fd + 1, &read_fdset, &write_fdset, NULL, tvp);
/* handle select errors.
* if errno == EINTR we just retry select */
if (rr < 0) {
if (errno == EINTR)
continue;
fatal("select error: %s", strerror(errno));
}
if (ios1_read_fd >= 0 && FD_ISSET(ios1_read_fd, &read_fdset)) {
/* ios1 is ready to read */
rr = ios_read(ios1);
if (rr < 0) {
if (rr == IOS_EOF) {
ios_write_eof(ios2);
} else {
/* something bad happened -
* exit the main loop */
retval = -1;
break;
}
}
}
if (ios2_read_fd >= 0 && FD_ISSET(ios2_read_fd, &read_fdset)) {
/* ios2 is ready to read */
rr = ios_read(ios2);
if (rr < 0) {
if (rr == IOS_EOF) {
ios_write_eof(ios1);
} else {
/* something bad happened -
* exit the main loop */
retval = -1;
break;
}
}
}
if (ios1_write_fd >= 0 && FD_ISSET(ios1_write_fd, &write_fdset))
{
/* ios1 is ready to write */
rr = ios_write(ios1);
if (rr < 0) {
/* write failed -
* exit the main loop */
retval = -1;
break;
}
}
if (ios2_write_fd >= 0 && FD_ISSET(ios2_write_fd, &write_fdset))
{
/* ios2 is ready to write */
rr = ios_write(ios2);
if (rr < 0) {
/* write failed -
* exit the main loop */
retval = -1;
break;
}
}
}
return retval;
}
syntax highlighted by Code2HTML, v. 0.9.1