RCS_ID_C= "$Id: letnet.c,v 1.9 1993/11/01 08:10:43 ppessi Exp $";
/*
 * letnet.c --- a tcp filter for Amiga
 *
 * Author: Pekka Pessi <Pekka.Pessi@hut.fi>
 *
 * Copyright © 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>,
 *                  Helsinki University of Technology, Finland.
 *                  All rights reserved.
 *
 * Created      : Tue Mar 23 20:10:33 1993 ppessi
 * Last modified: Mon Nov  1 10:10:32 1993 ppessi
 *
 * $Log: letnet.c,v $
 * Revision 1.9  1993/11/01  08:10:43  ppessi
 * Fixed bug with ObtainSocket()
 *
 */

static const char version[] = "$VER: letnet 2.0 (13.8.93)";

char copyright[] =
     "Copyright © 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>\n"
                      "Helsinki University of Technology, Finland.\n"
                      "All rights reserved.\n";

/****** netutil.doc/letnet **************************************************
*
*  NAME
*       Letnet - a simple TCP connection tool
*
*  SYNOPSIS
*       letnet HOSTNAME/A,PORT/A
*
*  DESCRIPTION
*       Letnet connects to the specified TCP port at the specified host. The
*       data read from standard input is sent to the host and data received
*       from the connection is written to the standard output. Letnet
*       terminates upon shutdown of the socket or receiving SIGBREAKF_CTRL_C
*       signal.
*
*  ARGUMENTS
*        HOSTNAME/A
*               If there is no name service available, hostname may be given
*               in the Internet dot notation.
*
*        PORT/A
*               The port identifier is searched from the standard services
*               (SEE ALSO netdb/services) database. A nonstandard
*               service port may be specified in the numeric form, numbers
*               between 1---65535 are acceptable. 
*
*  AUTHOR
*       Pekka Pessi, the AmiTCP/IP Group, Helsinki University of Technology
*
*  SEE ALSO
*       netdb/services, netdb/hosts       
*
*****************************************************************************
*
*/

#ifdef AMIGA
#if __SASC
#include <proto/socket.h>
#include <proto/dos.h>
#include <clib/exec_protos.h>
#include <pragmas/exec_sysbase_pragmas.h>
#elif __GNUC__
#include <inline/socket.h>
#include <inline/exec.h>
#else
#include <clib/socket_protos.h>
#endif
#define ioctl IoctlSocket
#endif /* AMIGA */

#include <errno.h>
#include <netdb.h>

#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>

#include <signal.h>

#include <dos/dos.h>
#include <dos/dostags.h>
#include <exec/execbase.h>

#include <stdlib.h>

#include "letnet.h"

extern struct ExecBase *SysBase;

BPTR Stdin = NULL;
BPTR Stdout = NULL;
BPTR Stderr = NULL;

struct MsgPort *dadp = NULL; 
struct SocketMessage *exitm = NULL; 

void
_STIdosStdio(void)
{
  struct Process *p = (struct Process *)SysBase->ThisTask;

  Stdin = p->pr_CIS;
  Stdout = p->pr_COS;
  Stderr = p->pr_CES ? p->pr_CES : Stdout;
}

void
free_ports(void)
{
  if (exitm) DeleteIORequest(exitm);
  exitm = NULL;

  if (dadp) DeleteMsgPort(dadp);
  dadp = NULL;
}

int
wait_sender(void) {
  do {
    WaitPort(dadp);
  } while (GetMsg(dadp) != exitm);
  return exitm->sm_retval;
}

void
stop_sender(struct Process * sender_pid)
{
  Forbid();
  /* Check that sender task is still around */
  /* i.e. the exitm Message is not replied */
  if (sender_pid && dadp->mp_MsgList.lh_Head != exitm)
    Signal((struct Task *)sender_pid, SIGBREAKF_CTRL_C);
  Permit();
}

