Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include "ratfs.h"
#include <ip.h>

enum {
        ACCEPT = 0,             /* verbs in control file */
        REFUSED,
        DENIED,
        DIALUP,
        BLOCKED,
        DELAY,
        NONE,

        Subchar =       '#',    /* character substituted for '/' in file names */
};

static  Keyword actions[] = {
        "allow",                ACCEPT,
        "accept",               ACCEPT,
        "block",                BLOCKED,
        "deny",                 DENIED,
        "dial",                 DIALUP,
        "relay",                DELAY,
        "delay",                DELAY,
        0,                      NONE,
};

static void     acctinsert(Node*, char*);
static char*    getline(Biobuf*);
static void     ipinsert(Node*, char*);
static void     ipsort(void);

/*
 *      Input the configuration file
 *      Currently we only process the "ournets"
 *      specification.
 */
void
getconf(void)
{
        Biobuf *bp;
        char *cp;
        Node *np, *dir, **l;

        if(debugfd >= 0)
                fprint(debugfd, "loading %s\n", conffile);

        bp = Bopen(conffile, OREAD);
        if(bp == 0)
                return;

        dir = finddir(Trusted);
        if(dir == 0)
                return;

        /*
         * if this isn't the first time, purge permanent entries
         */
        trustedqid = Qtrustedfile;
        if(lastconftime){
                l = &dir->children;
                for(np = dir->children; np; np = *l){
                        if(np->d.type == Trustedperm){
                                *l = np->sibs;
                                free(np);
                        } else {
                                np->d.qid.path = trustedqid++;
                                l = &np->sibs;
                        }
                }
                dir->count = 0;
        }

        for(;;){
                cp = getline(bp);
                if(cp == 0)
                        break;
                if (strcmp(cp, "ournets") == 0){
                        for(cp += strlen(cp)+1; cp && *cp; cp += strlen(cp)+1){
                                np = newnode(dir, cp, Trustedperm, 0111, trustedqid++);
                                cidrparse(&np->ip, cp);
                                subslash(cp);
                                np->d.name = atom(cp);
                        }
                }
        }
        Bterm(bp);
        lastconftime = time(0);
}

/*
 *      Reload the control file, if necessary
 */
void
reload(void)
{
        int type, action;
        Biobuf *bp;
        char *cp;
        Node *np, *dir;

        if(debugfd >= 0)
                fprint(debugfd,"loading %s\n", ctlfile);

        bp = Bopen(ctlfile, OREAD);
        if(bp == 0)
                return;
        
        if(lastctltime){
                for(dir = root->children; dir; dir = dir->sibs){
                        if (dir->d.type != Addrdir)
                                continue;
                        for(np = dir->children; np; np = np->sibs)
                                np->count = 0;
                }
        }

        for(;;){
                cp = getline(bp);
                if(cp == 0)
                        break;
                type = *cp;
                if(type == '*'){
                        cp++;
                        if(*cp == 0)            /* space before keyword */
                                cp++;
                }
                action = findkey(cp, actions);
                if (action == NONE)
                        continue;
                if (action == ACCEPT)
                        dir = dirwalk("allow", root);
                else
                if (action == DELAY)
                        dir = dirwalk("delay", root);
                else
                        dir = dirwalk(cp, root);
                if(dir == 0)
                        continue;
                
                for(cp += strlen(cp)+1; cp && *cp; cp += strlen(cp)+1){
                        if(type == '*')
                                acctinsert(dir, cp);
                        else
                                ipinsert(dir, cp);
                }
        }
        Bterm(bp);
        ipsort();
        dummy.d.mtime = dummy.d.atime = lastctltime = time(0);
}

/*
 * get a canonicalized line: a string of null-terminated lower-case
 * tokens with a two null bytes at the end.
 */
static char*
getline(Biobuf *bp)
{
        char c, *cp, *p, *q;
        int n;

        static char *buf;
        static int bufsize;

        for(;;){
                cp = Brdline(bp, '\n');
                if(cp == 0)
                        return 0;
                n = Blinelen(bp);
                cp[n-1] = 0;
                if(buf == 0 || bufsize < n+1){
                        bufsize += 512;
                        if(bufsize < n+1)
                                bufsize = n+1;
                        buf = realloc(buf, bufsize);
                        if(buf == 0)
                                break;
                }
                q = buf;
                for (p = cp; *p; p++){
                        c = *p;
                        if(c == '\\' && p[1])   /* we don't allow \<newline> */
                                c = *++p;
                        else
                        if(c == '#')
                                break;
                        else
                        if(c == ' ' || c == '\t' || c == ',')
                                if(q == buf || q[-1] == 0)
                                        continue;
                                else
                                        c = 0;
                        *q++ = tolower(c);
                }
                if(q != buf){
                        if(q[-1])
                                *q++ = 0;
                        *q = 0;
                        break;
                }
        }
        return buf;
}

