/* Distribution: Public */
/* Copyright: Held by the respective contributors */
/* Posted to USENET September '93 by Mark mark@cairo.anu.edu.au */

/* This file was telserv.c, part of the Telnet Server package v. 1.0,
     written by "Hal-9000". Much of that package was developed by Richard
     Stephens and his thanks go to "Xanadude" for providing him with that
     section. Performance fix by Darren Reed. */

/* Reworked to add concurrency, password checking and destination selection
   on the fly. - Mark 31st Aug 93

   Now its a IRC bouncer - riley Nov 93.

   Compiled and tested on:
       HPUX 9.01 9000/700 series        NeXTStep 3.1 NeXT 68040
    OSx Pyramid 90x BSD universe     SunOS 5.2 sun4c
    Ultrix 4.3 DEC RISC

   To compile, type "cc -O -s ts2.c -o ts2".
*/
 

/*+  IRC Bouncer hacks  +*/

#define IRCSERV "irc.lleida.net"
#define IRCPORT 6667
#define IRCBNC 9999

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>

#define    QLEN           5

char sbuf[2048], cbuf[2048];
extern int errno;
extern char *sys_errlist[];
void reaper();
int main();
void telcli();

int main(argc, argv)
int argc;
char *argv[];
{
    int srv_fd, rem_fd, rem_len, opt = 1;
    struct sockaddr_in rem_addr, srv_addr;
#if !defined(SVR4) && !defined(POSIX) && !defined(linux) && !defined(__386BSD__) && !defined(hpux)
    union wait status;
#else
    int    status;
#endif /* !defined(SVR4) */

    bzero((char *) &rem_addr, sizeof(rem_addr));
    bzero((char *) &srv_addr, sizeof(srv_addr));
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    srv_addr.sin_port = htons(IRCBNC);  /*+ IRC Bouncer hack  +*/
    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);
    close(0); close(1); close(2);
#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) {
    rem_len = sizeof(rem_addr);
        rem_fd=accept(srv_fd, (struct sockaddr *) &rem_addr, &rem_len);
        if (rem_fd < 0) {
            if (errno == EINTR) continue;
            exit(-1);
        }
        switch(fork()) {
        case 0:                             /* child process */
            close(srv_fd);                  /* close original socket */
            telcli(rem_fd);                 /* process the request */
            close(rem_fd);
            exit(0);
            break;
        default:
            close(rem_fd);                  /* parent process */
            if (fork()) exit(0);            /* let init worry about children */
            break;
        case -1:
            fprintf(stderr, "\n\rfork: %s\n\r", sys_errlist[errno]);
            break;
        }
    }
}

void telcli(source)
int source;
{
    int dest;
    int found;
    struct sockaddr_in sa;
    struct hostent *hp;
    struct servent *sp;
    char gethost[100];
    char getport[100];
    char string[100];

        hp = gethostbyname(IRCSERV);
        if (hp) {
            found++;
#if !defined(h_addr)        /* In 4.3, this is a #define */
#if defined(hpux) || defined(NeXT) || defined(ultrix) || defined(POSIX)
            memcpy((caddr_t)&sa.sin_addr, hp->h_addr_list[0], hp->h_length);
#else
            bcopy(hp->h_addr_list[0], &sa.sin_addr, hp->h_length);
#endif
#else /* defined(h_addr) */
#if defined(hpux) || defined(NeXT) || defined(ultrix) || defined(POSIX)
            memcpy((caddr_t)&sa.sin_addr, hp->h_addr, hp->h_length);
#else
            bcopy(hp->h_addr, &sa.sin_addr, hp->h_length);
#endif
#endif /* defined(h_addr) */
        } else {
            if (inet_addr(gethost) == -1) {
                found = 0;
                sprintf(string, "Didnt find address for %s\r\n", gethost);
                write(source, string, strlen(string));
            } else {
                found++;
                sa.sin_addr.s_addr = inet_addr(gethost);
            }
        }
    sa.sin_family = AF_INET;
    sa.sin_port = htons((unsigned) IRCPORT);
    if (sa.sin_port == 0) {
        sp = getservbyname(getport, "tcp");
        if (sp)
            sa.sin_port = sp->s_port;
        else {
            sprintf(string, "%s: bad port number\r\n", getport);
            write(source, string, strlen(string));
            return;
        }
    }
    if ((dest = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("telcli: socket");
        exit(1);
    }
    connect(dest, (struct sockaddr *) &sa, sizeof(sa));
#ifdef FNDELAY
    fcntl(source,F_SETFL,fcntl(source,F_GETFL,0)|FNDELAY);
    fcntl(dest,F_SETFL,fcntl(dest,F_GETFL,0)|FNDELAY);
#else
    fcntl(source,F_SETFL,O_NDELAY);
    fcntl(dest,F_SETFL,O_NDELAY);
#endif
    communicate(dest,source);
    close(dest);
    exit(0);
}

communicate(sfd,cfd)    {
    char *chead, *ctail, *shead, *stail;
    int num, nfd, spos, cpos;
    extern int errno;
    fd_set rd, wr;

    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) 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) 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;
            }
        }
    }
}