/* socket bouncer, by orabidoo  12 Feb '95 
   using code from mark@cairo.anu.edu.au's general purpose telnet server.
   Hacked by scot@poptart.org (April 1999) to allow a local bind address
   and syslog logging.
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>

#ifdef _AIX
#include <sys/select.h>
#endif

#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include <syslog.h>
#include <stdlib.h>
#include <string.h>

#define    QLEN           5
#define    DEFAULT_PORT   1523

char sbuf[16384], cbuf[16384];
int t=0;

void sigchld() {
  signal(SIGCHLD, sigchld);
  while(waitpid(0, (int *)0, WNOHANG|WUNTRACED)>=0);
}

void communicate(int sfd, int cfd) {
    char *chead, *ctail, *shead, *stail;
    int num, nfd, spos, cpos;

    extern int errno;
    fd_set rd, wr;

    struct itimerval itime;

    if (t)
    {
    itime.it_interval.tv_sec=0;
    itime.it_interval.tv_usec=0;
    itime.it_value.tv_sec=t;
    itime.it_value.tv_usec=0;
    setitimer(ITIMER_REAL,&itime,NULL);
    /* arbitrary connection time limit: 6 hours (in case the client hangs) */
    }

    chead=ctail=cbuf;
    cpos=0;
    shead=stail=sbuf;
    spos=0;
    while (1) {
        FD_ZERO(&rd);
        FD_ZERO(&wr);
        if (spos<sizeof(sbuf)-1) 
	    FD_SET(sfd, &rd);
        if (ctail>chead)
	    FD_SET(sfd, &wr);
        if (cpos<sizeof(cbuf)-1)
	    FD_SET(cfd, &rd);
        if (stail>shead)
	    FD_SET(cfd, &wr);
        nfd=select(256, &rd, &wr, 0, 0);
        if (nfd<=0) continue;
        if (FD_ISSET(sfd, &rd)) {
            num=read(sfd,stail,sizeof(sbuf)-spos);
            if ((num==-1) && (errno != EWOULDBLOCK)) return;
            if (num==0) {
#ifdef FNDELAY
		fcntl(sfd,F_SETFL,fcntl(sfd,F_GETFL,0)&~FNDELAY);
		fcntl(cfd,F_SETFL,fcntl(cfd,F_GETFL,0)&~FNDELAY);
#endif
		if (ctail!=chead) write(sfd,chead,ctail-chead);
		if (stail!=shead) write(cfd,shead,stail-shead);
		write(cfd,chead,0);
		return;
	    }
	    
            if (num>0) {
                spos+=num;
                stail+=num;
                if (!--nfd) continue;
            }
        }
        if (FD_ISSET(cfd, &rd)) {
            num=read(cfd,ctail,sizeof(cbuf)-cpos);
            if ((num==-1) && (errno != EWOULDBLOCK)) return;
            if (num==0) {
#ifdef FNDELAY
		fcntl(sfd,F_SETFL,fcntl(sfd,F_GETFL,0)&~FNDELAY);
		fcntl(cfd,F_SETFL,fcntl(cfd,F_GETFL,0)&~FNDELAY);
#endif
		if (ctail!=chead) write(sfd,chead,ctail-chead);
		if (stail!=shead) write(cfd,shead,stail-shead);
		write(sfd,chead,0);
		return;
	    }
		
            if (num>0) {
                cpos+=num;
                ctail+=num;
                if (!--nfd) continue;
            }
        }
        if (FD_ISSET(sfd, &wr)) {
            num=write(sfd,chead,ctail-chead);
            if ((num==-1) && (errno != EWOULDBLOCK)) return;
            if (num>0) {
                chead += num;
                if (chead==ctail) {
                    chead=ctail=cbuf;
                    cpos=0;
                }
                if (!--nfd) continue;
            }
        }
        if (FD_ISSET(cfd, &wr)) {
            num=write(cfd,shead,stail-shead);
            if ((num==-1) && (errno != EWOULDBLOCK)) return;
            if (num>0) {
                shead += num;
                if (shead==stail) {
                    shead=stail=sbuf;
                    spos=0;
                }
                if (!--nfd) continue;
            }
        }
    }
}

