/* connect.c

   Connect the modem to a TCP port... */

/*
 * Copyright (c) 1995 RadioMail Corporation.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of RadioMail Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY RADIOMAIL CORPORATION AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * RADIOMAIL CORPORATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software was written for RadioMail Corporation by Ted Lemon
 * under a contract with Vixie Enterprises, and is based on an earlier
 * design by Paul Vixie.
 */

#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1995 RadioMail Corporation.  All rights reserved.\n";
#endif /* not lint */

#include "cdefs.h"
#include "osdep.h"
#include "global.h"
#include "mcap.h"

#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/resource.h>

static void pump PROTO ((int, int));

int tcp_connect (tty)
     int tty;
{
  struct sockaddr_in name;
  int connectSock;
  struct linger linger;
  struct hostent *hent;
  struct servent *se;
  int pids [2], i, wait_stat, wpid;
  sigset_t sigmask;

  /* Make sure FNDELAY is clear... */
  if (fcntl (tty, F_SETFL, 0) < 0)
    error ("Unable to clear FNDELAY: %m");

  /* Enable HUPCL... */
  tthupcl (tty);

  /* Make the modem our controlling terminal... */
  ttset_ctty (tty);

  /* Figure out where to connect... */
  name.sin_family = AF_INET;

  /* Allow numeric or symbolic port names... */
  if (se = getservbyname (modemcap.connect_port, "tcp"))
    name.sin_port = se -> s_port;
  else
    name.sin_port = htons (atoi (modemcap.connect_port));

  /* Allow numeric or symbolic host addresses... */
  if (!inet_aton (modemcap.connect_addr, &name.sin_addr))
    {
      hent = gethostbyname (modemcap.connect_addr);
      if (!hent || hent -> h_addrtype != AF_INET)
	error ("Unable to translate %s into an internet address.",
	       modemcap.connect_addr);
      if (hent -> h_addr_list [0] == 0)
	error ("%s resolves to zero addresses.", modemcap.connect_addr);
      memcpy (&name.sin_addr, hent -> h_addr_list [0], hent -> h_length);
    }
  memset (name.sin_zero, 0, sizeof (name.sin_zero));

  syslog (LOG_INFO, "Connecting to %s, port %d\n",
	  modemcap.connect_addr, ntohs (name.sin_port));

  /* Make a socket; turn on SO_LINGER so that all the data gets delivered. */
  if ((connectSock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    error ("Can't create socket: %m");
  linger.l_onoff = 1;
  linger.l_linger = 10;
  if (setsockopt (connectSock,
		  SOL_SOCKET, SO_LINGER, &linger, sizeof linger) < 0)
    error ("Can't set SO_LINGER option: %m");

  /* Try to connect to the requested address... */
  if (connect (connectSock,
	       (struct sockaddr *)&name, sizeof name) < 0)
    error ("Connect failed: %m");

  for (i = 0; i < 2; i++)
    {
      if ((pids [i] = fork ()) < 0)
	error ("Can't fork: %m");
      else if (!pids [i])
	{
	  if (i)
	    pump (tty, connectSock);
	  else
	    pump (connectSock, tty);
	  exit (0);
	}
    }

  /* Wait for the session to end... */
  do {
    extern int hung_up, exited;
    sigemptyset (&sigmask);
    sigsuspend (&sigmask);
    if (exited)
      {
	int status;
	int wpid;
	do {
	  wpid = wait3 (&status, WNOHANG, (struct rusage *)0);
	  if (pids [0] == wpid)
	    {
	      syslog (LOG_DEBUG, "Child 0 exited.");
	      pids [0] = 0;
	      if (pids [1])
		kill (pids [1], SIGTERM);
	    }
	  else if (pids [1] == wpid)
	    {
	      syslog (LOG_DEBUG, "Child 1 exited.");
	      pids [1] = 0;
	      if (pids [0])
		kill (pids [0], SIGTERM);
	    }
	} while (wpid > 0);
	if (pids [0] || pids [1])
	  exited = 0;
      }
    if (hung_up)
      {
	if (pids [0])
	  kill (pids [0], SIGTERM);
	if (pids [1])
	  kill (pids [1], SIGTERM);
      }
    if (exited)
      {
	syslog (LOG_INFO, "Disconnected.");
	cleanup ();
	return 0;
      }
  } while (1);
}

/* Pump data from one descriptor to another. */

static void pump (in, out)
     int in, out;
{
  char ibuf [512];
  int rstatus, wstatus;
  int wcount;

  do {
    rstatus = read (in, ibuf, sizeof ibuf);
    if (rstatus <= 0)
      return;
    wcount = 0;
    do {
      wstatus = write (out, ibuf, rstatus);
      wcount += wstatus;
    } while (wstatus > 0 && wcount < rstatus);
  } while (wstatus > 0);
}
