/** * mrelayd.c -- a relay daemon * -- * $Id: mrelayd.c,v 1.9 1997/12/17 14:30:31 marcus Exp $ * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * "The Java Telnet Applet" is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* mrelayd.c (c) 1996,1997 Marcus Meissner */ /* Compile with: cc -o relayd relayd.c * or: gcc -o relayd relayd.c * Solaris: (g)cc -o relayd relayd.c -lsocket -lnsl */ /* this program expects the string: * "relay " or "relay " * after connecting. It will then try to connect to the specified host * if failures occur, it will terminate the connection. */ /* adjust this to a reasonable limit */ #define MAXUSERS 120 /* message printed if all slots are used ... */ #define FAILMESSAGE "Sorry, all slots are full.\r\n" /* string printed before connection */ #define RELAYHEADER "Relayd $Revision: 1.9 $ (c) Marcus Meissner\r\n" /* the tcp port this demons is listening on ... */ #define LISTENPORT 31415 /* default connect port (telnet) */ #define DEFAULTPORT 23 /* default buffersize */ #define DEFAULTSIZE 2000 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(sun) && defined(__GNUC__) int socket(int,int,int); int shutdown(int,int); int close(int); int bind(int,struct sockaddr*,int); int listen(int,int); void bzero(char*,int); int select(int,fd_set *,fd_set*,fd_set*,struct timeval*); int accept(int,struct sockaddr*,int*); int connect(int,struct sockaddr*,int); int recvfrom(int,char*,int,int,struct sockaddr*,int*); /*void perror(char*); SLOWLARIS HASS*/ /*int sendto(int,char*,int,int,struct sockaddr*,int); SLOWLARIS HASS*/ #endif #ifdef hpux /* redefinition... to avoid prototype in */ #define FD_CAST int #endif #ifdef sgi void bzero(void*,int); #endif #ifndef FD_CAST #define FD_CAST fd_set #endif extern int errno; struct relay { char *inbuf,*outbuf; int infd,outfd,incur,outcur,insize,outsize; struct sockaddr_in inaddr,outaddr; int state; #define STATE_ACCEPTED 0 #define STATE_OK 1 int flags; #define FLAG_EOF_USER 1 #define FLAG_EOF_TARGET 2 #define FLAG_CLOSED_TARGET 4 #define FLAG_CLOSED_USER 8 } *relays = NULL; int nrofrelays = 0; void* xmalloc(int size) { void*x; x=malloc(size); if (!x) { fprintf(stderr,"Out of memory, exiting.\n"); exit(1); } return x; } void* xrealloc(void *y,int size) { void*x; x=realloc(y,size); if (!x) { fprintf(stderr,"Out of memory, exiting.\n"); exit(1); } return x; } static int fd_make_nonblocking(int fd) { int isnonblock=0; #ifdef FIONBIO if (!isnonblock) { int b; b=1; if (-1==ioctl(fd,FIONBIO,&b)) { perror("ioctl FIONBIO"); } else isnonblock=1; } #endif #ifdef O_NDELAY if (!isnonblock) { int flags; if (-1==(flags=fcntl(fd,F_GETFL))) { perror("fcntl F_GETFL"); } else { flags|=O_NDELAY; if (-1==fcntl(fd,F_SETFL,flags)) { perror("fcntl F_SETFL O_NDELAY"); } else isnonblock=1; } } #endif #ifdef O_NONBLOCK if (!isnonblock) { int flags; if (-1==(flags=fcntl(fd,F_GETFL))) { perror("fcntl F_GETFL"); } else { flags|=O_NONBLOCK; if (-1==fcntl(fd,F_SETFL,flags)) { perror("fcntl F_SETFL O_NONBLOCK"); } else isnonblock=1; } } #endif return isnonblock; } void clean_connection(struct relay *relay) { if (!relay) return; if (relay->outfd>=0) { if (-1==close(relay->outfd)) perror("close"); relay->outfd=-1; } if (relay->infd>=0) { if (-1==close(relay->infd)) perror("close"); relay->infd=-1; } free(relay->outbuf);free(relay->inbuf); memcpy(relay,relay+1,sizeof(struct relay)*(nrofrelays-(relay-relays)-1)); relays = xrealloc(relays,sizeof(struct relay)*(--nrofrelays)); } void main(argc,argv) int argc; char **argv; { int i,j,res; int acfd; struct sockaddr_in acsa; char readbuf[1000],relaystring[1000]; struct in_addr targetaddr; close(0); close(1); signal(SIGPIPE,SIG_IGN); signal(SIGHUP,SIG_IGN); /* don't terminate on session detach */ strcpy(relaystring,FAILMESSAGE); if (-1==(acfd=socket(PF_INET,SOCK_STREAM,0))) { perror("socket(accept_socket)"); exit(1); } acsa.sin_family=AF_INET; acsa.sin_port=htons(LISTENPORT); acsa.sin_addr.s_addr=INADDR_ANY; #ifdef SO_REUSEADDR { int reuseit=1; if (-1==setsockopt(acfd,SOL_SOCKET,SO_REUSEADDR,(char*)&reuseit,sizeof(reuseit))) perror("setsockopt SOL_SOCKET SO_REUSEADDR"); } #endif if (-1==bind(acfd,(struct sockaddr*)&acsa,sizeof(struct sockaddr_in))) { perror("bind"); exit(1); } /* 5 is usual the maximum anyway */ if (-1==listen(acfd,5)) { perror("listen"); exit(1); } while (1) { fd_set readfds,writefds; int width; width=3; if (acfd>=width) width=acfd+1; restart_select: FD_ZERO(&readfds);FD_ZERO(&writefds); FD_SET(acfd,&readfds); for (i=nrofrelays;i--;) { struct relay *relay = relays+i; /* both sides closed? -> clean */ if ((relay->flags & (FLAG_CLOSED_TARGET|FLAG_CLOSED_USER)) == (FLAG_CLOSED_TARGET|FLAG_CLOSED_USER) ) { clean_connection(relay); continue; } /* transmitted all stuff left to user? -> close */ if ((relay->flags&(FLAG_CLOSED_TARGET|FLAG_EOF_TARGET))&&(!relay->outcur)) { clean_connection(relay); continue; } /* transmitted all stuff left to target? -> close */ if ((relay->flags&(FLAG_CLOSED_USER|FLAG_EOF_USER))&&(!relay->incur)) { clean_connection(relay); continue; } if (relay->outfd>=0) { /*need to do that... else it will cause load 1*/ if (relay->incur) FD_SET(relay->outfd,&writefds); if (!(relay->flags & FLAG_EOF_TARGET)) FD_SET(relay->outfd,&readfds); if (relay->outfd>=width) width=relay->outfd+1; } if (relay->infd>=0) { /*need to do that... else it will cause load 1*/ if (relay->outcur) FD_SET(relay->infd,&writefds); if (!(relay->flags & FLAG_EOF_USER)) FD_SET(relay->infd,&readfds); if (relay->infd>=width) width=relay->infd+1; } } if (-1==select( width, (FD_CAST*)&readfds, (FD_CAST*)&writefds, NULL,/*no exceptfds.*/ 0) ) { if (errno!=EINTR) perror("select"); else goto restart_select; } if (FD_ISSET(acfd,&readfds)) { int afd; int aclen; struct sockaddr_in conaddr; struct relay *relay = NULL; aclen=sizeof(struct sockaddr_in); if (-1==(afd=accept(acfd,(struct sockaddr*)&conaddr,&aclen))) perror("accept"); if (relays) relays=(struct relay*)xrealloc(relays,sizeof(struct relay)*(nrofrelays+1)); else relays=(struct relay*)xmalloc(sizeof(struct relay)); nrofrelays++; relay = relays+(nrofrelays-1); relay->inbuf = xmalloc(DEFAULTSIZE); relay->outbuf = xmalloc(DEFAULTSIZE); relay->insize = DEFAULTSIZE; relay->outsize = DEFAULTSIZE; relay->flags = 0; relay->incur = 0; relay->outcur = 0; relay->infd = afd; relay->outfd = -1; relay->state = STATE_ACCEPTED; memcpy(&relay->inaddr,&conaddr,sizeof(struct sockaddr_in)); if (nrofrelays>=MAXUSERS) { strcpy(relay->outbuf,relaystring); relay->outcur = strlen(relaystring)+1; relay->state = STATE_OK; relay->flags = FLAG_CLOSED_TARGET; } #ifdef SO_LINGER { struct linger sol; sol.l_linger = 5; sol.l_onoff = 1; if (-1==setsockopt(acfd,SOL_SOCKET,SO_LINGER,(char*)&sol,sizeof(sol))) perror("setsockopt SOL_SOCKET SO_LINGER"); } #endif } for (i=nrofrelays;i--;) { struct relay *relay = relays+i; if ((relay->infd>=0) && FD_ISSET(relay->infd,&readfds)) { do { if (-1==(res=read(relay->infd,readbuf,1000))) { if (errno==EINTR) break; /* user side has broken the connection */ close(relay->infd);relay->infd=-1; relay->flags |= FLAG_CLOSED_USER; break; } break; } while (1); if (res==0) { /* we read the End Of File marker. but we still have to write * the rest of the text */ relay->flags |= FLAG_EOF_USER; } if (res>0) { readbuf[res]='\0'; while (relay->incur+res>=relay->insize) { relay->inbuf=xrealloc(relay->inbuf,relay->insize*2); relay->insize*=2; } memcpy(relay->inbuf+relay->incur,readbuf,res+1); relay->incur+=res; } if ( (relay->outfd==-1) && (relay->state==STATE_ACCEPTED) && memchr(relay->inbuf,'\n',relay->incur) ) { char sendbuf[200]; struct hostent *hp; char *s,*nextchar,*tmp; int port; s = memchr(relay->inbuf,'\n',relay->incur); if (!s) continue; *s='\0'; nextchar=s+1; if ((s=memchr(relay->inbuf,'\r',(s-relay->inbuf)))) *s='\0'; relay->state = STATE_OK; tmp = (char*)xmalloc(strlen(relay->inbuf)); if (2!=sscanf(relay->inbuf,"relay %s %d", tmp,&port )) { if (!sscanf(relay->inbuf,"relay %s",tmp)) { free(tmp); /* we avoid telling potential hackers how to use this relay */ sprintf(relay->outbuf,"550 Bad syntax. Go away.\n",tmp); relay->outcur = strlen(relay->outbuf); relay->flags = FLAG_CLOSED_TARGET; continue; } else port = DEFAULTPORT; } hp=gethostbyname(tmp); if (!hp) {/* not found */ sprintf(relay->outbuf,"No hostentry for '%s'!\n",tmp); free(tmp); relay->outcur = strlen(relay->outbuf); relay->flags = FLAG_CLOSED_TARGET; continue; } memcpy(&targetaddr,hp->h_addr_list[0],sizeof(struct in_addr)); relay->outaddr.sin_family=AF_INET; relay->outaddr.sin_port=htons(port); memcpy(&(relay->outaddr.sin_addr),&targetaddr,4); strcpy(sendbuf,RELAYHEADER); relay->outcur=strlen(sendbuf); memcpy(relay->outbuf,sendbuf,strlen(sendbuf)+1); if (-1==(relay->outfd=socket(PF_INET,SOCK_STREAM,0))) perror("socket(connect_socket)"); (void)fd_make_nonblocking(relay->outfd); if ( (-1==connect( relay->outfd, (struct sockaddr*)&(relay->outaddr), sizeof(struct sockaddr_in)) ) && (errno!=EINPROGRESS) ) { sprintf(readbuf,"Connect to %s failed: %s\n",tmp,strerror(errno)); perror("connect"); close(relay->outfd);relay->outfd=-1; relay->state = STATE_OK; relay->flags |= FLAG_CLOSED_TARGET; strcpy(relay->outbuf,readbuf); relay->outsize = strlen(readbuf)+1; free(tmp); continue; } free(tmp); #ifdef SEND_REMOTEIP /* only useful if you want to tell the * remotemud the _real_ host the caller * is calling from */ tmphp=gethostbyaddr( (char*)(&(conaddr.sin_addr)), sizeof(struct in_addr), AF_INET ); if (!tmphp) { sprintf(sendbuf,"remoteip %s %s\n", inet_ntoa(conaddr.sin_addr), inet_ntoa(conaddr.sin_addr) ); } else { sprintf(sendbuf,"remoteip %s %s\n", inet_ntoa(conaddr.sin_addr), tmphp->h_name ); } memcpy(relay->inbuf,sendbuf,strlen(sendbuf)+1); relay->incur=strlen(sendbuf); #else relay->inbuf[0]='\0'; relay->incur=0; #endif } } if ((relay->outfd>=0) && FD_ISSET(relay->outfd,&readfds)) { do { if (-1==(res=read(relay->outfd,readbuf,1000))) { if (errno==EINTR) continue; /* the mudside has broken the * connection. we still have * to transmit the rest of * the text */ close(relay->outfd);relay->outfd=-1; relay->flags |= FLAG_CLOSED_TARGET; break; } break; } while (1); if (res==0) { /* we read the End Of File marker. but we still have to write * the rest of the text */ relay->flags |= FLAG_EOF_TARGET; } if (res>0) { /* 0 is not automagically appended. */ readbuf[res]='\0'; while (relay->outcur+res>=relay->outsize) { relay->outbuf=xrealloc(relay->outbuf,relay->outsize*2); relay->outsize*=2; } memcpy(relay->outbuf+relay->outcur,readbuf,res+1); relay->outcur+=res; } } if ((relay->infd>=0) && FD_ISSET(relay->infd,&writefds)) { j=relay->outcur; if (-1==(res=write(relay->infd,relay->outbuf,j))) { if (errno!=EINTR) { close(relay->infd);relay->infd=-1; relay->flags |= FLAG_CLOSED_USER; } } if (res>0) { memcpy(relay->outbuf,relay->outbuf+res,relay->outcur-res); relay->outcur-=res; } } if ((relay->outfd>=0) && FD_ISSET(relay->outfd,&writefds)) { j=relay->incur; if (-1==(res=write(relay->outfd,relay->inbuf,j))) { if (errno!=EINTR) { close(relay->outfd);relay->outfd=-1; relay->flags |= FLAG_CLOSED_TARGET; } } if (res>0) { memcpy(relay->inbuf,relay->inbuf+res,relay->incur-res); relay->incur-=res; } } } } }