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