/*
 *      Match a keyword
 */
int
findkey(char *val, Keyword *p)
{

        for(; p->name; p++)
                if(strcmp(val, p->name) == 0)
                                break;
        return p->code;
}

/*
 *      parse a cidr specification in either IP/mask or IP#mask format
 */
void
cidrparse(Cidraddr *cidr, char *cp)
{

        char *p, *slash;
        int c;
        ulong a, m;
        uchar addr[IPv4addrlen];
        uchar mask[IPv4addrlen];
        char buf[64];

        /*
         * find '/' or '#' character in the cidr specification
         */
        slash = 0;
        for(p = buf; p < buf+sizeof(buf)-1 && *cp; p++) {
                c = *cp++;
                switch(c) {
                case Subchar:
                        c = '/';
                        slash = p;
                        break;
                case '/':
                        slash = p;
                        break;
                default:
                        break;
                }
                *p = c;
        }
        *p = 0;

        v4parsecidr(addr, mask, buf);
        a = nhgetl(addr);
        m = nhgetl(mask);
        /*
         * if a mask isn't specified, we build a minimal mask
         * instead of using the default mask for that net.  in this
         * case we never allow a class A mask (0xff000000).
         */
        if(slash == 0){
                m = 0xff000000;
                p = buf;
                for(p = strchr(p, '.'); p && p[1]; p = strchr(p+1, '.'))
                                m = (m>>8)|0xff000000;

                /* force at least a class B */
                m |= 0xffff0000;
        }
        cidr->ipaddr = a;
        cidr->mask = m;
}

/*
 *      Substitute Subchar ('#') for '/'
 */
char*
subslash(char *os)
{
        char *s;

        for(s=os; *s; s++)
                if(*s == '/')
                        *s = Subchar;
        return os;
}

/*
 *      Insert an account pseudo-file in a directory
 */
static void
acctinsert(Node *np, char *cp)
{
        int i;
        char *tmp;
        Address *ap;

        static char *dangerous[] = { "*", "!", "*!", "!*", "*!*", 0 };

        if(cp == 0 || *cp == 0)
                return;

        /* rule out dangerous patterns */
        for (i = 0; dangerous[i]; i++)
                if(strcmp(cp, dangerous[i])== 0)
                        return;

        np = dirwalk("account", np);
        if(np == 0)
                return;

        i = np->count++;
        if(i >= np->allocated){
                np->allocated = np->count;
                np->addrs = realloc(np->addrs, np->allocated*sizeof(Address));
                if(np->addrs == 0)
                        fatal("out of memory");
        }

        ap = &np->addrs[i];                     /* new entry on end */
        tmp = strdup(cp);
        if(tmp == nil)
                fatal("out of memory");
        subslash(tmp);
        ap->name = atom(tmp);
        free(tmp);
}

/*
 *      Insert an IP address pseudo-file in a directory
 */
static void
ipinsert(Node *np, char *cp)
{
        char *tmp;
        int i;
        Address *ap;
        if(cp == 0 || *cp == 0)
                return;

        np = dirwalk("ip", np);
        if(np == 0)
                return;

        i = np->count++;
        if(i >= np->allocated){
                np->allocated = np->count;
                np->addrs = realloc(np->addrs, np->allocated*sizeof(Address));
                if(np->addrs == 0)
                        fatal("out of memory");
        }

        ap = &np->addrs[i];                             /* new entry on end */
        tmp = strdup(cp);
        if(tmp == nil)
                fatal("out of memory");
        subslash(tmp);
        ap->name = atom(tmp);
        free(tmp);
        cidrparse(&ap->ip, cp);
}

int
ipcomp(void *a, void *b)
{
        ulong aip, bip;

        aip = ((Address*)a)->ip.ipaddr;
        bip = ((Address*)b)->ip.ipaddr;
        if(aip > bip)
                return 1;
        if(aip < bip)
                return -1;
        return 0;
}

/*
 *      Sort a directory of IP addresses
 */
static void
ipsort(void)
{
        int base;
        Node *dir, *np;

        base = Qaddrfile;
        for(dir = root->children; dir; dir = dir->sibs){
                if (dir->d.type != Addrdir)
                        continue;
                for(np = dir->children; np; np = np->sibs){
                        if(np->d.type == IPaddr && np->count && np->addrs)
                                qsort(np->addrs, np->count, sizeof(Address), ipcomp);
                        np->baseqid = base;
                        base += np->count;
                }
        }
}