/* NNTP authentication proxy by Lasse L. Johnsen Based on FreeBSD bounce port by obrien@FreeBSD.org and others */ #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 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 (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; 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); } } }