/*
 * transfer.c
 * The data I/O engine.
 *
 * Copyright (c) 2002 Christoph Pfisterer
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */

#include "netstrain.h"
#include <fcntl.h>

/* settings */
#define BUFSIZE (65536)
#define SENDMULT (1)

#define SENDBUFSIZE (BUFSIZE*(SENDMULT+1))
#define SENDBUFWRAP (BUFSIZE*SENDMULT)

/* internal data */
static unsigned char *sendbuf = NULL;
static unsigned char *recvbuf = NULL;

static counter_t sendcount = 0;
static counter_t recvcount = 0;
static int sendoff = 0;

static void (*hook)(counter_t, counter_t) = NULL;


void transfer_init(void)
{
  int i;

  if (recvbuf != NULL)
    return;

  sendbuf = (unsigned char *)malloc(SENDBUFSIZE);
  recvbuf = (unsigned char *)malloc(BUFSIZE);

  if (sendbuf == NULL || recvbuf == NULL)
    bailout("malloc buffers");

  srand(time(NULL));
  for (i = 0; i < SENDBUFWRAP; i++) {
    sendbuf[i] = (unsigned char)(256.0 * rand() / (RAND_MAX+1.0));
  }
  for (; i < SENDBUFSIZE; i++) {
    sendbuf[i] = sendbuf[i - SENDBUFWRAP];
  }
  sendoff = 0;
}

void transfer_hook(void (*hookfunc)(counter_t, counter_t))
{
  hook = hookfunc;
}

void transfer_run(int sock, int send)
{
  int ret, got;
  fd_set rfds;
  fd_set wfds;

  transfer_init();

  if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
    bailout("fcntl");

  for(;;) {
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);
    FD_SET(sock, &rfds);
    if (send) {
      FD_ZERO(&wfds);
      FD_SET(sock, &wfds);
    }

    ret = select(sock + 1, &rfds, send ? &wfds : NULL, NULL, NULL);
    if (ret < 0 && errno != EINTR)
      bailout("select");

    /* read from console, looking for EOF */
    if (FD_ISSET(0, &rfds)) {
      got = read(0, recvbuf, 1);
      if (got == 0) {
	printf("Got EOF from console, shutting down\n");
	break;
      } else if (got < 0) {
	if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
	  bailout("read");
	}
      }
    }

    /* read from socket, discarding all data */
    if (FD_ISSET(sock, &rfds)) {
      got = read(sock, recvbuf, BUFSIZE);
      if (got == 0) {
	printf("Got EOF from socket, shutting down\n");
	break;
      } else if (got < 0) {
	if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
	  bailout("read");
	}
      } else {
	recvcount += got;
	if (hook)
	  (*hook)(sendcount, recvcount);
      }
    }

    /* write garbage to socket */
    if (send && FD_ISSET(sock, &wfds)) {
      got = write(sock, sendbuf + sendoff, BUFSIZE);
      if (got < 0) {
	if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
	  bailout("write");
	}
      } else if (got > 0) {
	sendcount += got;
	sendoff = (sendoff + got) % SENDBUFWRAP;
	if (hook)
	  (*hook)(sendcount, recvcount);
      }
    }
  }

  if (close(sock) < 0)
    bailout("close");
}


syntax highlighted by Code2HTML, v. 0.9.1