/*
NNTP authentication proxy by Lasse L. Johnsen

Based on FreeBSD bounce port by obrien@FreeBSD.org and others
*/

#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   119

char sbuf[16384], cbuf[16384];

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;

    itime.it_interval.tv_sec=0;
    itime.it_interval.tv_usec=0;
    itime.it_value.tv_sec=21600;
    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;
    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;

    /* NNTP vars */

	FILE *nntp_fi, *nntp_fo;
	char *lineptr, *fuser, *fpass, **cap, *ca[3];
	size_t ilen = 1;
	int cnt = 0, usergiven = 0, passgiven = 0;

    /* Ends */

    myname=argv[0];



    /* Process arguments */

    while( (ch = getopt(argc, argv, "p:a:")) != -1  ) {
      switch(ch) { 
      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;
      }
    }

    argc -= optind;
    argv += optind;

    if (argc!=4) {
	fprintf(stderr,"Use: %s [-a localaddr] [-p localport] machine port username password\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);

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

        /* This is where the NNTP authentication goes on */
		if(rem_fd  > -1){
           nntp_fi = fdopen(rem_fd, "r");
		   nntp_fo = fdopen(rem_fd, "w");

           fuser = (char *)malloc(256);
           fpass = (char *)malloc(256);
           
		   fprintf(nntp_fo,"200 NNTPProxy ready to authenticate\n");
		   fflush(nntp_fo);

		   while((lineptr = fgetln(nntp_fi, &ilen)) != NULL){
			  if(ilen > 2 ){
                 lineptr[ilen-2]='\0';
                 lineptr[ilen-1]='\0';
                 for (cap = ca; (*cap = strsep(&lineptr, " \t")) != NULL;)
			        if (**cap != '\0')
				       if (++cap >= &ca[3])
				          break;

			     if(strcasecmp("quit",ca[0]) == 0){
			        fprintf(nntp_fo,"205 Ciao\n");
				    fflush(nntp_fo);
				    fclose(nntp_fi);
				    fclose(nntp_fo);
			     }

			     if(strcasecmp("authinfo",ca[0]) == 0){
			        if(strcasecmp("user",ca[1]) == 0){
					   usergiven = 1;
			           snprintf(fuser,256,"%s",ca[2]);
					   fprintf(nntp_fo,"381 PASS required\n");
				       fflush(nntp_fo);
				    }
			        if(strcasecmp("pass",ca[1]) == 0){
					   if(usergiven == 1){
					      passgiven = 1;
					      snprintf(fpass,256,"%s",ca[2]);
					   }
					   else{
					      fprintf(nntp_fo,"480 Please give username first\n");
					      fflush(nntp_fo);
					   }
				    }
			        if((strcasecmp("pass",ca[1]) != 0) && (strcasecmp("user",ca[1]) != 0)){
				       fprintf(nntp_fo,"480 Unknown AUTHINFO parameter\n");
				       fflush(nntp_fo);
				    }
			     }
			     else{
			        fprintf(nntp_fo,"480 Please authenticate first\n");
			        fflush(nntp_fo);
			     }
			     if((usergiven == 1) && (passgiven == 1)){
			        if((strcmp(argv[2],fuser) == 0) && (strcmp(argv[3],fpass) == 0)){
			           fprintf(nntp_fo,"281 Let's go then\n");
			           fflush(nntp_fo);
					   break;
				    }
			        else{
				       fprintf(nntp_fo,"502 Username/Password incorrect\n");   
					   fflush(nntp_fo);
					   fclose(nntp_fi);
					   fclose(nntp_fo);
			        }
			     }

			     cnt++;
                 if(cnt > 10){
                    fclose(nntp_fi);
				    fclose(nntp_fo);
			     }
			  }
		   }
        }
        /* Ends */

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



syntax highlighted by Code2HTML, v. 0.9.1