stone の最初のバージョン。 11年前 Version 1.0 を出したときは、わずか 278行だった (現在の Version 2.3a では、9100行を超えている)。
/* * stone.c simple repeater * Copyright(C)1995 by Hiroaki Sengoku <sengoku@virgo.bekkoame.or.jp> * Version 1.0 Jan 28, 1995 * * This program 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. * * This program 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 GNU Emacs; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * Usage: stone <st> [-- <st>]... * <st> := <screen> [<hosts>...] | <host>:<port> <sport> [<hosts>...] * * (1) Any packets received by <screen> are passed to DISPLAY * (2) Any packets received by <sport> are passed to <host>:<port> * (3) as long as these packets are sent from <hosts>... * (4) if <hosts> are not given, any hosts are welcome. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #define BACKLOG_MAX 5 #define XPORT 6000 #define BUFMAX 256 #define STONEMAX (FD_SETSIZE/3) /* max # of stones */ typedef struct { int sd; /* socket descriptor to listen */ struct sockaddr_in sin; /* destination */ int nhosts; /* # of hosts */ struct in_addr xhosts[0]; /* hosts permitted to connect */ } Stone; /* *addrp is permitted to connect to *stonep ? */ int checkXhost(stonep,addrp) Stone *stonep; struct in_addr *addrp; { int i; if( !stonep->nhosts ) return 1; /* any hosts can access */ for( i=0; i < stonep->nhosts; i++ ) { if( addrp->s_addr == stonep->xhosts[i].s_addr ) return 1; } return 0; } /* *stonep accept connection */ void doaccept(stonep,fdsp,pair) Stone *stonep; fd_set *fdsp; int *pair; { struct sockaddr_in from; int nsd, dsd; int len; len = sizeof(from); nsd = accept(stonep->sd,(struct sockaddr*)&from,&len); #ifdef DEBUG printf("Accept: %x port %d ...", ntohl((unsigned long)from.sin_addr.s_addr),ntohs(from.sin_port)); #endif if( !checkXhost(stonep,&from.sin_addr) ) { #ifdef DEBUG printf("denied.\n"); #endif if( nsd >= 0 ) close(nsd); return; } #ifdef DEBUG printf("accepted.\n"); #endif if( nsd < 0 ) { if( errno == EINTR ) return; fprintf(stderr,"Accept error.\n"); return; } if( (dsd=socket(PF_INET,SOCK_STREAM,0)) < 0 ) { fprintf(stderr,"Cannot create socket.\n"); close(nsd); return; } if( connect(dsd,(struct sockaddr*)&stonep->sin,sizeof(stonep->sin)) < 0 ) { fprintf(stderr,"Cannot connect socket.\n"); close(nsd); if( dsd >= 0 ) close(dsd); return; } pair[nsd] = dsd; pair[dsd] = nsd; FD_SET(nsd,fdsp); FD_SET(dsd,fdsp); } void repeater(nstones,stones) int nstones; /* # of stones */ Stone *stones[]; { int sdmax; int pair[FD_SETSIZE]; fd_set fds, rfds; int width, i; char buf[BUFMAX]; int nbyte; sdmax = stones[nstones-1]->sd + 1; /* sd of last stone + 1 */ FD_ZERO(&fds); for( i=0; i < nstones; i++ ) FD_SET(stones[i]->sd,&fds); width = ulimit(4,0); for( i=0; i < width; i++ ) pair[i] = -1; while( rfds=fds, select(width,&rfds,NULL,NULL,NULL) > 0 ) { for( i=0; i < width; i++ ) { if( FD_ISSET(i,&rfds) ) { if( i < sdmax ) doaccept(stones[nstones-sdmax+i],&fds,pair); else if( (nbyte=read(i,buf,BUFMAX)) > 0 ) write(pair[i],buf,nbyte); else { #ifdef DEBUG printf("shutdown %d, close %d\n",pair[i],i); #endif if( pair[i] >= 0 ) { shutdown(pair[i],2); pair[pair[i]] = -1; } pair[i] = -1; close(i); FD_CLR(i,&fds); } } } } } void host2addr(name,addrp,familyp) char *name; struct in_addr *addrp; short *familyp; { struct hostent *hp; if( hp=gethostbyname(name) ) { bcopy(hp->h_addr,(char *)addrp,hp->h_length); if( familyp ) *familyp = hp->h_addrtype; } else if( (addrp->s_addr=inet_addr(name)) != -1 ) { if( familyp ) *familyp = AF_INET; } else { fprintf(stderr,"Unknown host : %s\n",name); exit(1); } } /* make stone */ Stone *mkstone(dhost,dport,port,nhosts,hosts) char *dhost; /* destination hostname */ int dport; /* destination port */ int port; /* listening port */ int nhosts; /* # of hosts to permit */ char *hosts[]; /* hosts to permit */ { Stone *stonep; struct sockaddr_in sin; int i; stonep = calloc(1,sizeof(Stone)+sizeof(struct in_addr)*nhosts); if( !stonep ) { fprintf(stderr,"Out of memory.\n"); exit(1); } stonep->nhosts = nhosts; bzero((char *)&sin,sizeof(sin)); /* clear sin struct */ sin.sin_family = AF_INET; sin.sin_port = htons(port); /* convert to network byte order */ host2addr(dhost,&stonep->sin.sin_addr,&stonep->sin.sin_family); for( i=0; i < nhosts; i++ ) { host2addr(hosts[i],&stonep->xhosts[i],NULL); #ifdef DEBUG printf("permit %x to connecting to %x:%d\n", ntohl((unsigned long)stonep->xhosts[i].s_addr), ntohl((unsigned long)stonep->sin.sin_addr.s_addr),dport); #endif } stonep->sin.sin_port = htons(dport); stonep->sd = socket(AF_INET,SOCK_STREAM,0); if( stonep->sd < 0 ) { fprintf(stderr,"Can't get socket.\n"); exit(1); } if( bind(stonep->sd, (struct sockaddr*)&sin, sizeof(sin)) ) { fprintf(stderr,"Can't bind.\n"); exit(1); } listen(stonep->sd,BACKLOG_MAX); #ifdef DEBUG printf("stone%3d:%s:%d <- %d\n",stonep->sd,dhost,dport,port); #endif return stonep; } help(com) char *com; { fprintf(stderr, "Usage: %s <st> [-- <st>]...\n" " st: <screen> [<hosts>...]" "| <host>:<port> <port> [<hosts>...]\n" ,com); exit(1); } int getdist(p,portp) char *p; int *portp; { while( *p ) { if( *p == ':' ) { *p++ = '\0'; *portp = atoi(p); return 1; } p++; } return 0; } main(argc,argv) int argc; char *argv[]; { int nstones; /* # of stones */ Stone *stones[STONEMAX]; int i, j, k; char display[256], *p, *q; char *disphost, *host; int dispport, port, sport; p = getenv("DISPLAY"); if( p ) { strcpy(display,p); getdist(display,&dispport); disphost = display; dispport += XPORT; } else { disphost = NULL; } if( argc < 2 ) help(argv[0]); setbuf(stdout,NULL); nstones = 0; for( i=1; i < argc; i++ ) { if( getdist(argv[i],&port) ) { host = argv[i++]; if( argc <= i ) help(argv[0]); sport = atoi(argv[i++]); } else { host = disphost; port = dispport; sport = XPORT+atoi(argv[i++]); } j = 0; k = i; for( ; i < argc; i++, j++ ) if( !strcmp(argv[i],"--") ) break; stones[nstones++] = mkstone(host,port,sport,j,&argv[k]); } q = argv[argc-1] + strlen(argv[argc-1]); for( p=argv[1]; p < q; p++ ) *p = '\0'; repeater(nstones,stones); }