int main(int argc,char *argv[]) {
    int srv_fd, rem_fd, len, cl_fd, on=1, b=0, d=0;
    int myport=DEFAULT_PORT, remoteport;
    struct sockaddr_in rem_addr, srv_addr, cl_addr;
    char *myname;
    struct hostent *hp, *hpLocal;

    extern char *optarg;
    extern int optind;
    char *hostname = NULL;
    char ch;

    myname=argv[0];

    /* Process arguments */

    while( (ch = getopt(argc, argv, "p:a:b:dt:")) != -1  ) {
      switch(ch) { 
      case 'b': b = 1;
      case 'd': d = 1; break;
      case 'a':
	hostname = malloc( strlen(optarg) + 1);
	if( !hostname ) {
	  fprintf( stderr, "Can't allocate memory!\n" );
	  exit(-1);
	}
	strcpy( hostname, optarg );
	break;

      case 'p':
	if ((myport=atoi(optarg))==0) {
	  fprintf(stderr,"Bad port number.\n");
	  exit(-1);
	}
	break;
      case 't':
	if ((t=atoi(optarg)) == 0)  {
	  fprintf(stderr, "Bad timer value.\n");
	  exit(-1);
	}
	break;
      }
    }

    argc -= optind;
    argv += optind;

    if (argc!=2) {
	fprintf(stderr,"Use: %s [-a localaddr | -b localaddr] [-d] [-p localport] [-t timer] machine port \n",myname);
	exit(-1);
    }
    if ((remoteport=atoi(argv[1]))<=0) {
	fprintf(stderr, "Bad remote port number.\n");
	exit(-1);
    }

    memset((char *) &rem_addr, 0, sizeof(rem_addr));
    memset((char *) &srv_addr, 0, sizeof(srv_addr));
    memset((char *) &cl_addr, 0, sizeof(cl_addr));

    cl_addr.sin_family=AF_INET;
    cl_addr.sin_port=htons(remoteport);
    if ((hp=gethostbyname(argv[0]))==NULL) {
	cl_addr.sin_addr.s_addr=inet_addr(argv[0]);
	if (cl_addr.sin_addr.s_addr==-1) {
	    fprintf(stderr, "Unknown host.\n");
	    exit(-1);
	}
    } else
	cl_addr.sin_addr=*(struct in_addr *)(hp->h_addr_list[0]);

    if( hostname ) {
      if ((hpLocal=gethostbyname(hostname))==NULL) {
	srv_addr.sin_addr.s_addr=inet_addr(hostname);
	if (srv_addr.sin_addr.s_addr==-1) {
	    fprintf(stderr, "Unknown host: %s\n", hostname);
	    exit(-1);
	}
    } else
	srv_addr.sin_addr=*(struct in_addr *)(hp->h_addr_list[0]);
    }

    srv_addr.sin_family=AF_INET;
    /*    srv_addr.sin_addr.s_addr=htonl(INADDR_ANY); */
    srv_addr.sin_port=htons(myport);
    srv_fd=socket(PF_INET,SOCK_STREAM,0);
    if (bind(srv_fd,(struct sockaddr *)&srv_addr,sizeof(srv_addr))==-1) {
	perror("bind");
        exit(-1);
    }
    listen(srv_fd,QLEN);
    srv_addr.sin_port=0;

    signal(SIGCHLD, sigchld);
    printf("Ready to bounce connections from port %i to %s on port %i\n",
	   myport, argv[0], remoteport);
    close(0); close(1); close(2);
    chdir("/");
#ifdef TIOCNOTTY
    if ((rem_fd=open("/dev/tty", O_RDWR)) >= 0) {
        ioctl(rem_fd, TIOCNOTTY, (char *)0);
        close(rem_fd);
    }
#endif
    if (fork()) exit(0);
    while (1) {
	len=sizeof(rem_addr);
	rem_fd=accept(srv_fd,(struct sockaddr *)&rem_addr,&len);
	if (rem_fd<0) {
	  if (errno==EINTR) continue;
	  exit(-1);
        }
	syslog( LOG_NOTICE, "connection from %s to local port %i.  Bouncing to %s, %i",
		inet_ntoa(rem_addr.sin_addr), myport, argv[0], remoteport );
	switch(fork()) {
	  case -1:
	    /* we're in the background.. no-one to complain to */
	    break;
	  case 0:                           /* child process */
	    close(rem_fd);
	    break;
	  default:			    /* parent process */
	    close(srv_fd);                  /* close original socket */
	    if ((cl_fd=socket(PF_INET, SOCK_STREAM, 0))<0) {
		close(rem_fd);
		exit(-1);
	    }
	    if (b) {
	    if (bind(cl_fd,(struct sockaddr *)&srv_addr,sizeof(srv_addr))<0) {
		close(rem_fd);
		exit(-1);
	    }
	    }
	    if (d) {
	    if ((hp=gethostbyname(argv[0]))!=NULL) {
		cl_addr.sin_addr=*(struct in_addr *)(hp->h_addr_list[0]);
	    }
	    }
	    if (connect(cl_fd, (struct sockaddr *)&cl_addr, 
	                sizeof(cl_addr))<0) {
		close(rem_fd);
		exit(-1);
	    }
	    
	    setsockopt(cl_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
	    setsockopt(rem_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));

	    communicate(cl_fd, rem_fd);
	    close(rem_fd);
	    exit(0);
	}
    }
}



syntax highlighted by Code2HTML, v. 0.9.1