akaros/tests/cs.c
<<
>>
Prefs
   1/*
   2 * This file is part of the UCB release of Plan 9. It is subject to the license
   3 * terms in the LICENSE file found in the top-level directory of this
   4 * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
   5 * part of the UCB release of Plan 9, including this file, may be copied,
   6 * modified, propagated, or distributed except according to the terms contained
   7 * in the LICENSE file.
   8 */
   9#include <stdlib.h>
  10
  11#include <ctype.h>
  12#include <error.h>
  13#include <fcall.h>
  14#include <fcntl.h>
  15#include <iplib/iplib.h>
  16#include <ndblib/fcallfmt.h>
  17#include <ndblib/ndb.h>
  18#include <parlib/parlib.h>
  19#include <parlib/spinlock.h>
  20#include <pthread.h>
  21#include <ros/common.h>
  22#include <signal.h>
  23#include <stdio.h>
  24#include <sys/stat.h>
  25#include <sys/types.h>
  26#include <unistd.h>
  27
  28enum {
  29        Nreply = 20,
  30        Maxreply = 256,
  31        Maxrequest = 128,
  32        Maxpath = 128,
  33        Maxfdata = 8192,
  34        Maxhost = 64,    /* maximum host name size */
  35        Maxservice = 64, /* maximum service name size */
  36
  37        Qdir = 0,
  38        Qcs = 1,
  39};
  40
  41int vers; /* incremented each clone/attach */
  42/* need to resolve the #inluce for all this stuff. */
  43#define DMDIR 0x80000000 /* mode bit for directories */
  44#define QTDIR 0x80
  45#define QTFILE 0
  46#define ERRMAX 128
  47
  48struct mfile {
  49        int busy;
  50
  51        char *user;
  52        struct qid qid;
  53        int fid;
  54
  55        /*
  56         *  current request
  57         */
  58        char *net;
  59        char *host;
  60        char *serv;
  61        char *rem;
  62
  63        /*
  64         *  result of the last lookup
  65         */
  66        struct network *nextnet;
  67        int nreply;
  68        char *reply[Nreply];
  69        int replylen[Nreply];
  70};
  71
  72struct mlist {
  73        struct mlist *next;
  74        struct mfile mf;
  75};
  76
  77/*
  78 *  active requests
  79 */
  80struct job {
  81        struct job *next;
  82        int flushed;
  83        struct fcall request;
  84        struct fcall reply;
  85        pthread_t thread;
  86};
  87
  88spinlock_t joblock = SPINLOCK_INITIALIZER;
  89struct job *joblist;
  90
  91struct mlist *mlist;
  92int mfd[2];
  93int debug;
  94int paranoia;
  95int ipv6lookups = 1;
  96int server;
  97char *dbfile = "lib/ndb/local";
  98struct ndb *db, *netdb;
  99
 100static void rversion(struct job *);
 101static void rflush(struct job *);
 102static void rattach(struct job *, struct mfile *);
 103static char *rwalk(struct job *, struct mfile *);
 104static void ropen(struct job *, struct mfile *);
 105static void rcreate(struct job *, struct mfile *);
 106static void rread(struct job *, struct mfile *);
 107static void rwrite(struct job *, struct mfile *);
 108static void rclunk(struct job *, struct mfile *);
 109static void rremove(struct job *, struct mfile *);
 110static void rstat(struct job *, struct mfile *);
 111static void rwstat(struct job *, struct mfile *);
 112static void rauth(struct job *);
 113static void sendmsg(struct job *, char *);
 114static void mountinit(char *, char *);
 115static void io(void);
 116static void ndbinit(void);
 117static void netinit(int);
 118static void netadd(char *);
 119static char *genquery(struct mfile *, char *);
 120static char *ipinfoquery(struct mfile *, char **, int);
 121static int needproto(struct network *, struct ndbtuple *);
 122static int lookup(struct mfile *);
 123static struct ndbtuple *reorder(struct ndbtuple *, struct ndbtuple *);
 124static void ipid(void);
 125static void readipinterfaces(void);
 126static void *emalloc(int);
 127static char *estrdup(char *);
 128static struct job *newjob(void);
 129static void freejob(struct job *);
 130static void setext(char *, int, char *);
 131static void cleanmf(struct mfile *);
 132
 133spinlock_t dblock = SPINLOCK_INITIALIZER;  /* mutex on database operations */
 134spinlock_t netlock = SPINLOCK_INITIALIZER; /* mutex for netinit() */
 135
 136char *logfile = "cs";
 137char *paranoiafile = "cs.paranoia";
 138
 139char mntpt[Maxpath];
 140char netndb[Maxpath];
 141
 142/*
 143 *  Network specific translators
 144 */
 145static struct ndbtuple *iplookup(struct network *, char *, char *, int);
 146static char *iptrans(struct ndbtuple *, struct network *, char *, char *, int);
 147static struct ndbtuple *telcolookup(struct network *, char *, char *, int);
 148static char *telcotrans(struct ndbtuple *, struct network *, char *, char *,
 149                        int);
 150static struct ndbtuple *dnsiplookup(char *, struct ndbs *);
 151
 152struct network {
 153        char *net;
 154        struct ndbtuple *(*lookup)(struct network *, char *, char *, int);
 155        char *(*trans)(struct ndbtuple *, struct network *, char *, char *, int);
 156        int considered;      /* flag: ignored for "net!"? */
 157        int fasttimeouthack; /* flag. was for IL */
 158        struct network *next;
 159};
 160
 161/*
 162 *  net doesn't apply to (r)udp, icmp(v6), or telco (for speed).
 163 */
 164struct network network[] = {
 165    {"tcp", iplookup, iptrans, 0},
 166    {"udp", iplookup, iptrans, 1},
 167    {"icmp", iplookup, iptrans, 1},
 168    {"icmpv6", iplookup, iptrans, 1},
 169    {"rudp", iplookup, iptrans, 1},
 170    {"ssh", iplookup, iptrans, 1},
 171    {"telco", telcolookup, telcotrans, 1},  {0},
 172};
 173
 174spinlock_t ipifclock = SPINLOCK_INITIALIZER;
 175struct ipifc *ipifcs;
 176
 177char eaddr[16];         /* ascii ethernet address */
 178char ipaddr[64];        /* ascii internet address */
 179uint8_t ipa[IPaddrlen]; /* binary internet address */
 180char *mysysname;
 181
 182struct network *netlist; /* networks ordered by preference */
 183struct network *last;
 184
 185char *argv0;
 186
 187static void evnotify(int rc)
 188{
 189        struct event_msg msg = { 0 };
 190
 191        msg.ev_type = EV_USER_IPI;
 192        msg.ev_arg1 = rc;
 193        sys_notify(getppid(), EV_USER_IPI, &msg);
 194}
 195
 196static void evexit(int rc)
 197{
 198        if (server)
 199                evnotify(rc);
 200        exit(rc);
 201}
 202
 203static void usage(void)
 204{
 205        fprintf(stderr, "CS:usage: %s [-dn] [-f ndb-file] [-x netmtpt]\n",
 206                argv0);
 207        fprintf(stderr, "CS:usage");
 208        evexit(1);
 209}
 210
 211/*
 212 * based on libthread's threadsetname, but drags in less library code.
 213 * actually just sets the arguments displayed.
 214 */
 215static void procsetname(char *fmt, ...)
 216{
 217/* someday ... */
 218#if 0
 219        int fd;
 220        char *cmdname;
 221        char buf[128];
 222        va_list arg;
 223
 224        va_start(arg, fmt);
 225        cmdname = vsmprint(fmt, arg);
 226        va_end(arg);
 227        if (cmdname == NULL)
 228                return;
 229        snprintf(buf, sizeof(buf), "#proc/%d/args", getpid());
 230        fd = open(buf, OWRITE);
 231        if (fd >= 0) {
 232                write(fd, cmdname, strlen(cmdname)+1);
 233                close(fd);
 234        }
 235        free(cmdname);
 236#endif
 237}
 238
 239int main(int argc, char *argv[])
 240{
 241        int justsetname, ch;
 242        char ext[Maxpath], servefile[Maxpath];
 243
 244        /* Make us an SCP with a 2LS */
 245        parlib_wants_to_be_mcp = FALSE;
 246        register_printf_specifier('F', printf_fcall, printf_fcall_info);
 247
 248        argv0 = argv[0];
 249        justsetname = 0;
 250        setnetmtpt(mntpt, sizeof(mntpt), NULL);
 251        ext[0] = 0;
 252        while ((ch = getopt(argc, argv, "4df:nSx:")) != -1) {
 253                switch (ch) {
 254                case '4':
 255                        ipv6lookups = 0;
 256                        break;
 257                case 'd':
 258                        debug = 1;
 259                        break;
 260                case 'f':
 261                        dbfile = optarg;
 262                        break;
 263                case 'n':
 264                        justsetname = 1;
 265                        break;
 266                case 'S':
 267                        server = 1;
 268                        break;
 269                case 'x':
 270                        setnetmtpt(mntpt, sizeof(mntpt), optarg);
 271                        setext(ext, sizeof(ext), mntpt);
 272                        break;
 273                default:
 274                        usage();
 275                        break;
 276                }
 277        }
 278        snprintf(servefile, sizeof(servefile), "#srv/cs%s", ext);
 279        snprintf(netndb, sizeof(netndb), "%s/ndb", mntpt);
 280        syscall(SYS_nunmount, (unsigned long)servefile, strlen(servefile),
 281                (unsigned long)mntpt, strlen(mntpt));
 282        remove(servefile);
 283
 284        ndbinit();
 285        netinit(0);
 286
 287        if (!justsetname) {
 288                mountinit(servefile, mntpt);
 289                if (server)
 290                        evnotify(0);
 291                io();
 292        }
 293
 294        evexit(0);
 295}
 296
 297/*
 298 *  if a mount point is specified, set the cs extention to be the mount point
 299 *  with '_'s replacing '/'s
 300 */
 301static void setext(char *ext, int n, char *p)
 302{
 303        int i, c;
 304
 305        n--;
 306        for (i = 0; i < n; i++) {
 307                c = p[i];
 308                if (c == 0)
 309                        break;
 310                if (c == '/')
 311                        c = '_';
 312                ext[i] = c;
 313        }
 314        ext[i] = 0;
 315}
 316
 317static void mountinit(char *service, char *mntpt)
 318{
 319        int f;
 320        int p[2];
 321        char buf[32];
 322        int ret;
 323
 324        ret = pipe(p);
 325        if (ret < 0) {
 326                error(1, 0, "pipe: %r");
 327                evexit(1);
 328        }
 329
 330        /*
 331         *  make a /srv/cs
 332         * ORCLOSE means remove on last close. Handy. Not here yet.
 333         */
 334        f = open(service, O_WRONLY | O_CREAT /*|ORCLOSE*/, 0666);
 335        if (f < 0)
 336                error(1, 0, "%s: %r", service);
 337        snprintf(buf, sizeof(buf), "%d", p[1]);
 338        if (write(f, buf, strlen(buf)) != strlen(buf))
 339                error(1, 0, "Write %s: %r", service);
 340        /* using #s: we create a pipe and drop it into #srv.
 341         * we no longer mount. That's up to you.
 342         * #srv will route requests to us.
 343         */
 344        close(p[1]);
 345
 346        mfd[0] = mfd[1] = p[0];
 347}
 348
 349static void ndbinit(void)
 350{
 351        db = ndbopen(dbfile);
 352        if (db == NULL)
 353                error(1, 0, "%s: %r", "can't open network database");
 354
 355        netdb = ndbopen(netndb);
 356        if (netdb != NULL) {
 357                netdb->nohash = 1;
 358                db = ndbcat(netdb, db);
 359        }
 360}
 361
 362static struct mfile *newfid(int fid)
 363{
 364        struct mlist *f, *ff;
 365        struct mfile *mf;
 366
 367        ff = 0;
 368        for (f = mlist; f; f = f->next)
 369                if (f->mf.busy && f->mf.fid == fid)
 370                        return &f->mf;
 371                else if (!ff && !f->mf.busy)
 372                        ff = f;
 373        if (ff == 0) {
 374                ff = emalloc(sizeof(*f));
 375                ff->next = mlist;
 376                mlist = ff;
 377        }
 378        mf = &ff->mf;
 379        memset(mf, 0, sizeof(*mf));
 380        mf->fid = fid;
 381        return mf;
 382}
 383
 384static struct job *newjob(void)
 385{
 386        struct job *job;
 387
 388        job = calloc(1, sizeof(struct job));
 389        if (!job)
 390                error(1, 0, "%s: %r", "job calloc");
 391        spinlock_lock(&joblock);
 392        job->next = joblist;
 393        joblist = job;
 394        job->request.tag = -1;
 395        spinlock_unlock(&joblock);
 396        return job;
 397}
 398
 399static void freejob(struct job *job)
 400{
 401        struct job **l;
 402        struct job *to_free = 0;
 403
 404        spinlock_lock(&joblock);
 405        for (l = &joblist; *l; l = &(*l)->next) {
 406                if ((*l) == job) {
 407                        *l = job->next;
 408                        to_free = job;
 409                        break;
 410                }
 411        }
 412        spinlock_unlock(&joblock);
 413        if (to_free)
 414                free(to_free);
 415}
 416
 417static void flushjob(int tag)
 418{
 419        struct job *job;
 420
 421        spinlock_lock(&joblock);
 422        for (job = joblist; job; job = job->next) {
 423                if (job->request.tag == tag && job->request.type != Tflush) {
 424                        job->flushed = 1;
 425                        break;
 426                }
 427        }
 428        spinlock_unlock(&joblock);
 429}
 430
 431static void *job_thread(void *arg)
 432{
 433        struct mfile *mf;
 434        struct job *job = arg;
 435
 436        spinlock_lock(&dblock);
 437        mf = newfid(job->request.fid);
 438
 439        if (debug)
 440                fprintf(stderr, "CS:%F", &job->request);
 441        switch (job->request.type) {
 442        default:
 443                fprintf(stderr, "CS:unknown request type %d", job->request.type);
 444                break;
 445        case Tversion:
 446                rversion(job);
 447                break;
 448        case Tauth:
 449                rauth(job);
 450                break;
 451        case Tflush:
 452                rflush(job);
 453                break;
 454        case Tattach:
 455                rattach(job, mf);
 456                break;
 457        case Twalk:
 458                rwalk(job, mf);
 459                break;
 460        case Topen:
 461                ropen(job, mf);
 462                break;
 463        case Tcreate:
 464                rcreate(job, mf);
 465                break;
 466        case Tread:
 467                rread(job, mf);
 468                break;
 469        case Twrite:
 470                rwrite(job, mf);
 471                break;
 472        case Tclunk:
 473                rclunk(job, mf);
 474                break;
 475        case Tremove:
 476                rremove(job, mf);
 477                break;
 478        case Tstat:
 479                rstat(job, mf);
 480                break;
 481        case Twstat:
 482                rwstat(job, mf);
 483                break;
 484        }
 485        spinlock_unlock(&dblock);
 486
 487        freejob(job);
 488
 489        if (debug)
 490                fprintf(stderr, "CS:Job done\n");
 491        return 0;
 492}
 493
 494static void io(void)
 495{
 496        long n;
 497        uint8_t mdata[IOHDRSZ + Maxfdata];
 498        struct job *job;
 499        pthread_attr_t pth_attr;
 500
 501        /*
 502         * each request is handled via a thread. Somewhat less efficient than
 503         * the old cs but way cleaner.
 504         */
 505
 506        pthread_attr_init(&pth_attr);
 507        pthread_attr_setdetachstate(&pth_attr, PTHREAD_CREATE_DETACHED);
 508        for (;;) {
 509                n = read9pmsg(mfd[0], mdata, sizeof(mdata));
 510                if (n <= 0)
 511                        error(1, 0, "%s: %r", "mount read");
 512                job = newjob();
 513                if (convM2S(mdata, n, &job->request) != n) {
 514                        fprintf(stderr,
 515                                "convM2S went south: format error %ux %ux %ux %ux %ux",
 516                                mdata[0], mdata[1], mdata[2], mdata[3],
 517                                mdata[4]);
 518                        error(1, 0, "format error %ux %ux %ux %ux %ux",
 519                              mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]);
 520                        freejob(job);
 521                        continue;
 522                }
 523                /* stash the thread in the job so we can join them all
 524                 * later if we want to.
 525                 */
 526                if (pthread_create(&job->thread, &pth_attr, &job_thread, job)) {
 527                        error(1, 0, "%s: %r", "Failed to create job");
 528                        continue;
 529                }
 530        }
 531}
 532
 533static void rversion(struct job *job)
 534{
 535        if (job->request.msize > IOHDRSZ + Maxfdata)
 536                job->reply.msize = IOHDRSZ + Maxfdata;
 537        else
 538                job->reply.msize = job->request.msize;
 539        if (strncmp(job->request.version, "9P2000", 6) != 0)
 540                sendmsg(job, "unknown 9P version");
 541        else {
 542                job->reply.version = "9P2000";
 543                sendmsg(job, 0);
 544        }
 545}
 546
 547static void rauth(struct job *job)
 548{
 549        sendmsg(job, "cs: authentication not required");
 550}
 551
 552/*
 553 *  don't flush till all the threads  are done
 554 */
 555static void rflush(struct job *job)
 556{
 557        flushjob(job->request.oldtag);
 558        sendmsg(job, 0);
 559}
 560
 561static void rattach(struct job *job, struct mfile *mf)
 562{
 563        if (mf->busy == 0) {
 564                mf->busy = 1;
 565                mf->user = estrdup(job->request.uname);
 566        }
 567        mf->qid.vers = vers++;
 568        mf->qid.type = QTDIR;
 569        mf->qid.path = 0LL;
 570        job->reply.qid = mf->qid;
 571        sendmsg(job, 0);
 572}
 573
 574static char *rwalk(struct job *job, struct mfile *mf)
 575{
 576        char *err;
 577        char **elems;
 578        int nelems;
 579        int i;
 580        struct mfile *nmf;
 581        struct qid qid;
 582
 583        err = 0;
 584        nmf = NULL;
 585        elems = job->request.wname;
 586        nelems = job->request.nwname;
 587        job->reply.nwqid = 0;
 588
 589        if (job->request.newfid != job->request.fid) {
 590                /* clone fid */
 591                nmf = newfid(job->request.newfid);
 592                if (nmf->busy) {
 593                        nmf = NULL;
 594                        err = "clone to used channel";
 595                        goto send;
 596                }
 597                *nmf = *mf;
 598                nmf->user = estrdup(mf->user);
 599                nmf->fid = job->request.newfid;
 600                nmf->qid.vers = vers++;
 601                mf = nmf;
 602        }
 603        /* else nmf will be nil */
 604
 605        qid = mf->qid;
 606        if (nelems > 0) {
 607                /* walk fid */
 608                for (i = 0; i < nelems && i < MAXWELEM; i++) {
 609                        if ((qid.type & QTDIR) == 0) {
 610                                err = "not a directory";
 611                                break;
 612                        }
 613                        if (strcmp(elems[i], "..") == 0
 614                            || strcmp(elems[i], ".") == 0) {
 615                                qid.type = QTDIR;
 616                                qid.path = Qdir;
 617Found:
 618                                job->reply.wqid[i] = qid;
 619                                job->reply.nwqid++;
 620                                continue;
 621                        }
 622                        if (strcmp(elems[i], "cs") == 0) {
 623                                qid.type = QTFILE;
 624                                qid.path = Qcs;
 625                                goto Found;
 626                        }
 627                        err = "file does not exist";
 628                        break;
 629                }
 630        }
 631
 632send:
 633        if (nmf != NULL && (err != NULL || job->reply.nwqid < nelems)) {
 634                cleanmf(nmf);
 635                free(nmf->user);
 636                nmf->user = 0;
 637                nmf->busy = 0;
 638                nmf->fid = 0;
 639        }
 640        if (err == NULL)
 641                mf->qid = qid;
 642        sendmsg(job, err);
 643        return err;
 644}
 645
 646static void ropen(struct job *job, struct mfile *mf)
 647{
 648        int mode;
 649        char *err;
 650
 651        err = 0;
 652        mode = job->request.mode;
 653        if (mf->qid.type & QTDIR) {
 654                if (mode)
 655                        err = "permission denied";
 656        }
 657        job->reply.qid = mf->qid;
 658        job->reply.iounit = 0;
 659        sendmsg(job, err);
 660}
 661
 662static void rcreate(struct job *job, struct mfile *mf)
 663{
 664        sendmsg(job, "creation permission denied");
 665}
 666
 667static void rread(struct job *job, struct mfile *mf)
 668{
 669        int i, n, cnt;
 670        long off, toff, clock;
 671        struct dir dir;
 672        uint8_t buf[Maxfdata];
 673        char *err;
 674
 675        n = 0;
 676        err = 0;
 677        off = job->request.offset;
 678        cnt = job->request.count;
 679        if (mf->qid.type & QTDIR) {
 680                //      clock = time(0);
 681                if (off == 0) {
 682                        memset(&dir, 0, sizeof(dir));
 683                        dir.name = "cs";
 684                        dir.qid.type = QTFILE;
 685                        dir.qid.vers = vers;
 686                        dir.qid.path = Qcs;
 687                        dir.mode = 0666;
 688                        dir.length = 0;
 689                        dir.uid = mf->user;
 690                        dir.gid = mf->user;
 691                        dir.muid = mf->user;
 692                        dir.atime = clock; /* wrong */
 693                        dir.mtime = clock; /* wrong */
 694                        n = convD2M(&dir, buf, sizeof(buf));
 695                }
 696                job->reply.data = (char *)buf;
 697        } else {
 698                for (;;) {
 699                        /* look for an answer at the right offset */
 700                        toff = 0;
 701                        for (i = 0; mf->reply[i] && i < mf->nreply; i++) {
 702                                n = mf->replylen[i];
 703                                if (off < toff + n)
 704                                        break;
 705                                toff += n;
 706                        }
 707                        if (i < mf->nreply)
 708                                break; /* got something to return */
 709
 710                        /* try looking up more answers */
 711                        if (lookup(mf) == 0) {
 712                                /* no more */
 713                                n = 0;
 714                                goto send;
 715                        }
 716                }
 717
 718                /* give back a single reply (or part of one) */
 719                job->reply.data = mf->reply[i] + (off - toff);
 720                if (cnt > toff - off + n)
 721                        n = toff - off + n;
 722                else
 723                        n = cnt;
 724        }
 725send:
 726        job->reply.count = n;
 727        sendmsg(job, err);
 728}
 729
 730static void cleanmf(struct mfile *mf)
 731{
 732        int i;
 733
 734        if (mf->net != NULL) {
 735                free(mf->net);
 736                mf->net = NULL;
 737        }
 738        if (mf->host != NULL) {
 739                free(mf->host);
 740                mf->host = NULL;
 741        }
 742        if (mf->serv != NULL) {
 743                free(mf->serv);
 744                mf->serv = NULL;
 745        }
 746        if (mf->rem != NULL) {
 747                free(mf->rem);
 748                mf->rem = NULL;
 749        }
 750        for (i = 0; i < mf->nreply; i++) {
 751                free(mf->reply[i]);
 752                mf->reply[i] = NULL;
 753                mf->replylen[i] = 0;
 754        }
 755        mf->nreply = 0;
 756        mf->nextnet = netlist;
 757}
 758
 759static void rwrite(struct job *job, struct mfile *mf)
 760{
 761        int cnt, n;
 762        char *err;
 763        char *field[4];
 764        char curerr[64];
 765
 766        err = 0;
 767        cnt = job->request.count;
 768        if (mf->qid.type & QTDIR) {
 769                err = "can't write directory";
 770                goto send;
 771        }
 772        if (cnt >= Maxrequest) {
 773                err = "request too long";
 774                goto send;
 775        }
 776        job->request.data[cnt] = 0;
 777        /*
 778         *  toggle debugging
 779         */
 780        if (strncmp(job->request.data, "debug", 5) == 0) {
 781                debug ^= 1;
 782                fprintf(stderr, "CS:debug %d", debug);
 783                goto send;
 784        }
 785
 786        /*
 787         *  toggle ipv6 lookups
 788         */
 789        if (strncmp(job->request.data, "ipv6", 4) == 0) {
 790                ipv6lookups ^= 1;
 791                fprintf(stderr, "CS:ipv6lookups %d", ipv6lookups);
 792                goto send;
 793        }
 794
 795        /*
 796         *  toggle debugging
 797         */
 798        if (strncmp(job->request.data, "paranoia", 8) == 0) {
 799                paranoia ^= 1;
 800                fprintf(stderr, "CS:paranoia %d", paranoia);
 801                goto send;
 802        }
 803
 804        /*
 805         *  add networks to the default list
 806         */
 807        if (strncmp(job->request.data, "add ", 4) == 0) {
 808                if (job->request.data[cnt - 1] == '\n')
 809                        job->request.data[cnt - 1] = 0;
 810                netadd(job->request.data + 4);
 811                readipinterfaces();
 812                goto send;
 813        }
 814
 815        /*
 816         *  refresh all state
 817         */
 818        if (strncmp(job->request.data, "refresh", 7) == 0) {
 819                netinit(0 /*1*/);
 820                goto send;
 821        }
 822
 823        /* start transaction with a clean slate */
 824        cleanmf(mf);
 825
 826        /*
 827         *  look for a general query
 828         */
 829        if (*job->request.data == '!') {
 830                err = genquery(mf, job->request.data + 1);
 831                goto send;
 832        }
 833
 834        if (debug)
 835                fprintf(stderr, "CS:write %s", job->request.data);
 836        if (paranoia)
 837                fprintf(stderr, "CS:write %s by %s", job->request.data,
 838                        mf->user);
 839
 840        /*
 841         *  break up name
 842         */
 843        n = getfields(job->request.data, field, 4, 1, "!");
 844        switch (n) {
 845        case 1:
 846                mf->net = strdup("net");
 847                mf->host = strdup(field[0]);
 848                break;
 849        case 4:
 850                mf->rem = strdup(field[3]);
 851        /* fall through */
 852        case 3:
 853                mf->serv = strdup(field[2]);
 854        /* fall through */
 855        case 2:
 856                mf->host = strdup(field[1]);
 857                mf->net = strdup(field[0]);
 858                break;
 859        }
 860        /*
 861         *  do the first net worth of lookup
 862         */
 863        if (lookup(mf) == 0) {
 864                snprintf(curerr, sizeof(curerr), "%r");
 865                err = curerr;
 866        }
 867send:
 868        job->reply.count = cnt;
 869        sendmsg(job, err);
 870}
 871
 872static void rclunk(struct job *job, struct mfile *mf)
 873{
 874        cleanmf(mf);
 875        free(mf->user);
 876        mf->user = 0;
 877        mf->busy = 0;
 878        mf->fid = 0;
 879        sendmsg(job, 0);
 880}
 881
 882static void rremove(struct job *job, struct mfile *mf)
 883{
 884        sendmsg(job, "remove permission denied");
 885}
 886
 887static void rstat(struct job *job, struct mfile *mf)
 888{
 889        struct dir dir;
 890        uint8_t buf[IOHDRSZ + Maxfdata];
 891
 892        memset(&dir, 0, sizeof(dir));
 893        if (mf->qid.type & QTDIR) {
 894                dir.name = ".";
 895                dir.mode = DMDIR | 0555;
 896        } else {
 897                dir.name = "cs";
 898                dir.mode = 0666;
 899        }
 900        dir.qid = mf->qid;
 901        dir.length = 0;
 902        dir.uid = mf->user;
 903        dir.gid = mf->user;
 904        dir.muid = mf->user;
 905        // dir.atime = dir.mtime = time(0);
 906        job->reply.nstat = convD2M(&dir, buf, sizeof(buf));
 907        job->reply.stat = buf;
 908        sendmsg(job, 0);
 909}
 910
 911static void rwstat(struct job *job, struct mfile *mf)
 912{
 913        sendmsg(job, "wstat permission denied");
 914}
 915
 916static void sendmsg(struct job *job, char *err)
 917{
 918        int n;
 919        uint8_t mdata[IOHDRSZ + Maxfdata];
 920        char ename[ERRMAX];
 921
 922        if (err) {
 923                job->reply.type = Rerror;
 924                snprintf(ename, sizeof(ename), "cs: %s", err);
 925                job->reply.ename = ename;
 926        } else {
 927                job->reply.type = job->request.type + 1;
 928        }
 929        job->reply.tag = job->request.tag;
 930        n = convS2M(&job->reply, mdata, sizeof(mdata));
 931        if (n == 1) {
 932                fprintf(stderr, "CS:sendmsg convS2M of %F returns 0",
 933                        &job->reply);
 934                abort();
 935        }
 936        spinlock_lock(&joblock);
 937        if (job->flushed == 0)
 938                if (write(mfd[1], mdata, n) != n)
 939                        error(1, 0, "%s: %r", "mount write");
 940        spinlock_unlock(&joblock);
 941        if (debug)
 942                fprintf(stderr, "CS:%F %d", &job->reply, n);
 943}
 944
 945static int isvalidip(uint8_t *ip)
 946{
 947        return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0;
 948}
 949
 950static uint8_t loopbacknet[IPaddrlen] = {0, 0, 0,    0,    0,   0, 0, 0,
 951                                         0, 0, 0xff, 0xff, 127, 0, 0, 0};
 952static uint8_t loopbackmask[IPaddrlen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 953                                          0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 954                                          0xff, 0,    0,    0};
 955
 956static void readipinterfaces(void)
 957{
 958        if (myipaddr(ipa, mntpt) != 0)
 959                ipmove(ipa, IPnoaddr);
 960        snprintf(ipaddr, sizeof(ipaddr), "%I", ipa);
 961        if (debug)
 962                fprintf(stderr, "CS:dns", "ipaddr is %s\n", ipaddr);
 963}
 964
 965/*
 966 *  get the system name
 967 */
 968static void ipid(void)
 969{
 970        uint8_t addr[6];
 971        struct ndbtuple *t, *tt;
 972        char *p, *attr;
 973        struct ndbs s;
 974        int f;
 975        char buf[Maxpath];
 976
 977        /* use environment, ether addr, or ipaddr to get system name */
 978        if (mysysname == 0) {
 979                /*
 980                 *  environment has priority.
 981                 *
 982                 *  on the sgi power the default system name
 983                 *  is the ip address.  ignore that.
 984                 *
 985                 */
 986                p = getenv("sysname");
 987                if (p && *p) {
 988                        attr = ipattr(p);
 989                        if (strcmp(attr, "ip") != 0)
 990                                mysysname = strdup(p);
 991                }
 992
 993                /*
 994                 *  the /net/ndb contains what the network
 995                 *  figured out from DHCP.  use that name if
 996                 *  there is one.
 997                 */
 998                if (mysysname == 0 && netdb != NULL) {
 999                        ndbreopen(netdb);
1000                        for (tt = t = ndbparse(netdb); t != NULL; t = t->entry)
1001                        {
1002                                if (strcmp(t->attr, "sys") == 0) {
1003                                        mysysname = strdup(t->val);
1004                                        break;
1005                                }
1006                        }
1007                        ndbfree(tt);
1008                }
1009
1010                /* next network database, ip address, and ether address to find
1011                 * a name
1012                 */
1013                if (mysysname == 0) {
1014                        t = NULL;
1015                        if (isvalidip(ipa))
1016                                free(ndbgetvalue(db, &s, "ip", ipaddr, "sys",
1017                                                 &t));
1018                        if (t == NULL) {
1019                                for (f = 0; f < 3; f++) {
1020                                        snprintf(buf, sizeof(buf), "%s/ether%d",
1021                                                 mntpt, f);
1022                                        if (myetheraddr(addr, buf) < 0)
1023                                                continue;
1024                                        snprintf(eaddr, sizeof(eaddr), "%E",
1025                                                 addr);
1026                                        free(ndbgetvalue(db, &s, "ether", eaddr,
1027                                                         "sys", &t));
1028                                        if (t != NULL)
1029                                                break;
1030                                }
1031                        }
1032                        for (tt = t; tt != NULL; tt = tt->entry) {
1033                                if (strcmp(tt->attr, "sys") == 0) {
1034                                        mysysname = strdup(tt->val);
1035                                        break;
1036                                }
1037                        }
1038                        ndbfree(t);
1039                }
1040
1041                /* nothing else worked, use the ip address */
1042                if (mysysname == 0 && isvalidip(ipa))
1043                        mysysname = strdup(ipaddr);
1044
1045                /* set /dev/sysname if we now know it */
1046                if (mysysname) {
1047                        f = open("/dev/sysname", O_RDWR);
1048                        if (f >= 0) {
1049                                write(f, mysysname, strlen(mysysname));
1050                                close(f);
1051                        }
1052                }
1053        }
1054}
1055
1056/*
1057 *  Set up a list of default networks by looking for
1058 *  /net/^*^/clone.
1059 *  For now, never background.
1060 */
1061static void netinit(int background)
1062{
1063        char clone[Maxpath];
1064        struct network *np;
1065        static int working;
1066
1067        /* add the mounted networks to the default list */
1068        for (np = network; np->net; np++) {
1069                int fuckup;
1070
1071                if (np->considered)
1072                        continue;
1073                snprintf(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net);
1074                fuckup = open(clone, O_RDONLY);
1075                if (fuckup < 0)
1076                        continue;
1077                close(fuckup);
1078                // if(access(clone, R_OK))
1079                // continue;
1080                if (netlist)
1081                        last->next = np;
1082                else
1083                        netlist = np;
1084                last = np;
1085                np->next = 0;
1086                np->considered = 1;
1087        }
1088
1089        /* find out what our ip address is */
1090        readipinterfaces();
1091
1092        /* set the system name if we need to, these days ip is all we have */
1093        ipid();
1094
1095        if (debug)
1096                fprintf(stderr, logfile,
1097                        "CS:mysysname %s eaddr %s ipaddr %s ipa %I\n",
1098                        mysysname ? mysysname : "???", eaddr, ipaddr, ipa);
1099}
1100
1101/*
1102 *  add networks to the standard list
1103 */
1104static void netadd(char *p)
1105{
1106        struct network *np;
1107        char *field[12];
1108        int i, n;
1109
1110        n = getfields(p, field, 12, 1, " ");
1111        for (i = 0; i < n; i++) {
1112                for (np = network; np->net; np++) {
1113                        if (strcmp(field[i], np->net) != 0)
1114                                continue;
1115                        if (np->considered)
1116                                break;
1117                        if (netlist)
1118                                last->next = np;
1119                        else
1120                                netlist = np;
1121                        last = np;
1122                        np->next = 0;
1123                        np->considered = 1;
1124                }
1125        }
1126}
1127
1128static int lookforproto(struct ndbtuple *t, char *proto)
1129{
1130        for (; t != NULL; t = t->entry)
1131                if (strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0)
1132                        return 1;
1133        return 0;
1134}
1135
1136/*
1137 *  lookup a request.  the network "net" means we should pick the
1138 *  best network to get there.
1139 */
1140static int lookup(struct mfile *mf)
1141{
1142        struct network *np;
1143        char *cp;
1144        struct ndbtuple *nt, *t;
1145        char reply[Maxreply];
1146        int i, rv;
1147        int hack;
1148
1149        /* open up the standard db files */
1150        if (db == 0)
1151                ndbinit();
1152        if (db == 0)
1153                error(1, 0, "%s: %r", "can't open mf->network database\n");
1154
1155        rv = 0;
1156
1157        if (mf->net == NULL)
1158                return 0; /* must have been a genquery */
1159
1160        if (strcmp(mf->net, "net") == 0) {
1161                /*
1162                 *  go through set of default nets
1163                 */
1164                for (np = mf->nextnet; np; np = np->next) {
1165                        nt = (*np->lookup)(np, mf->host, mf->serv, 1);
1166                        if (nt == NULL)
1167                                continue;
1168                        hack = np->fasttimeouthack && !lookforproto(nt,
1169                                                                    np->net);
1170                        for (t = nt; mf->nreply < Nreply && t; t = t->entry) {
1171                                cp = (*np->trans)(t, np, mf->serv, mf->rem,
1172                                                  hack);
1173                                if (!cp)
1174                                        continue;
1175                                /* avoid duplicates */
1176                                for (i = 0; i < mf->nreply; i++)
1177                                        if (strcmp(mf->reply[i], cp) == 0)
1178                                                break;
1179                                if (i == mf->nreply) {
1180                                        /* save the reply */
1181                                        mf->replylen[mf->nreply] = strlen(cp);
1182                                        mf->reply[mf->nreply++] = cp;
1183                                        rv++;
1184                                }
1185                        }
1186                        ndbfree(nt);
1187                        np = np->next;
1188                        break;
1189                }
1190                mf->nextnet = np;
1191                return rv;
1192        }
1193
1194        /*
1195         *  if not /net, we only get one lookup
1196         */
1197        if (mf->nreply != 0)
1198                return 0;
1199        /*
1200         *  look for a specific network
1201         */
1202        for (np = netlist; np && np->net != NULL; np++) {
1203                if (np->fasttimeouthack)
1204                        continue;
1205                if (strcmp(np->net, mf->net) == 0)
1206                        break;
1207        }
1208
1209        if (np && np->net != NULL) {
1210                /*
1211                 *  known network
1212                 */
1213                nt = (*np->lookup)(np, mf->host, mf->serv, 1);
1214                for (t = nt; mf->nreply < Nreply && t; t = t->entry) {
1215                        cp = (*np->trans)(t, np, mf->serv, mf->rem, 0);
1216                        if (cp) {
1217                                mf->replylen[mf->nreply] = strlen(cp);
1218                                mf->reply[mf->nreply++] = cp;
1219                                rv++;
1220                        }
1221                }
1222                ndbfree(nt);
1223                return rv;
1224        }
1225        /*
1226         *  not a known network, don't translate host or service
1227         */
1228        if (mf->serv)
1229                snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s", mntpt,
1230                         mf->net, mf->host, mf->serv);
1231        else
1232                snprintf(reply, sizeof(reply), "%s/%s/clone %s", mntpt, mf->net,
1233                         mf->host);
1234        mf->reply[0] = strdup(reply);
1235        mf->replylen[0] = strlen(reply);
1236        mf->nreply = 1;
1237        return 1;
1238}
1239
1240/*
1241 *  translate an ip service name into a port number.  If it's a numeric port
1242 *  number, look for restricted access.
1243 *
1244 *  the service '*' needs no translation.
1245 */
1246static char *ipserv(struct network *np, char *name, char *buf, int blen)
1247{
1248        char *p;
1249        int alpha = 0;
1250        int restr = 0;
1251        char port[10];
1252        struct ndbtuple *t, *nt;
1253        struct ndbs s;
1254
1255        /* '*' means any service */
1256        if (strcmp(name, "*") == 0) {
1257                strlcpy(buf, name, blen);
1258                return buf;
1259        }
1260
1261        /*  see if it's numeric or symbolic */
1262        port[0] = 0;
1263        for (p = name; *p; p++) {
1264                if (!isdigit(*p)) {
1265                        if (isalpha(*p) || *p == '-' || *p == '$')
1266                                alpha = 1;
1267                        else
1268                                return 0;
1269                }
1270        }
1271        t = NULL;
1272        p = NULL;
1273        if (alpha) {
1274                p = ndbgetvalue(db, &s, np->net, name, "port", &t);
1275                if (p == NULL)
1276                        return 0;
1277        } else {
1278                /* look up only for tcp ports < 1024 to get the restricted
1279                 * attribute
1280                 */
1281                if (atoi(name) < 1024 && strcmp(np->net, "tcp") == 0)
1282                        p = ndbgetvalue(db, &s, "port", name, "port", &t);
1283                if (p == NULL)
1284                        p = strdup(name);
1285        }
1286
1287        if (t) {
1288                for (nt = t; nt; nt = nt->entry)
1289                        if (strcmp(nt->attr, "restricted") == 0)
1290                                restr = 1;
1291                ndbfree(t);
1292        }
1293        snprintf(buf, blen, "%s%s", p, restr ? "!r" : "");
1294        free(p);
1295        return buf;
1296}
1297
1298/*
1299 *  lookup an ip attribute
1300 */
1301static int ipattrlookup(struct ndb *db, char *ipa, char *attr, char *val,
1302                        int vlen)
1303{
1304
1305        struct ndbtuple *t, *nt;
1306        char *alist[2];
1307
1308        alist[0] = attr;
1309        t = ndbipinfo(db, "ip", ipa, alist, 1);
1310        if (t == NULL)
1311                return 0;
1312        for (nt = t; nt != NULL; nt = nt->entry) {
1313                if (strcmp(nt->attr, attr) == 0) {
1314                        strlcpy(val, nt->val, vlen);
1315                        ndbfree(t);
1316                        return 1;
1317                }
1318        }
1319
1320        /* we shouldn't get here */
1321        ndbfree(t);
1322        return 0;
1323}
1324
1325/*
1326 *  lookup (and translate) an ip destination
1327 */
1328static struct ndbtuple *iplookup(struct network *np, char *host, char *serv,
1329                                 int nolookup)
1330{
1331        char *attr, *dnsname;
1332        struct ndbtuple *t, *nt;
1333        struct ndbs s;
1334        char ts[Maxservice];
1335        char dollar[Maxhost];
1336        uint8_t ip[IPaddrlen];
1337        uint8_t net[IPaddrlen];
1338        uint8_t tnet[IPaddrlen];
1339        struct ipifc *ifc;
1340        struct iplifc *lifc;
1341
1342        /*
1343         *  start with the service since it's the most likely to fail
1344         *  and costs the least
1345         */
1346        werrstr("can't translate address");
1347        if (serv == 0 || ipserv(np, serv, ts, sizeof(ts)) == 0) {
1348                werrstr("can't translate service");
1349                return 0;
1350        }
1351
1352        /* for dial strings with no host */
1353        if (strcmp(host, "*") == 0)
1354                return ndbnew("ip", "*");
1355
1356        /*
1357         *  hack till we go v6 :: = 0.0.0.0
1358         */
1359        if (strcmp("::", host) == 0)
1360                return ndbnew("ip", "*");
1361
1362        /*
1363         *  '$' means the rest of the name is an attribute that we
1364         *  need to search for
1365         */
1366        if (*host == '$') {
1367                if (ipattrlookup(db, ipaddr, host + 1, dollar, sizeof(dollar)))
1368                        host = dollar;
1369        }
1370
1371        /*
1372         *  turn '[ip address]' into just 'ip address'
1373         */
1374        if (*host == '[' && host[strlen(host) - 1] == ']') {
1375                host++;
1376                host[strlen(host) - 1] = 0;
1377        }
1378
1379        /*
1380         *  just accept addresses
1381         */
1382        attr = ipattr(host);
1383        if (strcmp(attr, "ip") == 0)
1384                return ndbnew("ip", host);
1385
1386        /*
1387         *  give the domain name server the first opportunity to
1388         *  resolve domain names.  if that fails try the database.
1389         */
1390        t = 0;
1391        werrstr("can't translate address");
1392        if (strcmp(attr, "dom") == 0)
1393                t = dnsiplookup(host, &s);
1394        if (t == 0)
1395                free(ndbgetvalue(db, &s, attr, host, "ip", &t));
1396        if (t == 0) {
1397                dnsname = ndbgetvalue(db, &s, attr, host, "dom", NULL);
1398                if (dnsname) {
1399                        t = dnsiplookup(dnsname, &s);
1400                        free(dnsname);
1401                }
1402        }
1403        if (t == 0)
1404                t = dnsiplookup(host, &s);
1405        if (t == 0)
1406                return 0;
1407
1408        /*
1409         *  reorder the tuple to have the matched line first and
1410         *  save that in the request structure.
1411         */
1412        t = reorder(t, s.t);
1413
1414        /*
1415         * reorder according to our interfaces
1416         */
1417        spinlock_lock(&ipifclock);
1418        for (ifc = ipifcs; ifc != NULL; ifc = ifc->next) {
1419                for (lifc = ifc->lifc; lifc != NULL; lifc = lifc->next) {
1420                        maskip(lifc->ip, lifc->mask, net);
1421                        for (nt = t; nt; nt = nt->entry) {
1422                                if (strcmp(nt->attr, "ip") != 0)
1423                                        continue;
1424                                parseip(ip, nt->val);
1425                                maskip(ip, lifc->mask, tnet);
1426                                if (memcmp(net, tnet, IPaddrlen) == 0) {
1427                                        t = reorder(t, nt);
1428                                        spinlock_unlock(&ipifclock);
1429                                        return t;
1430                                }
1431                        }
1432                }
1433        }
1434        spinlock_unlock(&ipifclock);
1435
1436        return t;
1437}
1438
1439/*
1440 *  translate an ip address
1441 */
1442static char *iptrans(struct ndbtuple *t, struct network *np, char *serv,
1443                     char *rem, int hack)
1444{
1445        char ts[Maxservice];
1446        char reply[Maxreply];
1447        char x[Maxservice];
1448
1449        if (strcmp(t->attr, "ip") != 0)
1450                return 0;
1451
1452        if (serv == 0 || ipserv(np, serv, ts, sizeof(ts)) == 0) {
1453                werrstr("can't translate service");
1454                return 0;
1455        }
1456        if (rem != NULL)
1457                snprintf(x, sizeof(x), "!%s", rem);
1458        else
1459                *x = 0;
1460
1461        if (*t->val == '*')
1462                snprintf(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt,
1463                         np->net, ts, x);
1464        else
1465                snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s", mntpt,
1466                         np->net, t->val, ts, x, hack ? "!fasttimeout" : "");
1467
1468        return strdup(reply);
1469}
1470
1471/*
1472 *  lookup a telephone number
1473 */
1474static struct ndbtuple *telcolookup(struct network *np, char *host, char *serv,
1475                                    int nolookup)
1476{
1477        struct ndbtuple *t;
1478        struct ndbs s;
1479
1480        werrstr("can't translate address");
1481        free(ndbgetvalue(db, &s, "sys", host, "telco", &t));
1482        if (t == 0)
1483                return ndbnew("telco", host);
1484
1485        return reorder(t, s.t);
1486}
1487
1488/*
1489 *  translate a telephone address
1490 */
1491static char *telcotrans(struct ndbtuple *t, struct network *np, char *serv,
1492                        char *rem, int unused)
1493{
1494        char reply[Maxreply];
1495        char x[Maxservice];
1496
1497        if (strcmp(t->attr, "telco") != 0)
1498                return 0;
1499
1500        if (rem != NULL)
1501                snprintf(x, sizeof(x), "!%s", rem);
1502        else
1503                *x = 0;
1504        if (serv)
1505                snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt,
1506                         np->net, t->val, serv, x);
1507        else
1508                snprintf(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt,
1509                         np->net, t->val, x);
1510        return strdup(reply);
1511}
1512
1513/*
1514 *  reorder the tuple to put x's line first in the entry
1515 */
1516static struct ndbtuple *reorder(struct ndbtuple *t, struct ndbtuple *x)
1517{
1518        struct ndbtuple *nt;
1519        struct ndbtuple *line;
1520
1521        /* find start of this entry's line */
1522        for (line = x; line->entry == line->line; line = line->line)
1523                ;
1524        line = line->line;
1525        if (line == t)
1526                return t; /* already the first line */
1527
1528        /* remove this line and everything after it from the entry */
1529        for (nt = t; nt->entry != line; nt = nt->entry)
1530                ;
1531        nt->entry = 0;
1532
1533        /* make that the start of the entry */
1534        for (nt = line; nt->entry; nt = nt->entry)
1535                ;
1536        nt->entry = t;
1537        return line;
1538}
1539
1540static struct ndbtuple *dnsip6lookup(char *mntpt, char *buf, struct ndbtuple *t)
1541{
1542        struct ndbtuple *t6, *tt;
1543
1544        t6 = dnsquery(mntpt, buf, "ipv6"); /* lookup AAAA dns RRs */
1545        if (t6 == NULL)
1546                return t;
1547
1548        /* convert ipv6 attr to ip */
1549        for (tt = t6; tt != NULL; tt = tt->entry)
1550                if (strcmp(tt->attr, "ipv6") == 0)
1551                        strlcpy(tt->attr, "ip", sizeof(tt->attr));
1552
1553        if (t == NULL)
1554                return t6;
1555
1556        /* append t6 list to t list */
1557        for (tt = t; tt->entry != NULL; tt = tt->entry)
1558                ;
1559        tt->entry = t6;
1560        return t;
1561}
1562
1563/*
1564 *  call the dns process and have it try to translate a name
1565 */
1566static struct ndbtuple *dnsiplookup(char *host, struct ndbs *s)
1567{
1568        char buf[Maxreply];
1569        struct ndbtuple *t;
1570
1571        spinlock_unlock(&dblock);
1572
1573        /* save the name */
1574        snprintf(buf, sizeof(buf), "%s", host);
1575
1576        if (strcmp(ipattr(buf), "ip") == 0)
1577                t = dnsquery(mntpt, buf, "ptr");
1578        else {
1579                t = dnsquery(mntpt, buf, "ip");
1580                /* special case: query ipv6 (AAAA dns RR) too */
1581                if (ipv6lookups)
1582                        t = dnsip6lookup(mntpt, buf, t);
1583        }
1584        s->t = t;
1585
1586        if (t == NULL) {
1587                snprintf(buf, sizeof(buf), "%r");
1588                if (strstr(buf, "exist"))
1589                        werrstr("can't translate address: %s", buf);
1590                else if (strstr(buf, "dns failure"))
1591                        werrstr("temporary problem: %s", buf);
1592        }
1593
1594        spinlock_lock(&dblock);
1595        return t;
1596}
1597
1598static int qmatch(struct ndbtuple *t, char **attr, char **val, int n)
1599{
1600        int i, found;
1601        struct ndbtuple *nt;
1602
1603        for (i = 1; i < n; i++) {
1604                found = 0;
1605                for (nt = t; nt; nt = nt->entry)
1606                        if (strcmp(attr[i], nt->attr) == 0)
1607                                if (strcmp(val[i], "*") == 0 ||
1608                                    strcmp(val[i], nt->val) == 0) {
1609                                        found = 1;
1610                                        break;
1611                                }
1612                if (found == 0)
1613                        break;
1614        }
1615        return i == n;
1616}
1617
1618/* this is awful but I don't want to bring in libstring just for this.
1619 * you want real strings don't use C
1620 */
1621static void qreply(struct mfile *mf, struct ndbtuple *t)
1622{
1623        struct ndbtuple *nt;
1624        char *s, *cur;
1625        int len, amt;
1626
1627        s = malloc(4096);
1628        cur = s;
1629        len = 4096;
1630
1631        for (nt = t; mf->nreply < Nreply && nt; nt = nt->entry) {
1632                amt = snprintf(cur, len, "%s=%s", nt->attr, nt->val);
1633
1634                if (amt < 0)
1635                        len = 0;
1636                else {
1637                        len -= amt;
1638                        cur += amt;
1639                }
1640
1641                if (nt->line != nt->entry) {
1642                        mf->replylen[mf->nreply] = strlen(s);
1643                        mf->reply[mf->nreply++] = strdup(s);
1644                        cur = s;
1645                        len = 4096;
1646                } else {
1647                        amt = snprintf(cur, len, " ");
1648                        if (amt < 0)
1649                                len = 0;
1650                        else {
1651                                len -= amt;
1652                                cur += amt;
1653                        }
1654                }
1655        }
1656        free(s);
1657}
1658
1659enum {
1660        Maxattr = 32,
1661};
1662
1663/*
1664 *  generic query lookup.  The query is of one of the following
1665 *  forms:
1666 *
1667 *  attr1=val1 attr2=val2 attr3=val3 ...
1668 *
1669 *  returns the matching tuple
1670 *
1671 *  ipinfo attr=val attr1 attr2 attr3 ...
1672 *
1673 *  is like ipinfo and returns the attr{1-n}
1674 *  associated with the ip address.
1675 */
1676static char *genquery(struct mfile *mf, char *query)
1677{
1678        int i, n;
1679        char *p;
1680        char *attr[Maxattr];
1681        char *val[Maxattr];
1682        struct ndbtuple *t;
1683        struct ndbs s;
1684
1685        n = getfields(query, attr, COUNT_OF(attr), 1, " ");
1686        if (n == 0)
1687                return "bad query";
1688
1689        if (strcmp(attr[0], "ipinfo") == 0)
1690                return ipinfoquery(mf, attr, n);
1691
1692        /* parse pairs */
1693        for (i = 0; i < n; i++) {
1694                p = strchr(attr[i], '=');
1695                if (p == 0)
1696                        return "bad query";
1697                *p++ = 0;
1698                val[i] = p;
1699        }
1700
1701        /* give dns a chance */
1702        if ((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0)
1703            && val[0]) {
1704                t = dnsiplookup(val[0], &s);
1705                if (t) {
1706                        if (qmatch(t, attr, val, n)) {
1707                                qreply(mf, t);
1708                                ndbfree(t);
1709                                return 0;
1710                        }
1711                        ndbfree(t);
1712                }
1713        }
1714
1715        /* first pair is always the key.  It can't be a '*' */
1716        t = ndbsearch(db, &s, attr[0], val[0]);
1717
1718        /* search is the and of all the pairs */
1719        while (t) {
1720                if (qmatch(t, attr, val, n)) {
1721                        qreply(mf, t);
1722                        ndbfree(t);
1723                        return 0;
1724                }
1725
1726                ndbfree(t);
1727                t = ndbsnext(&s, attr[0], val[0]);
1728        }
1729
1730        return "no match";
1731}
1732
1733/*
1734 *  resolve an ip address
1735 */
1736static struct ndbtuple *ipresolve(char *attr, char *host)
1737{
1738        struct ndbtuple *t, *nt, **l;
1739
1740        t = iplookup(&network[0], host, "*", 0);
1741        for (l = &t; *l != NULL;) {
1742                nt = *l;
1743                if (strcmp(nt->attr, "ip") != 0) {
1744                        *l = nt->entry;
1745                        nt->entry = NULL;
1746                        ndbfree(nt);
1747                        continue;
1748                }
1749                strlcpy(nt->attr, attr, sizeof(nt->attr));
1750                l = &nt->entry;
1751        }
1752        return t;
1753}
1754
1755static char *ipinfoquery(struct mfile *mf, char **list, int n)
1756{
1757        int i, nresolve;
1758        int resolve[Maxattr];
1759        struct ndbtuple *t, *nt, **l;
1760        char *attr, *val;
1761
1762        /* skip 'ipinfo' */
1763        list++;
1764        n--;
1765
1766        if (n < 1)
1767                return "bad query";
1768
1769        /* get search attribute=value, or assume ip=myipaddr */
1770        attr = *list;
1771        val = strchr(attr, '=');
1772        if (val != NULL) {
1773                *val++ = 0;
1774                list++;
1775                n--;
1776        } else {
1777                attr = "ip";
1778                val = ipaddr;
1779        }
1780
1781        if (n < 1)
1782                return "bad query";
1783
1784        /*
1785         *  don't let ndbipinfo resolve the addresses, we're
1786         *  better at it.
1787         */
1788        nresolve = 0;
1789        for (i = 0; i < n; i++)
1790                if (*list[i] == '@') { /* @attr=val ? */
1791                        list[i]++;
1792                        resolve[i] = 1; /* we'll resolve it */
1793                        nresolve++;
1794                } else
1795                        resolve[i] = 0;
1796
1797        t = ndbipinfo(db, attr, val, list, n);
1798        if (t == NULL)
1799                return "no match";
1800
1801        if (nresolve != 0) {
1802                for (l = &t; *l != NULL;) {
1803                        nt = *l;
1804
1805                        /* already an address? */
1806                        if (strcmp(ipattr(nt->val), "ip") == 0) {
1807                                l = &(*l)->entry;
1808                                continue;
1809                        }
1810
1811                        /* user wants it resolved? */
1812                        for (i = 0; i < n; i++)
1813                                if (strcmp(list[i], nt->attr) == 0)
1814                                        break;
1815                        if (i >= n || resolve[i] == 0) {
1816                                l = &(*l)->entry;
1817                                continue;
1818                        }
1819
1820                        /* resolve address and replace entry */
1821                        *l = ipresolve(nt->attr, nt->val);
1822                        while (*l != NULL)
1823                                l = &(*l)->entry;
1824                        *l = nt->entry;
1825
1826                        nt->entry = NULL;
1827                        ndbfree(nt);
1828                }
1829        }
1830
1831        /* make it all one line */
1832        for (nt = t; nt != NULL; nt = nt->entry) {
1833                if (nt->entry == NULL)
1834                        nt->line = t;
1835                else
1836                        nt->line = nt->entry;
1837        }
1838
1839        qreply(mf, t);
1840
1841        return NULL;
1842}
1843
1844static void *emalloc(int size)
1845{
1846        void *x;
1847
1848        x = calloc(size, 1);
1849        if (x == NULL)
1850                abort();
1851        memset(x, 0, size);
1852        return x;
1853}
1854
1855static char *estrdup(char *s)
1856{
1857        int size;
1858        char *p;
1859
1860        size = strlen(s) + 1;
1861        p = calloc(size, 1);
1862        if (p == NULL)
1863                abort();
1864        memmove(p, s, size);
1865        return p;
1866}
1867