struct Process *
start_sender(int s)
{
  struct Process *pid = NULL;

  dadp = CreateMsgPort();
  exitm = CreateIORequest(dadp, sizeof(*exitm));

  if (!exitm || !dadp) {
    FPrintf(Stderr, "letnet: memory or signals exhausted\n");
    return NULL;
  }

  if ((exitm->sm_id = ReleaseCopyOfSocket(s, -1L)) == -1) {
    PrintNetFault(Errno(), "ReleaseCopyOfSocket");
    return NULL;
  }

  pid = CreateNewProcTags(NP_Entry, do_sender,
			  NP_Name, "sender",
			  NP_Output, Stderr,
			  NP_CloseOutput, FALSE,
			  NP_Input, Stdin,
			  NP_CloseInput, FALSE,
			  NP_ExitCode, exitcode,
			  NP_ExitData, exitm,
			  TAG_END, NULL);
  if (pid == 0) {
    /* We should not leave orphan sockets into the internal list */
    (void)ObtainSocket((LONG)exitm->sm_id, PF_INET, SOCK_STREAM, NULL);
  }

  return pid;
}

int
do_receiver(int s)
{
  int n, m, retval = 0;
  char *recb = malloc(RECBUFLEN);

  if (!recb) {
    PrintNetFault(Errno(), "buffer malloc");
    retval = 0; goto Return;
  }

  while (recv(s, recb, 1, 0) == 1) {
    if (SetSignal(0L, 0L) & SIGBREAKF_CTRL_C) {
      retval = 128; goto Return;
    }
    if (Write(Stdout, recb, 1) == -1) {
      PrintFault(IoErr(), "Write");
      retval = 20; goto Return;
    }
    if (ioctl(s, FIONREAD, (char *)&n)) {
      PrintNetFault(Errno(), "FIONREAD");
      retval = 1; goto Return;
    }
    while (n > 0) {
      m = recv(s, recb, n > RECBUFLEN ? RECBUFLEN : n, 0);
      if (m == -1) {
	PrintNetFault(Errno(), "recv");
	retval = 1; goto Return;
      }
      if (Write(Stdout, recb, m) != m) {
	PrintFault(IoErr(), "Write");
	retval = 1; goto Return;
      }
      n -= m;
    }
  }

 Return:
  shutdown(s, 0);		/* no more receiving */
  return retval;
}

int
main()
{
  struct sockaddr_in addr;
  struct hostent *hp;
  struct servent *sp;
  long val;
  unsigned long port;
  int s;
  struct Process *child; 
  int retval = 0, retval2 = 0;
  LONG args[3] = { 0, 0, 0};
  struct RDArgs *rdargs = NULL;

  signal(SIGINT, SIG_IGN);

  atexit(free_ports);

  rdargs = ReadArgs("HOST/A,PORT/A", args, NULL);
  if (!rdargs) {
    PrintFault(IoErr(), "letnet");
    exit(10);
  }

  addr.sin_len = sizeof(addr);
  addr.sin_family = AF_INET;
  addr.sin_port = 0;
  addr.sin_addr.s_addr = 0;

  if ((val = inet_addr((char *)args[0])) != -1)
    addr.sin_addr.s_addr = val;
  else if (hp = gethostbyname((char *)args[0]))
    bcopy(hp->h_addr, (char *)&addr.sin_addr, hp->h_length);
  else {
    FPrintf(Stderr, "%s: unknown host\n", (char *)args[0]);
    FreeArgs(rdargs);
    exit(1);
  }

  /* A port must be in the range 1 - 65535 */
  if ((port = strtoul((char *)args[1], NULL, 0)) && port < 65536 )
    addr.sin_port = port;
  else if (sp = getservbyname((char *)args[1], "tcp"))
    addr.sin_port = sp->s_port;
  else {
    FPrintf(Stderr, "%s: unknown port\n", (char *)args[1]);
    FreeArgs(rdargs);
    exit(1);
  }

  FreeArgs(rdargs); rdargs = NULL;

  s = socket(PF_INET, SOCK_STREAM, NULL);

  if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
    PrintNetFault(Errno(), "connect");
    exit(1);
  }

  if (child = start_sender(s)) {
    retval = do_receiver(s);
    stop_sender(child);
    retval2 = wait_sender();
  } else {
    FPrintf(Stderr, "letnet: couldn't start sender process\n");
  }
  if (!retval)
    retval = retval2;

  exit(retval);
}
