仙石浩明の日記

2006年4月8日

stone 1.0

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);
}
Filed under: stone 開発日記 — hiroaki_sengoku @ 11:45

No Comments »

No comments yet.

RSS feed for comments on this post.

Leave a comment