/* 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 #include #include #include #include #ifdef _AIX #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #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 (sposchead) FD_SET(sfd, &wr); if (cposshead) 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); } } }