#include "kip.h"

RCSID("$Id: common.c,v 1.5 1996/05/15 15:07:38 assar Exp $");

/*
 * Copy packets from `tundev' to `netdev' or vice versa.
 * Mode is used when reading from `tundev'
 */

int
copy_packets (int tundev, int netdev, int mtu, des_cblock *iv,
	      des_key_schedule schedule)
{
     des_cblock iv1, iv2;
     int num1 = 0, num2 = 0;
     u_char *buf;

     buf = malloc (mtu + 2);
     if (buf == NULL) {
	  fprintf (stderr, "%s: malloc(%d) failed\n", progname, mtu);
	  return 1;
     }

     memcpy (&iv1, iv, sizeof(iv1));
     memcpy (&iv2, iv, sizeof(iv2));
     for (;;) {
	  fd_set fdset;
	  int ret, len;

	  FD_ZERO(&fdset);
	  FD_SET(tundev, &fdset);
	  FD_SET(netdev, &fdset);

	  ret = select (max(tundev, netdev)+1, &fdset, NULL, NULL, NULL);
	  if (ret < 0 && errno != EINTR) {
	       fprintf (stderr, "%s: select: %s\n", progname,strerror (errno));
	       return 1;
	  }
	  if (FD_ISSET(tundev, &fdset)) {
	       ret = read (tundev, buf + 2, mtu);
	       if (ret == 0)
		    return 0;
	       if (ret < 0) {
		    if (errno == EINTR)
			 continue;
		    else { 
			 fprintf (stderr, "%s: read: %s\n", progname,
				  strerror (errno));
			 return ret;
		    }
	       }
	       buf[0] = ret >> 8;
	       buf[1] = ret & 0xFF;
	       ret += 2;
	       des_cfb64_encrypt (buf, buf, ret, schedule,
				  &iv1, &num1, DES_ENCRYPT);
	       ret = krb_net_write (netdev, buf, ret);
	       if (ret < 0) {
		    fprintf (stderr, "%s: write: %s\n", progname,
			     strerror (errno));
		    return ret;
	       }
	  }
	  if (FD_ISSET(netdev, &fdset)) {
	       ret = read (netdev, buf, 2);
	       if (ret == 0)
		    return 0;
	       if (ret < 0) {
		    if (errno == EINTR)
			 continue;
		    else { 
			 fprintf (stderr, "%s: read: %s\n", progname,
				  strerror (errno));
			 return ret;
		    }
	       }
	       des_cfb64_encrypt (buf, buf, 2, schedule,
				  &iv2, &num2, DES_DECRYPT);
	       len = (buf[0] << 8 ) | buf[1];
	       ret = krb_net_read (netdev, buf + 2, len);
	       if (ret == 0)
		    return 0;
	       if (ret < 0) {
		    if (errno == EINTR)
			 continue;
		    else { 
			 fprintf (stderr, "%s: read: %s\n", progname,
				  strerror (errno));
			 return ret;
		    }
	       }
	       des_cfb64_encrypt (buf + 2, buf + 2, len, schedule,
				  &iv2, &num2, DES_DECRYPT);
	       ret = krb_net_write (tundev, buf + 2, len);
	       if (ret < 0) {
		    fprintf (stderr, "%s: write: %s\n", progname,
			     strerror(errno));
		    return ret;
	       }
	  }
     }
}

/*
 * Signal handler that justs waits for the children when they die.
 */

RETSIGTYPE
childhandler (int sig)
{
     pid_t pid;
     int status;

     do { 
	  pid = waitpid (-1, &status, WNOHANG|WUNTRACED);
     } while(pid > 0);
     signal (SIGCHLD, childhandler);
}

/*
 * Find a free tunnel device and open it.
 */

int
tunnel_open ()
{
     int fd;
     int i;
     char name[64];

     for (i = 0; i < 256; ++i) {
	  sprintf (name, "%s%s%d", _PATH_DEV, TUNDEV, i);
	  fd = open (name, O_RDWR, 0);
	  if (fd >= 0)
	       break;
	  if (errno == ENOENT || errno == ENODEV) {
	       fprintf (stderr, "%s: open %s: %s\n", progname, name,
			strerror(errno));
	       return fd;
	  }
     }
     if (fd < 0)
	  fprintf (stderr, "%s: open %s: %s\n", progname, name,
		   strerror(errno));
     return fd;
}
