|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include <ctype.h> | 
|  | #include <error.h> | 
|  | #include <fcall.h> | 
|  | #include <fcntl.h> | 
|  | #include <iplib/iplib.h> | 
|  | #include <ndblib/fcallfmt.h> | 
|  | #include <ndblib/ndb.h> | 
|  | #include <parlib/parlib.h> | 
|  | #include <parlib/spinlock.h> | 
|  | #include <pthread.h> | 
|  | #include <ros/common.h> | 
|  | #include <signal.h> | 
|  | #include <stdio.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/types.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | enum { | 
|  | Nreply = 20, | 
|  | Maxreply = 256, | 
|  | Maxrequest = 128, | 
|  | Maxpath = 128, | 
|  | Maxfdata = 8192, | 
|  | Maxhost = 64,    /* maximum host name size */ | 
|  | Maxservice = 64, /* maximum service name size */ | 
|  |  | 
|  | Qdir = 0, | 
|  | Qcs = 1, | 
|  | }; | 
|  |  | 
|  | int vers; /* incremented each clone/attach */ | 
|  | /* need to resolve the #inluce for all this stuff. */ | 
|  | #define DMDIR 0x80000000 /* mode bit for directories */ | 
|  | #define QTDIR 0x80 | 
|  | #define QTFILE 0 | 
|  | #define ERRMAX 128 | 
|  |  | 
|  | struct mfile { | 
|  | int busy; | 
|  |  | 
|  | char *user; | 
|  | struct qid qid; | 
|  | int fid; | 
|  |  | 
|  | /* | 
|  | *  current request | 
|  | */ | 
|  | char *net; | 
|  | char *host; | 
|  | char *serv; | 
|  | char *rem; | 
|  |  | 
|  | /* | 
|  | *  result of the last lookup | 
|  | */ | 
|  | struct network *nextnet; | 
|  | int nreply; | 
|  | char *reply[Nreply]; | 
|  | int replylen[Nreply]; | 
|  | }; | 
|  |  | 
|  | struct mlist { | 
|  | struct mlist *next; | 
|  | struct mfile mf; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | *  active requests | 
|  | */ | 
|  | struct job { | 
|  | struct job *next; | 
|  | int flushed; | 
|  | struct fcall request; | 
|  | struct fcall reply; | 
|  | pthread_t thread; | 
|  | }; | 
|  |  | 
|  | spinlock_t joblock = SPINLOCK_INITIALIZER; | 
|  | struct job *joblist; | 
|  |  | 
|  | struct mlist *mlist; | 
|  | int mfd[2]; | 
|  | int debug; | 
|  | int paranoia; | 
|  | int ipv6lookups = 1; | 
|  | int server; | 
|  | char *dbfile = "lib/ndb/local"; | 
|  | struct ndb *db, *netdb; | 
|  |  | 
|  | static void rversion(struct job *); | 
|  | static void rflush(struct job *); | 
|  | static void rattach(struct job *, struct mfile *); | 
|  | static char *rwalk(struct job *, struct mfile *); | 
|  | static void ropen(struct job *, struct mfile *); | 
|  | static void rcreate(struct job *, struct mfile *); | 
|  | static void rread(struct job *, struct mfile *); | 
|  | static void rwrite(struct job *, struct mfile *); | 
|  | static void rclunk(struct job *, struct mfile *); | 
|  | static void rremove(struct job *, struct mfile *); | 
|  | static void rstat(struct job *, struct mfile *); | 
|  | static void rwstat(struct job *, struct mfile *); | 
|  | static void rauth(struct job *); | 
|  | static void sendmsg(struct job *, char *); | 
|  | static void mountinit(char *, char *); | 
|  | static void io(void); | 
|  | static void ndbinit(void); | 
|  | static void netinit(int); | 
|  | static void netadd(char *); | 
|  | static char *genquery(struct mfile *, char *); | 
|  | static char *ipinfoquery(struct mfile *, char **, int); | 
|  | static int needproto(struct network *, struct ndbtuple *); | 
|  | static int lookup(struct mfile *); | 
|  | static struct ndbtuple *reorder(struct ndbtuple *, struct ndbtuple *); | 
|  | static void ipid(void); | 
|  | static void readipinterfaces(void); | 
|  | static void *emalloc(int); | 
|  | static char *estrdup(char *); | 
|  | static struct job *newjob(void); | 
|  | static void freejob(struct job *); | 
|  | static void setext(char *, int, char *); | 
|  | static void cleanmf(struct mfile *); | 
|  |  | 
|  | spinlock_t dblock = SPINLOCK_INITIALIZER;  /* mutex on database operations */ | 
|  | spinlock_t netlock = SPINLOCK_INITIALIZER; /* mutex for netinit() */ | 
|  |  | 
|  | char *logfile = "cs"; | 
|  | char *paranoiafile = "cs.paranoia"; | 
|  |  | 
|  | char mntpt[Maxpath]; | 
|  | char netndb[Maxpath]; | 
|  |  | 
|  | /* | 
|  | *  Network specific translators | 
|  | */ | 
|  | static struct ndbtuple *iplookup(struct network *, char *, char *, int); | 
|  | static char *iptrans(struct ndbtuple *, struct network *, char *, char *, int); | 
|  | static struct ndbtuple *telcolookup(struct network *, char *, char *, int); | 
|  | static char *telcotrans(struct ndbtuple *, struct network *, char *, char *, | 
|  | int); | 
|  | static struct ndbtuple *dnsiplookup(char *, struct ndbs *); | 
|  |  | 
|  | struct network { | 
|  | char *net; | 
|  | struct ndbtuple *(*lookup)(struct network *, char *, char *, int); | 
|  | char *(*trans)(struct ndbtuple *, struct network *, char *, char *, int); | 
|  | int considered;      /* flag: ignored for "net!"? */ | 
|  | int fasttimeouthack; /* flag. was for IL */ | 
|  | struct network *next; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | *  net doesn't apply to (r)udp, icmp(v6), or telco (for speed). | 
|  | */ | 
|  | struct network network[] = { | 
|  | {"tcp", iplookup, iptrans, 0}, | 
|  | {"udp", iplookup, iptrans, 1}, | 
|  | {"icmp", iplookup, iptrans, 1}, | 
|  | {"icmpv6", iplookup, iptrans, 1}, | 
|  | {"rudp", iplookup, iptrans, 1}, | 
|  | {"ssh", iplookup, iptrans, 1}, | 
|  | {"telco", telcolookup, telcotrans, 1},  {0}, | 
|  | }; | 
|  |  | 
|  | spinlock_t ipifclock = SPINLOCK_INITIALIZER; | 
|  | struct ipifc *ipifcs; | 
|  |  | 
|  | char eaddr[16];         /* ascii ethernet address */ | 
|  | char ipaddr[64];        /* ascii internet address */ | 
|  | uint8_t ipa[IPaddrlen]; /* binary internet address */ | 
|  | char *mysysname; | 
|  |  | 
|  | struct network *netlist; /* networks ordered by preference */ | 
|  | struct network *last; | 
|  |  | 
|  | char *argv0; | 
|  |  | 
|  | static void evnotify(int rc) | 
|  | { | 
|  | struct event_msg msg = { 0 }; | 
|  |  | 
|  | msg.ev_type = EV_USER_IPI; | 
|  | msg.ev_arg1 = rc; | 
|  | sys_notify(getppid(), EV_USER_IPI, &msg); | 
|  | } | 
|  |  | 
|  | static void evexit(int rc) | 
|  | { | 
|  | if (server) | 
|  | evnotify(rc); | 
|  | exit(rc); | 
|  | } | 
|  |  | 
|  | static void usage(void) | 
|  | { | 
|  | fprintf(stderr, "CS:usage: %s [-dn] [-f ndb-file] [-x netmtpt]\n", argv0); | 
|  | fprintf(stderr, "CS:usage"); | 
|  | evexit(1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * based on libthread's threadsetname, but drags in less library code. | 
|  | * actually just sets the arguments displayed. | 
|  | */ | 
|  | static void procsetname(char *fmt, ...) | 
|  | { | 
|  | /* someday ... */ | 
|  | #if 0 | 
|  | int fd; | 
|  | char *cmdname; | 
|  | char buf[128]; | 
|  | va_list arg; | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | cmdname = vsmprint(fmt, arg); | 
|  | va_end(arg); | 
|  | if (cmdname == NULL) | 
|  | return; | 
|  | snprintf(buf, sizeof(buf), "#proc/%d/args", getpid()); | 
|  | fd = open(buf, OWRITE); | 
|  | if (fd >= 0) { | 
|  | write(fd, cmdname, strlen(cmdname)+1); | 
|  | close(fd); | 
|  | } | 
|  | free(cmdname); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | int main(int argc, char *argv[]) | 
|  | { | 
|  | int justsetname, ch; | 
|  | char ext[Maxpath], servefile[Maxpath]; | 
|  |  | 
|  | /* Make us an SCP with a 2LS */ | 
|  | parlib_wants_to_be_mcp = FALSE; | 
|  | register_printf_specifier('F', printf_fcall, printf_fcall_info); | 
|  |  | 
|  | argv0 = argv[0]; | 
|  | justsetname = 0; | 
|  | setnetmtpt(mntpt, sizeof(mntpt), NULL); | 
|  | ext[0] = 0; | 
|  | while ((ch = getopt(argc, argv, "4df:nSx:")) != -1) { | 
|  | switch (ch) { | 
|  | case '4': | 
|  | ipv6lookups = 0; | 
|  | break; | 
|  | case 'd': | 
|  | debug = 1; | 
|  | break; | 
|  | case 'f': | 
|  | dbfile = optarg; | 
|  | break; | 
|  | case 'n': | 
|  | justsetname = 1; | 
|  | break; | 
|  | case 'S': | 
|  | server = 1; | 
|  | break; | 
|  | case 'x': | 
|  | setnetmtpt(mntpt, sizeof(mntpt), optarg); | 
|  | setext(ext, sizeof(ext), mntpt); | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | snprintf(servefile, sizeof(servefile), "#srv/cs%s", ext); | 
|  | snprintf(netndb, sizeof(netndb), "%s/ndb", mntpt); | 
|  | syscall(SYS_nunmount, (unsigned long)servefile, strlen(servefile), | 
|  | (unsigned long)mntpt, strlen(mntpt)); | 
|  | remove(servefile); | 
|  |  | 
|  | ndbinit(); | 
|  | netinit(0); | 
|  |  | 
|  | if (!justsetname) { | 
|  | mountinit(servefile, mntpt); | 
|  | if (server) | 
|  | evnotify(0); | 
|  | io(); | 
|  | } | 
|  |  | 
|  | evexit(0); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  if a mount point is specified, set the cs extention to be the mount point | 
|  | *  with '_'s replacing '/'s | 
|  | */ | 
|  | static void setext(char *ext, int n, char *p) | 
|  | { | 
|  | int i, c; | 
|  |  | 
|  | n--; | 
|  | for (i = 0; i < n; i++) { | 
|  | c = p[i]; | 
|  | if (c == 0) | 
|  | break; | 
|  | if (c == '/') | 
|  | c = '_'; | 
|  | ext[i] = c; | 
|  | } | 
|  | ext[i] = 0; | 
|  | } | 
|  |  | 
|  | static void mountinit(char *service, char *mntpt) | 
|  | { | 
|  | int f; | 
|  | int p[2]; | 
|  | char buf[32]; | 
|  | int ret; | 
|  |  | 
|  | ret = pipe(p); | 
|  | if (ret < 0) { | 
|  | error(1, 0, "pipe: %r"); | 
|  | evexit(1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  make a /srv/cs | 
|  | * ORCLOSE means remove on last close. Handy. Not here yet. | 
|  | */ | 
|  | f = open(service, O_WRONLY | O_CREAT /*|ORCLOSE*/, 0666); | 
|  | if (f < 0) | 
|  | error(1, 0, "%s: %r", service); | 
|  | snprintf(buf, sizeof(buf), "%d", p[1]); | 
|  | if (write(f, buf, strlen(buf)) != strlen(buf)) | 
|  | error(1, 0, "Write %s: %r", service); | 
|  | /* using #s: we create a pipe and drop it into #srv. | 
|  | * we no longer mount. That's up to you. | 
|  | * #srv will route requests to us. | 
|  | */ | 
|  | close(p[1]); | 
|  |  | 
|  | mfd[0] = mfd[1] = p[0]; | 
|  | } | 
|  |  | 
|  | static void ndbinit(void) | 
|  | { | 
|  | db = ndbopen(dbfile); | 
|  | if (db == NULL) | 
|  | error(1, 0, "%s: %r", "can't open network database"); | 
|  |  | 
|  | netdb = ndbopen(netndb); | 
|  | if (netdb != NULL) { | 
|  | netdb->nohash = 1; | 
|  | db = ndbcat(netdb, db); | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct mfile *newfid(int fid) | 
|  | { | 
|  | struct mlist *f, *ff; | 
|  | struct mfile *mf; | 
|  |  | 
|  | ff = 0; | 
|  | for (f = mlist; f; f = f->next) | 
|  | if (f->mf.busy && f->mf.fid == fid) | 
|  | return &f->mf; | 
|  | else if (!ff && !f->mf.busy) | 
|  | ff = f; | 
|  | if (ff == 0) { | 
|  | ff = emalloc(sizeof(*f)); | 
|  | ff->next = mlist; | 
|  | mlist = ff; | 
|  | } | 
|  | mf = &ff->mf; | 
|  | memset(mf, 0, sizeof(*mf)); | 
|  | mf->fid = fid; | 
|  | return mf; | 
|  | } | 
|  |  | 
|  | static struct job *newjob(void) | 
|  | { | 
|  | struct job *job; | 
|  |  | 
|  | job = calloc(1, sizeof(struct job)); | 
|  | if (!job) | 
|  | error(1, 0, "%s: %r", "job calloc"); | 
|  | spinlock_lock(&joblock); | 
|  | job->next = joblist; | 
|  | joblist = job; | 
|  | job->request.tag = -1; | 
|  | spinlock_unlock(&joblock); | 
|  | return job; | 
|  | } | 
|  |  | 
|  | static void freejob(struct job *job) | 
|  | { | 
|  | struct job **l; | 
|  | struct job *to_free = 0; | 
|  |  | 
|  | spinlock_lock(&joblock); | 
|  | for (l = &joblist; *l; l = &(*l)->next) { | 
|  | if ((*l) == job) { | 
|  | *l = job->next; | 
|  | to_free = job; | 
|  | break; | 
|  | } | 
|  | } | 
|  | spinlock_unlock(&joblock); | 
|  | if (to_free) | 
|  | free(to_free); | 
|  | } | 
|  |  | 
|  | static void flushjob(int tag) | 
|  | { | 
|  | struct job *job; | 
|  |  | 
|  | spinlock_lock(&joblock); | 
|  | for (job = joblist; job; job = job->next) { | 
|  | if (job->request.tag == tag && job->request.type != Tflush) { | 
|  | job->flushed = 1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | spinlock_unlock(&joblock); | 
|  | } | 
|  |  | 
|  | static void *job_thread(void *arg) | 
|  | { | 
|  | struct mfile *mf; | 
|  | struct job *job = arg; | 
|  |  | 
|  | spinlock_lock(&dblock); | 
|  | mf = newfid(job->request.fid); | 
|  |  | 
|  | if (debug) | 
|  | fprintf(stderr, "CS:%F", &job->request); | 
|  | switch (job->request.type) { | 
|  | default: | 
|  | fprintf(stderr, "CS:unknown request type %d", job->request.type); | 
|  | break; | 
|  | case Tversion: | 
|  | rversion(job); | 
|  | break; | 
|  | case Tauth: | 
|  | rauth(job); | 
|  | break; | 
|  | case Tflush: | 
|  | rflush(job); | 
|  | break; | 
|  | case Tattach: | 
|  | rattach(job, mf); | 
|  | break; | 
|  | case Twalk: | 
|  | rwalk(job, mf); | 
|  | break; | 
|  | case Topen: | 
|  | ropen(job, mf); | 
|  | break; | 
|  | case Tcreate: | 
|  | rcreate(job, mf); | 
|  | break; | 
|  | case Tread: | 
|  | rread(job, mf); | 
|  | break; | 
|  | case Twrite: | 
|  | rwrite(job, mf); | 
|  | break; | 
|  | case Tclunk: | 
|  | rclunk(job, mf); | 
|  | break; | 
|  | case Tremove: | 
|  | rremove(job, mf); | 
|  | break; | 
|  | case Tstat: | 
|  | rstat(job, mf); | 
|  | break; | 
|  | case Twstat: | 
|  | rwstat(job, mf); | 
|  | break; | 
|  | } | 
|  | spinlock_unlock(&dblock); | 
|  |  | 
|  | freejob(job); | 
|  |  | 
|  | if (debug) | 
|  | fprintf(stderr, "CS:Job done\n"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void io(void) | 
|  | { | 
|  | long n; | 
|  | uint8_t mdata[IOHDRSZ + Maxfdata]; | 
|  | struct job *job; | 
|  |  | 
|  | /* | 
|  | * each request is handled via a thread. Somewhat less efficient than the | 
|  | * old | 
|  | * cs but way cleaner. | 
|  | */ | 
|  |  | 
|  | for (;;) { | 
|  | n = read9pmsg(mfd[0], mdata, sizeof(mdata)); | 
|  | if (n <= 0) | 
|  | error(1, 0, "%s: %r", "mount read"); | 
|  | job = newjob(); | 
|  | if (convM2S(mdata, n, &job->request) != n) { | 
|  | fprintf(stderr, | 
|  | "convM2S went south: format error %ux %ux %ux %ux %ux", | 
|  | mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]); | 
|  | error(1, 0, "format error %ux %ux %ux %ux %ux", mdata[0], mdata[1], | 
|  | mdata[2], mdata[3], mdata[4]); | 
|  | freejob(job); | 
|  | continue; | 
|  | } | 
|  | /* stash the thread in the job so we can join them all | 
|  | * later if we want to. | 
|  | */ | 
|  | if (pthread_create(&job->thread, NULL, &job_thread, job)) { | 
|  | error(1, 0, "%s: %r", "Failed to create job"); | 
|  | continue; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void rversion(struct job *job) | 
|  | { | 
|  | if (job->request.msize > IOHDRSZ + Maxfdata) | 
|  | job->reply.msize = IOHDRSZ + Maxfdata; | 
|  | else | 
|  | job->reply.msize = job->request.msize; | 
|  | if (strncmp(job->request.version, "9P2000", 6) != 0) | 
|  | sendmsg(job, "unknown 9P version"); | 
|  | else { | 
|  | job->reply.version = "9P2000"; | 
|  | sendmsg(job, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void rauth(struct job *job) | 
|  | { | 
|  | sendmsg(job, "cs: authentication not required"); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  don't flush till all the threads  are done | 
|  | */ | 
|  | static void rflush(struct job *job) | 
|  | { | 
|  | flushjob(job->request.oldtag); | 
|  | sendmsg(job, 0); | 
|  | } | 
|  |  | 
|  | static void rattach(struct job *job, struct mfile *mf) | 
|  | { | 
|  | if (mf->busy == 0) { | 
|  | mf->busy = 1; | 
|  | mf->user = estrdup(job->request.uname); | 
|  | } | 
|  | mf->qid.vers = vers++; | 
|  | mf->qid.type = QTDIR; | 
|  | mf->qid.path = 0LL; | 
|  | job->reply.qid = mf->qid; | 
|  | sendmsg(job, 0); | 
|  | } | 
|  |  | 
|  | static char *rwalk(struct job *job, struct mfile *mf) | 
|  | { | 
|  | char *err; | 
|  | char **elems; | 
|  | int nelems; | 
|  | int i; | 
|  | struct mfile *nmf; | 
|  | struct qid qid; | 
|  |  | 
|  | err = 0; | 
|  | nmf = NULL; | 
|  | elems = job->request.wname; | 
|  | nelems = job->request.nwname; | 
|  | job->reply.nwqid = 0; | 
|  |  | 
|  | if (job->request.newfid != job->request.fid) { | 
|  | /* clone fid */ | 
|  | nmf = newfid(job->request.newfid); | 
|  | if (nmf->busy) { | 
|  | nmf = NULL; | 
|  | err = "clone to used channel"; | 
|  | goto send; | 
|  | } | 
|  | *nmf = *mf; | 
|  | nmf->user = estrdup(mf->user); | 
|  | nmf->fid = job->request.newfid; | 
|  | nmf->qid.vers = vers++; | 
|  | mf = nmf; | 
|  | } | 
|  | /* else nmf will be nil */ | 
|  |  | 
|  | qid = mf->qid; | 
|  | if (nelems > 0) { | 
|  | /* walk fid */ | 
|  | for (i = 0; i < nelems && i < MAXWELEM; i++) { | 
|  | if ((qid.type & QTDIR) == 0) { | 
|  | err = "not a directory"; | 
|  | break; | 
|  | } | 
|  | if (strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0) { | 
|  | qid.type = QTDIR; | 
|  | qid.path = Qdir; | 
|  | Found: | 
|  | job->reply.wqid[i] = qid; | 
|  | job->reply.nwqid++; | 
|  | continue; | 
|  | } | 
|  | if (strcmp(elems[i], "cs") == 0) { | 
|  | qid.type = QTFILE; | 
|  | qid.path = Qcs; | 
|  | goto Found; | 
|  | } | 
|  | err = "file does not exist"; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | send: | 
|  | if (nmf != NULL && (err != NULL || job->reply.nwqid < nelems)) { | 
|  | cleanmf(nmf); | 
|  | free(nmf->user); | 
|  | nmf->user = 0; | 
|  | nmf->busy = 0; | 
|  | nmf->fid = 0; | 
|  | } | 
|  | if (err == NULL) | 
|  | mf->qid = qid; | 
|  | sendmsg(job, err); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static void ropen(struct job *job, struct mfile *mf) | 
|  | { | 
|  | int mode; | 
|  | char *err; | 
|  |  | 
|  | err = 0; | 
|  | mode = job->request.mode; | 
|  | if (mf->qid.type & QTDIR) { | 
|  | if (mode) | 
|  | err = "permission denied"; | 
|  | } | 
|  | job->reply.qid = mf->qid; | 
|  | job->reply.iounit = 0; | 
|  | sendmsg(job, err); | 
|  | } | 
|  |  | 
|  | static void rcreate(struct job *job, struct mfile *mf) | 
|  | { | 
|  | sendmsg(job, "creation permission denied"); | 
|  | } | 
|  |  | 
|  | static void rread(struct job *job, struct mfile *mf) | 
|  | { | 
|  | int i, n, cnt; | 
|  | long off, toff, clock; | 
|  | struct dir dir; | 
|  | uint8_t buf[Maxfdata]; | 
|  | char *err; | 
|  |  | 
|  | n = 0; | 
|  | err = 0; | 
|  | off = job->request.offset; | 
|  | cnt = job->request.count; | 
|  | if (mf->qid.type & QTDIR) { | 
|  | //	clock = time(0); | 
|  | if (off == 0) { | 
|  | memset(&dir, 0, sizeof(dir)); | 
|  | dir.name = "cs"; | 
|  | dir.qid.type = QTFILE; | 
|  | dir.qid.vers = vers; | 
|  | dir.qid.path = Qcs; | 
|  | dir.mode = 0666; | 
|  | dir.length = 0; | 
|  | dir.uid = mf->user; | 
|  | dir.gid = mf->user; | 
|  | dir.muid = mf->user; | 
|  | dir.atime = clock; /* wrong */ | 
|  | dir.mtime = clock; /* wrong */ | 
|  | n = convD2M(&dir, buf, sizeof(buf)); | 
|  | } | 
|  | job->reply.data = (char *)buf; | 
|  | } else { | 
|  | for (;;) { | 
|  | /* look for an answer at the right offset */ | 
|  | toff = 0; | 
|  | for (i = 0; mf->reply[i] && i < mf->nreply; i++) { | 
|  | n = mf->replylen[i]; | 
|  | if (off < toff + n) | 
|  | break; | 
|  | toff += n; | 
|  | } | 
|  | if (i < mf->nreply) | 
|  | break; /* got something to return */ | 
|  |  | 
|  | /* try looking up more answers */ | 
|  | if (lookup(mf) == 0) { | 
|  | /* no more */ | 
|  | n = 0; | 
|  | goto send; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* give back a single reply (or part of one) */ | 
|  | job->reply.data = mf->reply[i] + (off - toff); | 
|  | if (cnt > toff - off + n) | 
|  | n = toff - off + n; | 
|  | else | 
|  | n = cnt; | 
|  | } | 
|  | send: | 
|  | job->reply.count = n; | 
|  | sendmsg(job, err); | 
|  | } | 
|  |  | 
|  | static void cleanmf(struct mfile *mf) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if (mf->net != NULL) { | 
|  | free(mf->net); | 
|  | mf->net = NULL; | 
|  | } | 
|  | if (mf->host != NULL) { | 
|  | free(mf->host); | 
|  | mf->host = NULL; | 
|  | } | 
|  | if (mf->serv != NULL) { | 
|  | free(mf->serv); | 
|  | mf->serv = NULL; | 
|  | } | 
|  | if (mf->rem != NULL) { | 
|  | free(mf->rem); | 
|  | mf->rem = NULL; | 
|  | } | 
|  | for (i = 0; i < mf->nreply; i++) { | 
|  | free(mf->reply[i]); | 
|  | mf->reply[i] = NULL; | 
|  | mf->replylen[i] = 0; | 
|  | } | 
|  | mf->nreply = 0; | 
|  | mf->nextnet = netlist; | 
|  | } | 
|  |  | 
|  | static void rwrite(struct job *job, struct mfile *mf) | 
|  | { | 
|  | int cnt, n; | 
|  | char *err; | 
|  | char *field[4]; | 
|  | char curerr[64]; | 
|  |  | 
|  | err = 0; | 
|  | cnt = job->request.count; | 
|  | if (mf->qid.type & QTDIR) { | 
|  | err = "can't write directory"; | 
|  | goto send; | 
|  | } | 
|  | if (cnt >= Maxrequest) { | 
|  | err = "request too long"; | 
|  | goto send; | 
|  | } | 
|  | job->request.data[cnt] = 0; | 
|  | /* | 
|  | *  toggle debugging | 
|  | */ | 
|  | if (strncmp(job->request.data, "debug", 5) == 0) { | 
|  | debug ^= 1; | 
|  | fprintf(stderr, "CS:debug %d", debug); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  toggle ipv6 lookups | 
|  | */ | 
|  | if (strncmp(job->request.data, "ipv6", 4) == 0) { | 
|  | ipv6lookups ^= 1; | 
|  | fprintf(stderr, "CS:ipv6lookups %d", ipv6lookups); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  toggle debugging | 
|  | */ | 
|  | if (strncmp(job->request.data, "paranoia", 8) == 0) { | 
|  | paranoia ^= 1; | 
|  | fprintf(stderr, "CS:paranoia %d", paranoia); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  add networks to the default list | 
|  | */ | 
|  | if (strncmp(job->request.data, "add ", 4) == 0) { | 
|  | if (job->request.data[cnt - 1] == '\n') | 
|  | job->request.data[cnt - 1] = 0; | 
|  | netadd(job->request.data + 4); | 
|  | readipinterfaces(); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  refresh all state | 
|  | */ | 
|  | if (strncmp(job->request.data, "refresh", 7) == 0) { | 
|  | netinit(0 /*1*/); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | /* start transaction with a clean slate */ | 
|  | cleanmf(mf); | 
|  |  | 
|  | /* | 
|  | *  look for a general query | 
|  | */ | 
|  | if (*job->request.data == '!') { | 
|  | err = genquery(mf, job->request.data + 1); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | if (debug) | 
|  | fprintf(stderr, "CS:write %s", job->request.data); | 
|  | if (paranoia) | 
|  | fprintf(stderr, "CS:write %s by %s", job->request.data, mf->user); | 
|  |  | 
|  | /* | 
|  | *  break up name | 
|  | */ | 
|  | n = getfields(job->request.data, field, 4, 1, "!"); | 
|  | switch (n) { | 
|  | case 1: | 
|  | mf->net = strdup("net"); | 
|  | mf->host = strdup(field[0]); | 
|  | break; | 
|  | case 4: | 
|  | mf->rem = strdup(field[3]); | 
|  | /* fall through */ | 
|  | case 3: | 
|  | mf->serv = strdup(field[2]); | 
|  | /* fall through */ | 
|  | case 2: | 
|  | mf->host = strdup(field[1]); | 
|  | mf->net = strdup(field[0]); | 
|  | break; | 
|  | } | 
|  | /* | 
|  | *  do the first net worth of lookup | 
|  | */ | 
|  | if (lookup(mf) == 0) { | 
|  | snprintf(curerr, sizeof(curerr), "%r"); | 
|  | err = curerr; | 
|  | } | 
|  | send: | 
|  | job->reply.count = cnt; | 
|  | sendmsg(job, err); | 
|  | } | 
|  |  | 
|  | static void rclunk(struct job *job, struct mfile *mf) | 
|  | { | 
|  | cleanmf(mf); | 
|  | free(mf->user); | 
|  | mf->user = 0; | 
|  | mf->busy = 0; | 
|  | mf->fid = 0; | 
|  | sendmsg(job, 0); | 
|  | } | 
|  |  | 
|  | static void rremove(struct job *job, struct mfile *mf) | 
|  | { | 
|  | sendmsg(job, "remove permission denied"); | 
|  | } | 
|  |  | 
|  | static void rstat(struct job *job, struct mfile *mf) | 
|  | { | 
|  | struct dir dir; | 
|  | uint8_t buf[IOHDRSZ + Maxfdata]; | 
|  |  | 
|  | memset(&dir, 0, sizeof(dir)); | 
|  | if (mf->qid.type & QTDIR) { | 
|  | dir.name = "."; | 
|  | dir.mode = DMDIR | 0555; | 
|  | } else { | 
|  | dir.name = "cs"; | 
|  | dir.mode = 0666; | 
|  | } | 
|  | dir.qid = mf->qid; | 
|  | dir.length = 0; | 
|  | dir.uid = mf->user; | 
|  | dir.gid = mf->user; | 
|  | dir.muid = mf->user; | 
|  | // dir.atime = dir.mtime = time(0); | 
|  | job->reply.nstat = convD2M(&dir, buf, sizeof(buf)); | 
|  | job->reply.stat = buf; | 
|  | sendmsg(job, 0); | 
|  | } | 
|  |  | 
|  | static void rwstat(struct job *job, struct mfile *mf) | 
|  | { | 
|  | sendmsg(job, "wstat permission denied"); | 
|  | } | 
|  |  | 
|  | static void sendmsg(struct job *job, char *err) | 
|  | { | 
|  | int n; | 
|  | uint8_t mdata[IOHDRSZ + Maxfdata]; | 
|  | char ename[ERRMAX]; | 
|  |  | 
|  | if (err) { | 
|  | job->reply.type = Rerror; | 
|  | snprintf(ename, sizeof(ename), "cs: %s", err); | 
|  | job->reply.ename = ename; | 
|  | } else { | 
|  | job->reply.type = job->request.type + 1; | 
|  | } | 
|  | job->reply.tag = job->request.tag; | 
|  | n = convS2M(&job->reply, mdata, sizeof(mdata)); | 
|  | if (n == 1) { | 
|  | fprintf(stderr, "CS:sendmsg convS2M of %F returns 0", &job->reply); | 
|  | abort(); | 
|  | } | 
|  | spinlock_lock(&joblock); | 
|  | if (job->flushed == 0) | 
|  | if (write(mfd[1], mdata, n) != n) | 
|  | error(1, 0, "%s: %r", "mount write"); | 
|  | spinlock_unlock(&joblock); | 
|  | if (debug) | 
|  | fprintf(stderr, "CS:%F %d", &job->reply, n); | 
|  | } | 
|  |  | 
|  | static int isvalidip(uint8_t *ip) | 
|  | { | 
|  | return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0; | 
|  | } | 
|  |  | 
|  | static uint8_t loopbacknet[IPaddrlen] = {0, 0, 0,    0,    0,   0, 0, 0, | 
|  | 0, 0, 0xff, 0xff, 127, 0, 0, 0}; | 
|  | static uint8_t loopbackmask[IPaddrlen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0,    0,    0}; | 
|  |  | 
|  | static void readipinterfaces(void) | 
|  | { | 
|  | if (myipaddr(ipa, mntpt) != 0) | 
|  | ipmove(ipa, IPnoaddr); | 
|  | snprintf(ipaddr, sizeof(ipaddr), "%I", ipa); | 
|  | if (debug) | 
|  | fprintf(stderr, "CS:dns", "ipaddr is %s\n", ipaddr); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  get the system name | 
|  | */ | 
|  | static void ipid(void) | 
|  | { | 
|  | uint8_t addr[6]; | 
|  | struct ndbtuple *t, *tt; | 
|  | char *p, *attr; | 
|  | struct ndbs s; | 
|  | int f; | 
|  | char buf[Maxpath]; | 
|  |  | 
|  | /* use environment, ether addr, or ipaddr to get system name */ | 
|  | if (mysysname == 0) { | 
|  | /* | 
|  | *  environment has priority. | 
|  | * | 
|  | *  on the sgi power the default system name | 
|  | *  is the ip address.  ignore that. | 
|  | * | 
|  | */ | 
|  | p = getenv("sysname"); | 
|  | if (p && *p) { | 
|  | attr = ipattr(p); | 
|  | if (strcmp(attr, "ip") != 0) | 
|  | mysysname = strdup(p); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  the /net/ndb contains what the network | 
|  | *  figured out from DHCP.  use that name if | 
|  | *  there is one. | 
|  | */ | 
|  | if (mysysname == 0 && netdb != NULL) { | 
|  | ndbreopen(netdb); | 
|  | for (tt = t = ndbparse(netdb); t != NULL; t = t->entry) { | 
|  | if (strcmp(t->attr, "sys") == 0) { | 
|  | mysysname = strdup(t->val); | 
|  | break; | 
|  | } | 
|  | } | 
|  | ndbfree(tt); | 
|  | } | 
|  |  | 
|  | /* next network database, ip address, and ether address to find a name | 
|  | */ | 
|  | if (mysysname == 0) { | 
|  | t = NULL; | 
|  | if (isvalidip(ipa)) | 
|  | free(ndbgetvalue(db, &s, "ip", ipaddr, "sys", &t)); | 
|  | if (t == NULL) { | 
|  | for (f = 0; f < 3; f++) { | 
|  | snprintf(buf, sizeof(buf), "%s/ether%d", mntpt, f); | 
|  | if (myetheraddr(addr, buf) < 0) | 
|  | continue; | 
|  | snprintf(eaddr, sizeof(eaddr), "%E", addr); | 
|  | free(ndbgetvalue(db, &s, "ether", eaddr, "sys", &t)); | 
|  | if (t != NULL) | 
|  | break; | 
|  | } | 
|  | } | 
|  | for (tt = t; tt != NULL; tt = tt->entry) { | 
|  | if (strcmp(tt->attr, "sys") == 0) { | 
|  | mysysname = strdup(tt->val); | 
|  | break; | 
|  | } | 
|  | } | 
|  | ndbfree(t); | 
|  | } | 
|  |  | 
|  | /* nothing else worked, use the ip address */ | 
|  | if (mysysname == 0 && isvalidip(ipa)) | 
|  | mysysname = strdup(ipaddr); | 
|  |  | 
|  | /* set /dev/sysname if we now know it */ | 
|  | if (mysysname) { | 
|  | f = open("/dev/sysname", O_RDWR); | 
|  | if (f >= 0) { | 
|  | write(f, mysysname, strlen(mysysname)); | 
|  | close(f); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  Set up a list of default networks by looking for | 
|  | *  /net/^*^/clone. | 
|  | *  For now, never background. | 
|  | */ | 
|  | static void netinit(int background) | 
|  | { | 
|  | char clone[Maxpath]; | 
|  | struct network *np; | 
|  | static int working; | 
|  |  | 
|  | /* add the mounted networks to the default list */ | 
|  | for (np = network; np->net; np++) { | 
|  | int fuckup; | 
|  |  | 
|  | if (np->considered) | 
|  | continue; | 
|  | snprintf(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net); | 
|  | fuckup = open(clone, O_RDONLY); | 
|  | if (fuckup < 0) | 
|  | continue; | 
|  | close(fuckup); | 
|  | // if(access(clone, R_OK)) | 
|  | // continue; | 
|  | if (netlist) | 
|  | last->next = np; | 
|  | else | 
|  | netlist = np; | 
|  | last = np; | 
|  | np->next = 0; | 
|  | np->considered = 1; | 
|  | } | 
|  |  | 
|  | /* find out what our ip address is */ | 
|  | readipinterfaces(); | 
|  |  | 
|  | /* set the system name if we need to, these days ip is all we have */ | 
|  | ipid(); | 
|  |  | 
|  | if (debug) | 
|  | fprintf(stderr, logfile, "CS:mysysname %s eaddr %s ipaddr %s ipa %I\n", | 
|  | mysysname ? mysysname : "???", eaddr, ipaddr, ipa); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  add networks to the standard list | 
|  | */ | 
|  | static void netadd(char *p) | 
|  | { | 
|  | struct network *np; | 
|  | char *field[12]; | 
|  | int i, n; | 
|  |  | 
|  | n = getfields(p, field, 12, 1, " "); | 
|  | for (i = 0; i < n; i++) { | 
|  | for (np = network; np->net; np++) { | 
|  | if (strcmp(field[i], np->net) != 0) | 
|  | continue; | 
|  | if (np->considered) | 
|  | break; | 
|  | if (netlist) | 
|  | last->next = np; | 
|  | else | 
|  | netlist = np; | 
|  | last = np; | 
|  | np->next = 0; | 
|  | np->considered = 1; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static int lookforproto(struct ndbtuple *t, char *proto) | 
|  | { | 
|  | for (; t != NULL; t = t->entry) | 
|  | if (strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0) | 
|  | return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  lookup a request.  the network "net" means we should pick the | 
|  | *  best network to get there. | 
|  | */ | 
|  | static int lookup(struct mfile *mf) | 
|  | { | 
|  | struct network *np; | 
|  | char *cp; | 
|  | struct ndbtuple *nt, *t; | 
|  | char reply[Maxreply]; | 
|  | int i, rv; | 
|  | int hack; | 
|  |  | 
|  | /* open up the standard db files */ | 
|  | if (db == 0) | 
|  | ndbinit(); | 
|  | if (db == 0) | 
|  | error(1, 0, "%s: %r", "can't open mf->network database\n"); | 
|  |  | 
|  | rv = 0; | 
|  |  | 
|  | if (mf->net == NULL) | 
|  | return 0; /* must have been a genquery */ | 
|  |  | 
|  | if (strcmp(mf->net, "net") == 0) { | 
|  | /* | 
|  | *  go through set of default nets | 
|  | */ | 
|  | for (np = mf->nextnet; np; np = np->next) { | 
|  | nt = (*np->lookup)(np, mf->host, mf->serv, 1); | 
|  | if (nt == NULL) | 
|  | continue; | 
|  | hack = np->fasttimeouthack && !lookforproto(nt, np->net); | 
|  | for (t = nt; mf->nreply < Nreply && t; t = t->entry) { | 
|  | cp = (*np->trans)(t, np, mf->serv, mf->rem, hack); | 
|  | if (!cp) | 
|  | continue; | 
|  | /* avoid duplicates */ | 
|  | for (i = 0; i < mf->nreply; i++) | 
|  | if (strcmp(mf->reply[i], cp) == 0) | 
|  | break; | 
|  | if (i == mf->nreply) { | 
|  | /* save the reply */ | 
|  | mf->replylen[mf->nreply] = strlen(cp); | 
|  | mf->reply[mf->nreply++] = cp; | 
|  | rv++; | 
|  | } | 
|  | } | 
|  | ndbfree(nt); | 
|  | np = np->next; | 
|  | break; | 
|  | } | 
|  | mf->nextnet = np; | 
|  | return rv; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  if not /net, we only get one lookup | 
|  | */ | 
|  | if (mf->nreply != 0) | 
|  | return 0; | 
|  | /* | 
|  | *  look for a specific network | 
|  | */ | 
|  | for (np = netlist; np && np->net != NULL; np++) { | 
|  | if (np->fasttimeouthack) | 
|  | continue; | 
|  | if (strcmp(np->net, mf->net) == 0) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (np && np->net != NULL) { | 
|  | /* | 
|  | *  known network | 
|  | */ | 
|  | nt = (*np->lookup)(np, mf->host, mf->serv, 1); | 
|  | for (t = nt; mf->nreply < Nreply && t; t = t->entry) { | 
|  | cp = (*np->trans)(t, np, mf->serv, mf->rem, 0); | 
|  | if (cp) { | 
|  | mf->replylen[mf->nreply] = strlen(cp); | 
|  | mf->reply[mf->nreply++] = cp; | 
|  | rv++; | 
|  | } | 
|  | } | 
|  | ndbfree(nt); | 
|  | return rv; | 
|  | } | 
|  | /* | 
|  | *  not a known network, don't translate host or service | 
|  | */ | 
|  | if (mf->serv) | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s", mntpt, mf->net, | 
|  | mf->host, mf->serv); | 
|  | else | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s", mntpt, mf->net, | 
|  | mf->host); | 
|  | mf->reply[0] = strdup(reply); | 
|  | mf->replylen[0] = strlen(reply); | 
|  | mf->nreply = 1; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  translate an ip service name into a port number.  If it's a numeric port | 
|  | *  number, look for restricted access. | 
|  | * | 
|  | *  the service '*' needs no translation. | 
|  | */ | 
|  | static char *ipserv(struct network *np, char *name, char *buf, int blen) | 
|  | { | 
|  | char *p; | 
|  | int alpha = 0; | 
|  | int restr = 0; | 
|  | char port[10]; | 
|  | struct ndbtuple *t, *nt; | 
|  | struct ndbs s; | 
|  |  | 
|  | /* '*' means any service */ | 
|  | if (strcmp(name, "*") == 0) { | 
|  | strlcpy(buf, name, blen); | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | /*  see if it's numeric or symbolic */ | 
|  | port[0] = 0; | 
|  | for (p = name; *p; p++) { | 
|  | if (!isdigit(*p)) { | 
|  | if (isalpha(*p) || *p == '-' || *p == '$') | 
|  | alpha = 1; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | t = NULL; | 
|  | p = NULL; | 
|  | if (alpha) { | 
|  | p = ndbgetvalue(db, &s, np->net, name, "port", &t); | 
|  | if (p == NULL) | 
|  | return 0; | 
|  | } else { | 
|  | /* look up only for tcp ports < 1024 to get the restricted | 
|  | * attribute | 
|  | */ | 
|  | if (atoi(name) < 1024 && strcmp(np->net, "tcp") == 0) | 
|  | p = ndbgetvalue(db, &s, "port", name, "port", &t); | 
|  | if (p == NULL) | 
|  | p = strdup(name); | 
|  | } | 
|  |  | 
|  | if (t) { | 
|  | for (nt = t; nt; nt = nt->entry) | 
|  | if (strcmp(nt->attr, "restricted") == 0) | 
|  | restr = 1; | 
|  | ndbfree(t); | 
|  | } | 
|  | snprintf(buf, blen, "%s%s", p, restr ? "!r" : ""); | 
|  | free(p); | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  lookup an ip attribute | 
|  | */ | 
|  | static int ipattrlookup(struct ndb *db, char *ipa, char *attr, char *val, | 
|  | int vlen) | 
|  | { | 
|  |  | 
|  | struct ndbtuple *t, *nt; | 
|  | char *alist[2]; | 
|  |  | 
|  | alist[0] = attr; | 
|  | t = ndbipinfo(db, "ip", ipa, alist, 1); | 
|  | if (t == NULL) | 
|  | return 0; | 
|  | for (nt = t; nt != NULL; nt = nt->entry) { | 
|  | if (strcmp(nt->attr, attr) == 0) { | 
|  | strlcpy(val, nt->val, vlen); | 
|  | ndbfree(t); | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* we shouldn't get here */ | 
|  | ndbfree(t); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  lookup (and translate) an ip destination | 
|  | */ | 
|  | static struct ndbtuple *iplookup(struct network *np, char *host, char *serv, | 
|  | int nolookup) | 
|  | { | 
|  | char *attr, *dnsname; | 
|  | struct ndbtuple *t, *nt; | 
|  | struct ndbs s; | 
|  | char ts[Maxservice]; | 
|  | char dollar[Maxhost]; | 
|  | uint8_t ip[IPaddrlen]; | 
|  | uint8_t net[IPaddrlen]; | 
|  | uint8_t tnet[IPaddrlen]; | 
|  | struct ipifc *ifc; | 
|  | struct iplifc *lifc; | 
|  |  | 
|  | /* | 
|  | *  start with the service since it's the most likely to fail | 
|  | *  and costs the least | 
|  | */ | 
|  | werrstr("can't translate address"); | 
|  | if (serv == 0 || ipserv(np, serv, ts, sizeof(ts)) == 0) { | 
|  | werrstr("can't translate service"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* for dial strings with no host */ | 
|  | if (strcmp(host, "*") == 0) | 
|  | return ndbnew("ip", "*"); | 
|  |  | 
|  | /* | 
|  | *  hack till we go v6 :: = 0.0.0.0 | 
|  | */ | 
|  | if (strcmp("::", host) == 0) | 
|  | return ndbnew("ip", "*"); | 
|  |  | 
|  | /* | 
|  | *  '$' means the rest of the name is an attribute that we | 
|  | *  need to search for | 
|  | */ | 
|  | if (*host == '$') { | 
|  | if (ipattrlookup(db, ipaddr, host + 1, dollar, sizeof(dollar))) | 
|  | host = dollar; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  turn '[ip address]' into just 'ip address' | 
|  | */ | 
|  | if (*host == '[' && host[strlen(host) - 1] == ']') { | 
|  | host++; | 
|  | host[strlen(host) - 1] = 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  just accept addresses | 
|  | */ | 
|  | attr = ipattr(host); | 
|  | if (strcmp(attr, "ip") == 0) | 
|  | return ndbnew("ip", host); | 
|  |  | 
|  | /* | 
|  | *  give the domain name server the first opportunity to | 
|  | *  resolve domain names.  if that fails try the database. | 
|  | */ | 
|  | t = 0; | 
|  | werrstr("can't translate address"); | 
|  | if (strcmp(attr, "dom") == 0) | 
|  | t = dnsiplookup(host, &s); | 
|  | if (t == 0) | 
|  | free(ndbgetvalue(db, &s, attr, host, "ip", &t)); | 
|  | if (t == 0) { | 
|  | dnsname = ndbgetvalue(db, &s, attr, host, "dom", NULL); | 
|  | if (dnsname) { | 
|  | t = dnsiplookup(dnsname, &s); | 
|  | free(dnsname); | 
|  | } | 
|  | } | 
|  | if (t == 0) | 
|  | t = dnsiplookup(host, &s); | 
|  | if (t == 0) | 
|  | return 0; | 
|  |  | 
|  | /* | 
|  | *  reorder the tuple to have the matched line first and | 
|  | *  save that in the request structure. | 
|  | */ | 
|  | t = reorder(t, s.t); | 
|  |  | 
|  | /* | 
|  | * reorder according to our interfaces | 
|  | */ | 
|  | spinlock_lock(&ipifclock); | 
|  | for (ifc = ipifcs; ifc != NULL; ifc = ifc->next) { | 
|  | for (lifc = ifc->lifc; lifc != NULL; lifc = lifc->next) { | 
|  | maskip(lifc->ip, lifc->mask, net); | 
|  | for (nt = t; nt; nt = nt->entry) { | 
|  | if (strcmp(nt->attr, "ip") != 0) | 
|  | continue; | 
|  | parseip(ip, nt->val); | 
|  | maskip(ip, lifc->mask, tnet); | 
|  | if (memcmp(net, tnet, IPaddrlen) == 0) { | 
|  | t = reorder(t, nt); | 
|  | spinlock_unlock(&ipifclock); | 
|  | return t; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | spinlock_unlock(&ipifclock); | 
|  |  | 
|  | return t; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  translate an ip address | 
|  | */ | 
|  | static char *iptrans(struct ndbtuple *t, struct network *np, char *serv, | 
|  | char *rem, int hack) | 
|  | { | 
|  | char ts[Maxservice]; | 
|  | char reply[Maxreply]; | 
|  | char x[Maxservice]; | 
|  |  | 
|  | if (strcmp(t->attr, "ip") != 0) | 
|  | return 0; | 
|  |  | 
|  | if (serv == 0 || ipserv(np, serv, ts, sizeof(ts)) == 0) { | 
|  | werrstr("can't translate service"); | 
|  | return 0; | 
|  | } | 
|  | if (rem != NULL) | 
|  | snprintf(x, sizeof(x), "!%s", rem); | 
|  | else | 
|  | *x = 0; | 
|  |  | 
|  | if (*t->val == '*') | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net, ts, | 
|  | x); | 
|  | else | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s", mntpt, np->net, | 
|  | t->val, ts, x, hack ? "!fasttimeout" : ""); | 
|  |  | 
|  | return strdup(reply); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  lookup a telephone number | 
|  | */ | 
|  | static struct ndbtuple *telcolookup(struct network *np, char *host, char *serv, | 
|  | int nolookup) | 
|  | { | 
|  | struct ndbtuple *t; | 
|  | struct ndbs s; | 
|  |  | 
|  | werrstr("can't translate address"); | 
|  | free(ndbgetvalue(db, &s, "sys", host, "telco", &t)); | 
|  | if (t == 0) | 
|  | return ndbnew("telco", host); | 
|  |  | 
|  | return reorder(t, s.t); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  translate a telephone address | 
|  | */ | 
|  | static char *telcotrans(struct ndbtuple *t, struct network *np, char *serv, | 
|  | char *rem, int unused) | 
|  | { | 
|  | char reply[Maxreply]; | 
|  | char x[Maxservice]; | 
|  |  | 
|  | if (strcmp(t->attr, "telco") != 0) | 
|  | return 0; | 
|  |  | 
|  | if (rem != NULL) | 
|  | snprintf(x, sizeof(x), "!%s", rem); | 
|  | else | 
|  | *x = 0; | 
|  | if (serv) | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net, | 
|  | t->val, serv, x); | 
|  | else | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net, | 
|  | t->val, x); | 
|  | return strdup(reply); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  reorder the tuple to put x's line first in the entry | 
|  | */ | 
|  | static struct ndbtuple *reorder(struct ndbtuple *t, struct ndbtuple *x) | 
|  | { | 
|  | struct ndbtuple *nt; | 
|  | struct ndbtuple *line; | 
|  |  | 
|  | /* find start of this entry's line */ | 
|  | for (line = x; line->entry == line->line; line = line->line) | 
|  | ; | 
|  | line = line->line; | 
|  | if (line == t) | 
|  | return t; /* already the first line */ | 
|  |  | 
|  | /* remove this line and everything after it from the entry */ | 
|  | for (nt = t; nt->entry != line; nt = nt->entry) | 
|  | ; | 
|  | nt->entry = 0; | 
|  |  | 
|  | /* make that the start of the entry */ | 
|  | for (nt = line; nt->entry; nt = nt->entry) | 
|  | ; | 
|  | nt->entry = t; | 
|  | return line; | 
|  | } | 
|  |  | 
|  | static struct ndbtuple *dnsip6lookup(char *mntpt, char *buf, struct ndbtuple *t) | 
|  | { | 
|  | struct ndbtuple *t6, *tt; | 
|  |  | 
|  | t6 = dnsquery(mntpt, buf, "ipv6"); /* lookup AAAA dns RRs */ | 
|  | if (t6 == NULL) | 
|  | return t; | 
|  |  | 
|  | /* convert ipv6 attr to ip */ | 
|  | for (tt = t6; tt != NULL; tt = tt->entry) | 
|  | if (strcmp(tt->attr, "ipv6") == 0) | 
|  | strlcpy(tt->attr, "ip", sizeof(tt->attr)); | 
|  |  | 
|  | if (t == NULL) | 
|  | return t6; | 
|  |  | 
|  | /* append t6 list to t list */ | 
|  | for (tt = t; tt->entry != NULL; tt = tt->entry) | 
|  | ; | 
|  | tt->entry = t6; | 
|  | return t; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  call the dns process and have it try to translate a name | 
|  | */ | 
|  | static struct ndbtuple *dnsiplookup(char *host, struct ndbs *s) | 
|  | { | 
|  | char buf[Maxreply]; | 
|  | struct ndbtuple *t; | 
|  |  | 
|  | spinlock_unlock(&dblock); | 
|  |  | 
|  | /* save the name */ | 
|  | snprintf(buf, sizeof(buf), "%s", host); | 
|  |  | 
|  | if (strcmp(ipattr(buf), "ip") == 0) | 
|  | t = dnsquery(mntpt, buf, "ptr"); | 
|  | else { | 
|  | t = dnsquery(mntpt, buf, "ip"); | 
|  | /* special case: query ipv6 (AAAA dns RR) too */ | 
|  | if (ipv6lookups) | 
|  | t = dnsip6lookup(mntpt, buf, t); | 
|  | } | 
|  | s->t = t; | 
|  |  | 
|  | if (t == NULL) { | 
|  | snprintf(buf, sizeof(buf), "%r"); | 
|  | if (strstr(buf, "exist")) | 
|  | werrstr("can't translate address: %s", buf); | 
|  | else if (strstr(buf, "dns failure")) | 
|  | werrstr("temporary problem: %s", buf); | 
|  | } | 
|  |  | 
|  | spinlock_lock(&dblock); | 
|  | return t; | 
|  | } | 
|  |  | 
|  | static int qmatch(struct ndbtuple *t, char **attr, char **val, int n) | 
|  | { | 
|  | int i, found; | 
|  | struct ndbtuple *nt; | 
|  |  | 
|  | for (i = 1; i < n; i++) { | 
|  | found = 0; | 
|  | for (nt = t; nt; nt = nt->entry) | 
|  | if (strcmp(attr[i], nt->attr) == 0) | 
|  | if (strcmp(val[i], "*") == 0 || strcmp(val[i], nt->val) == 0) { | 
|  | found = 1; | 
|  | break; | 
|  | } | 
|  | if (found == 0) | 
|  | break; | 
|  | } | 
|  | return i == n; | 
|  | } | 
|  |  | 
|  | /* this is awful but I don't want to bring in libstring just for this. | 
|  | * you want real strings don't use C | 
|  | */ | 
|  | static void qreply(struct mfile *mf, struct ndbtuple *t) | 
|  | { | 
|  | struct ndbtuple *nt; | 
|  | char *s, *cur; | 
|  | int len, amt; | 
|  |  | 
|  | s = malloc(4096); | 
|  | cur = s; | 
|  | len = 4096; | 
|  |  | 
|  | for (nt = t; mf->nreply < Nreply && nt; nt = nt->entry) { | 
|  | amt = snprintf(cur, len, "%s=%s", nt->attr, nt->val); | 
|  |  | 
|  | if (amt < 0) | 
|  | len = 0; | 
|  | else { | 
|  | len -= amt; | 
|  | cur += amt; | 
|  | } | 
|  |  | 
|  | if (nt->line != nt->entry) { | 
|  | mf->replylen[mf->nreply] = strlen(s); | 
|  | mf->reply[mf->nreply++] = strdup(s); | 
|  | cur = s; | 
|  | len = 4096; | 
|  | } else { | 
|  | amt = snprintf(cur, len, " "); | 
|  | if (amt < 0) | 
|  | len = 0; | 
|  | else { | 
|  | len -= amt; | 
|  | cur += amt; | 
|  | } | 
|  | } | 
|  | } | 
|  | free(s); | 
|  | } | 
|  |  | 
|  | enum { | 
|  | Maxattr = 32, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | *  generic query lookup.  The query is of one of the following | 
|  | *  forms: | 
|  | * | 
|  | *  attr1=val1 attr2=val2 attr3=val3 ... | 
|  | * | 
|  | *  returns the matching tuple | 
|  | * | 
|  | *  ipinfo attr=val attr1 attr2 attr3 ... | 
|  | * | 
|  | *  is like ipinfo and returns the attr{1-n} | 
|  | *  associated with the ip address. | 
|  | */ | 
|  | static char *genquery(struct mfile *mf, char *query) | 
|  | { | 
|  | int i, n; | 
|  | char *p; | 
|  | char *attr[Maxattr]; | 
|  | char *val[Maxattr]; | 
|  | struct ndbtuple *t; | 
|  | struct ndbs s; | 
|  |  | 
|  | n = getfields(query, attr, COUNT_OF(attr), 1, " "); | 
|  | if (n == 0) | 
|  | return "bad query"; | 
|  |  | 
|  | if (strcmp(attr[0], "ipinfo") == 0) | 
|  | return ipinfoquery(mf, attr, n); | 
|  |  | 
|  | /* parse pairs */ | 
|  | for (i = 0; i < n; i++) { | 
|  | p = strchr(attr[i], '='); | 
|  | if (p == 0) | 
|  | return "bad query"; | 
|  | *p++ = 0; | 
|  | val[i] = p; | 
|  | } | 
|  |  | 
|  | /* give dns a chance */ | 
|  | if ((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]) { | 
|  | t = dnsiplookup(val[0], &s); | 
|  | if (t) { | 
|  | if (qmatch(t, attr, val, n)) { | 
|  | qreply(mf, t); | 
|  | ndbfree(t); | 
|  | return 0; | 
|  | } | 
|  | ndbfree(t); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* first pair is always the key.  It can't be a '*' */ | 
|  | t = ndbsearch(db, &s, attr[0], val[0]); | 
|  |  | 
|  | /* search is the and of all the pairs */ | 
|  | while (t) { | 
|  | if (qmatch(t, attr, val, n)) { | 
|  | qreply(mf, t); | 
|  | ndbfree(t); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | ndbfree(t); | 
|  | t = ndbsnext(&s, attr[0], val[0]); | 
|  | } | 
|  |  | 
|  | return "no match"; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  resolve an ip address | 
|  | */ | 
|  | static struct ndbtuple *ipresolve(char *attr, char *host) | 
|  | { | 
|  | struct ndbtuple *t, *nt, **l; | 
|  |  | 
|  | t = iplookup(&network[0], host, "*", 0); | 
|  | for (l = &t; *l != NULL;) { | 
|  | nt = *l; | 
|  | if (strcmp(nt->attr, "ip") != 0) { | 
|  | *l = nt->entry; | 
|  | nt->entry = NULL; | 
|  | ndbfree(nt); | 
|  | continue; | 
|  | } | 
|  | strlcpy(nt->attr, attr, sizeof(nt->attr)); | 
|  | l = &nt->entry; | 
|  | } | 
|  | return t; | 
|  | } | 
|  |  | 
|  | static char *ipinfoquery(struct mfile *mf, char **list, int n) | 
|  | { | 
|  | int i, nresolve; | 
|  | int resolve[Maxattr]; | 
|  | struct ndbtuple *t, *nt, **l; | 
|  | char *attr, *val; | 
|  |  | 
|  | /* skip 'ipinfo' */ | 
|  | list++; | 
|  | n--; | 
|  |  | 
|  | if (n < 1) | 
|  | return "bad query"; | 
|  |  | 
|  | /* get search attribute=value, or assume ip=myipaddr */ | 
|  | attr = *list; | 
|  | val = strchr(attr, '='); | 
|  | if (val != NULL) { | 
|  | *val++ = 0; | 
|  | list++; | 
|  | n--; | 
|  | } else { | 
|  | attr = "ip"; | 
|  | val = ipaddr; | 
|  | } | 
|  |  | 
|  | if (n < 1) | 
|  | return "bad query"; | 
|  |  | 
|  | /* | 
|  | *  don't let ndbipinfo resolve the addresses, we're | 
|  | *  better at it. | 
|  | */ | 
|  | nresolve = 0; | 
|  | for (i = 0; i < n; i++) | 
|  | if (*list[i] == '@') { /* @attr=val ? */ | 
|  | list[i]++; | 
|  | resolve[i] = 1; /* we'll resolve it */ | 
|  | nresolve++; | 
|  | } else | 
|  | resolve[i] = 0; | 
|  |  | 
|  | t = ndbipinfo(db, attr, val, list, n); | 
|  | if (t == NULL) | 
|  | return "no match"; | 
|  |  | 
|  | if (nresolve != 0) { | 
|  | for (l = &t; *l != NULL;) { | 
|  | nt = *l; | 
|  |  | 
|  | /* already an address? */ | 
|  | if (strcmp(ipattr(nt->val), "ip") == 0) { | 
|  | l = &(*l)->entry; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* user wants it resolved? */ | 
|  | for (i = 0; i < n; i++) | 
|  | if (strcmp(list[i], nt->attr) == 0) | 
|  | break; | 
|  | if (i >= n || resolve[i] == 0) { | 
|  | l = &(*l)->entry; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* resolve address and replace entry */ | 
|  | *l = ipresolve(nt->attr, nt->val); | 
|  | while (*l != NULL) | 
|  | l = &(*l)->entry; | 
|  | *l = nt->entry; | 
|  |  | 
|  | nt->entry = NULL; | 
|  | ndbfree(nt); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* make it all one line */ | 
|  | for (nt = t; nt != NULL; nt = nt->entry) { | 
|  | if (nt->entry == NULL) | 
|  | nt->line = t; | 
|  | else | 
|  | nt->line = nt->entry; | 
|  | } | 
|  |  | 
|  | qreply(mf, t); | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static void *emalloc(int size) | 
|  | { | 
|  | void *x; | 
|  |  | 
|  | x = calloc(size, 1); | 
|  | if (x == NULL) | 
|  | abort(); | 
|  | memset(x, 0, size); | 
|  | return x; | 
|  | } | 
|  |  | 
|  | static char *estrdup(char *s) | 
|  | { | 
|  | int size; | 
|  | char *p; | 
|  |  | 
|  | size = strlen(s) + 1; | 
|  | p = calloc(size, 1); | 
|  | if (p == NULL) | 
|  | abort(); | 
|  | memmove(p, s, size); | 
|  | return p; | 
|  | } |