18 Maxhost= 64, /* maximum host name size */
19 Maxservice= 64, /* maximum service name size */
20 Maxactive= 200, /* maximum number of active slave procs */
26 typedef struct Mfile Mfile;
27 typedef struct Mlist Mlist;
28 typedef struct Network Network;
29 typedef struct Flushreq Flushreq;
30 typedef struct Job Job;
32 int vers; /* incremented each clone/attach */
36 int busy; /* fid in use */
37 int ref; /* cleanup when drops to zero */
52 * result of the last lookup
84 jmp_buf masterjmp; /* return through here after a slave process has been created */
85 int *isslave; /* *isslave non-zero means this is a slave process */
86 long active; /* number of active slaves */
93 void rattach(Job*, Mfile*);
94 char* rwalk(Job*, Mfile*);
95 void ropen(Job*, Mfile*);
96 void rcreate(Job*, Mfile*);
97 void rread(Job*, Mfile*);
98 void rwrite(Job*, Mfile*);
99 void rclunk(Job*, Mfile*);
100 void rremove(Job*, Mfile*);
101 void rstat(Job*, Mfile*);
102 void rwstat(Job*, Mfile*);
104 void sendmsg(Job*, char*);
106 void mountinit(char*, char*);
111 char *genquery(Mfile*, char*);
112 char* ipinfoquery(Mfile*, char**, int);
113 int needproto(Network*, Ndbtuple*);
116 void readipinterfaces(void);
118 char* estrdup(char*);
121 void setext(char*, int, char*);
122 void cleanmf(Mfile*);
124 QLock dblock; /* mutex on database operations */
125 QLock netlock; /* mutex for netinit() */
127 char *logfile = "cs";
130 char netndb[Maxpath];
133 * Network specific translators
135 Ndbtuple* iplookup(Network*, char*, char*);
136 char* iptrans(Ndbtuple*, Network*, char*, char*, int);
137 Ndbtuple* telcolookup(Network*, char*, char*);
138 char* telcotrans(Ndbtuple*, Network*, char*, char*, int);
140 Ndbtuple* dnsiplookup(char*, Ndbs*, int);
141 Ndbtuple* myipinfo(Ndb *db, char **list, int n);
146 Ndbtuple *(*lookup)(Network*, char*, char*);
147 char *(*trans)(Ndbtuple*, Network*, char*, char*, int);
149 char considered; /* flag: ignored for "net!"? */
150 char fasttimeout; /* flag. was for IL */
151 char ipvers; /* flag: V4, V6 */
164 * net doesn't apply to (r)udp, icmp(v6), or telco (for speed).
166 Network network[] = {
167 { "il", iplookup, iptrans, 0, 1, V4, },
168 { "tcp", iplookup, iptrans, 0, 0, V4|V6, },
169 { "il", iplookup, iptrans, 0, 0, V4, },
170 { "udp", iplookup, iptrans, 1, 0, V4|V6, },
171 { "icmp", iplookup, iptrans, 1, 0, V4, },
172 { "icmpv6", iplookup, iptrans, 1, 0, V6, },
173 { "rudp", iplookup, iptrans, 1, 0, V4, },
174 { "ssh", iplookup, iptrans, 1, 0, V4|V6, },
175 { "telco", telcolookup, telcotrans, 1, 0, 0, },
182 int lookipvers = V4|V6;
186 Network *netlist; /* networks ordered by preference */
190 nstrcpy(char *to, char *from, int len)
192 strncpy(to, from, len);
199 fprint(2, "usage: %s [-46dn] [-f ndb-file] [-x netmtpt]\n", argv0);
204 * based on libthread's threadsetname, but drags in less library code.
205 * actually just sets the arguments displayed.
208 procsetname(char *fmt, ...)
216 cmdname = vsmprint(fmt, arg);
220 snprint(buf, sizeof buf, "#p/%d/args", getpid());
221 if((fd = open(buf, OWRITE)) >= 0){
222 write(fd, cmdname, strlen(cmdname)+1);
229 main(int argc, char *argv[])
232 char ext[Maxpath], servefile[Maxpath];
235 setnetmtpt(mntpt, sizeof(mntpt), nil);
248 dbfile = EARGF(usage());
254 setnetmtpt(mntpt, sizeof(mntpt), EARGF(usage()));
255 setext(ext, sizeof(ext), mntpt);
261 snprint(netndb, sizeof(netndb), "%s/ndb", mntpt);
263 fmtinstall('E', eipfmt);
264 fmtinstall('I', eipfmt);
265 fmtinstall('M', eipfmt);
266 fmtinstall('F', fcallfmt);
272 snprint(servefile, sizeof(servefile), "#s/cs%s", ext);
273 unmount(servefile, mntpt);
276 rfork(RFREND|RFNOTEG);
277 csuser = estrdup(getuser());
278 mountinit(servefile, mntpt);
285 * if a mount point is specified, set the cs extention to be the mount point
286 * with '_'s replacing '/'s
289 setext(char *ext, int n, char *p)
294 for(i = 0; i < n; i++){
306 mountinit(char *service, char *mntpt)
313 error("pipe failed");
318 f = create(service, OWRITE|ORCLOSE, 0666);
321 snprint(buf, sizeof(buf), "%d", p[1]);
322 if(write(f, buf, strlen(buf)) != strlen(buf))
323 error("write /srv/cs");
325 switch(rfork(RFFDG|RFPROC|RFNAMEG)){
328 procsetname("%s", mntpt);
331 error("fork failed");
334 * put ourselves into the file system
337 if(mount(p[1], -1, mntpt, MAFTER, "") < 0)
338 error("mount failed");
341 mfd[0] = mfd[1] = p[0];
347 db = ndbopen(dbfile);
349 error("can't open network database");
351 for(netdb = db; netdb != nil; netdb = netdb->next)
352 if(strcmp(netdb->file, netndb) == 0)
355 netdb = ndbopen(netndb);
358 db = ndbcat(netdb, db);
369 for(f = mlist; f != nil; f = f->next)
370 if(f->mf.busy && f->mf.fid == fid)
372 else if(ff == nil && !f->mf.busy && !f->mf.ref)
375 ff = emalloc(sizeof *f);
380 memset(mf, 0, sizeof *mf);
390 job = emalloc(sizeof *job);
394 job->request.tag = -1;
405 for(l = &joblist; *l != nil; l = &(*l)->next){
421 for(job = joblist; job != nil; job = job->next){
422 if(job->request.tag == tag && job->request.type != Tflush){
436 uchar mdata[IOHDRSZ + Maxfdata];
440 * if we ask dns to fulfill requests,
441 * a slave process is created to wait for replies. The
442 * master process returns immediately via a longjmp
443 * through 'masterjmp'.
445 * *isslave is a pointer into the call stack to a variable
446 * that tells whether or not the current process is a slave.
448 slaveflag = 0; /* init slave variable */
449 isslave = &slaveflag;
453 n = read9pmsg(mfd[0], mdata, sizeof mdata);
459 if(convM2S(mdata, n, &job->request) != n){
460 syslog(1, logfile, "format error %ux %ux %ux %ux %ux",
461 mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]);
466 mf = newfid(job->request.fid);
468 syslog(0, logfile, "%F", &job->request);
470 switch(job->request.type){
472 syslog(1, logfile, "unknown request type %d", job->request.type);
519 * slave processes die after replying
523 syslog(0, logfile, "slave death %d", getpid());
533 if(job->request.msize > IOHDRSZ + Maxfdata)
534 job->reply.msize = IOHDRSZ + Maxfdata;
536 job->reply.msize = job->request.msize;
537 if(strncmp(job->request.version, "9P2000", 6) != 0)
538 sendmsg(job, "unknown 9P version");
540 job->reply.version = "9P2000";
548 sendmsg(job, "cs: authentication not required");
552 * don't flush till all the slaves are done
557 flushjob(job->request.oldtag);
562 rattach(Job *job, Mfile *mf)
566 mf->user = estrdup(job->request.uname);
568 mf->qid.vers = vers++;
569 mf->qid.type = QTDIR;
571 job->reply.qid = mf->qid;
577 rwalk(Job *job, Mfile *mf)
588 elems = job->request.wname;
589 nelems = job->request.nwname;
590 job->reply.nwqid = 0;
592 if(job->request.newfid != job->request.fid){
594 nmf = newfid(job->request.newfid);
597 err = "clone to used channel";
601 nmf->user = estrdup(mf->user);
602 nmf->fid = job->request.newfid;
603 nmf->qid.vers = vers++;
606 /* else nmf will be nil */
611 for(i=0; i<nelems && i<MAXWELEM; i++){
612 if((qid.type & QTDIR) == 0){
613 err = "not a directory";
616 if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
620 job->reply.wqid[i] = qid;
624 if(strcmp(elems[i], "cs") == 0){
629 err = "file does not exist";
635 if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)){
649 ropen(Job *job, Mfile *mf)
655 mode = job->request.mode;
656 if(mf->qid.type & QTDIR){
658 err = "permission denied";
660 job->reply.qid = mf->qid;
661 job->reply.iounit = 0;
666 rcreate(Job *job, Mfile *mf)
669 sendmsg(job, "creation permission denied");
673 rread(Job *job, Mfile *mf)
676 long off, toff, clock;
683 off = job->request.offset;
684 cnt = job->request.count;
687 if(mf->qid.type & QTDIR){
690 memset(&dir, 0, sizeof dir);
692 dir.qid.type = QTFILE;
700 dir.atime = clock; /* wrong */
701 dir.mtime = clock; /* wrong */
702 n = convD2M(&dir, buf, sizeof buf);
704 job->reply.data = (char*)buf;
709 /* look for an answer at the right offset */
711 for(i = 0; mf->reply[i] != nil && i < mf->nreply; i++){
718 break; /* got something to return */
720 /* try looking up more answers */
721 if(lookup(mf) == 0 || job->flushed){
728 /* give back a single reply (or part of one) */
729 job->reply.data = mf->reply[i] + (off - toff);
730 if(cnt > toff - off + n)
736 job->reply.count = n;
739 if(--mf->ref == 0 && mf->busy == 0)
764 for(i = 0; i < mf->nreply; i++){
770 mf->nextnet = netlist;
774 rwrite(Job *job, Mfile *mf)
782 cnt = job->request.count;
783 if(mf->qid.type & QTDIR){
784 err = "can't write directory";
787 if(cnt >= Maxrequest){
788 err = "request too long";
791 job->request.data[cnt] = 0;
793 if(strcmp(mf->user, "none") == 0 || strcmp(mf->user, csuser) != 0)
794 goto query; /* skip special commands if not owner */
799 if(strncmp(job->request.data, "debug", 5)==0){
801 syslog(1, logfile, "debug %d", debug);
806 * toggle ipv4 lookups
808 if(strncmp(job->request.data, "ipv4", 4)==0){
810 syslog(1, logfile, "ipv4lookups %d", (lookipvers & V4) != 0);
815 * toggle ipv6 lookups
817 if(strncmp(job->request.data, "ipv6", 4)==0){
819 syslog(1, logfile, "ipv6lookups %d", (lookipvers & V6) != 0);
824 * add networks to the default list
826 if(strncmp(job->request.data, "add ", 4)==0){
827 if(job->request.data[cnt-1] == '\n')
828 job->request.data[cnt-1] = 0;
829 netadd(job->request.data+4);
837 if(strncmp(job->request.data, "refresh", 7)==0){
844 err = "query already in progress";
849 /* start transaction with a clean slate */
853 * look for a general query
855 if(*job->request.data == '!'){
856 err = genquery(mf, job->request.data+1);
861 syslog(0, logfile, "write %s", job->request.data);
865 n = getfields(job->request.data, field, 4, 1, "!");
868 mf->net = estrdup("net");
869 mf->host = estrdup(field[0]);
872 mf->rem = estrdup(field[3]);
875 mf->serv = estrdup(field[2]);
878 mf->host = estrdup(field[1]);
879 mf->net = estrdup(field[0]);
884 * do the first net worth of lookup
887 rerrstr(curerr, sizeof curerr);
892 if(--mf->ref == 0 && mf->busy == 0)
896 job->reply.count = cnt;
901 rclunk(Job *job, Mfile *mf)
913 rremove(Job *job, Mfile *mf)
916 sendmsg(job, "remove permission denied");
920 rstat(Job *job, Mfile *mf)
923 uchar buf[IOHDRSZ+Maxfdata];
925 memset(&dir, 0, sizeof dir);
926 if(mf->qid.type & QTDIR){
928 dir.mode = DMDIR|0555;
938 dir.atime = dir.mtime = time(0);
939 job->reply.nstat = convD2M(&dir, buf, sizeof buf);
940 job->reply.stat = buf;
945 rwstat(Job *job, Mfile *mf)
948 sendmsg(job, "wstat permission denied");
952 sendmsg(Job *job, char *err)
955 uchar mdata[IOHDRSZ + Maxfdata];
959 job->reply.type = Rerror;
960 snprint(ename, sizeof(ename), "cs: %s", err);
961 job->reply.ename = ename;
963 job->reply.type = job->request.type+1;
965 job->reply.tag = job->request.tag;
966 n = convS2M(&job->reply, mdata, sizeof mdata);
968 syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply);
972 if(job->flushed == 0)
973 if(write(mfd[1], mdata, n)!=n)
974 error("mount write");
977 syslog(0, logfile, "%F %d", &job->reply, n);
983 syslog(1, logfile, "%s: %r", s);
988 readipinterfaces(void)
996 ipifcs = readipifc(mntpt, ipifcs, -1);
997 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
998 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1005 qunlock(&ipifclock);
1010 * get the system name
1015 char eaddr[16], buf[Maxpath];
1023 if(mysysname != nil)
1027 * environment has priority.
1029 * on the sgi power the default system name
1030 * is the ip address. ignore that.
1033 p = getenv("sysname");
1034 if(p != nil && *p != 0){
1036 if(strcmp(attr, "ip") != 0) {
1043 /* try configured interfaces */
1045 t = s.t = myipinfo(db, &attr, 1);
1049 /* try ethernet interfaces */
1052 f = open(mntpt, OREAD);
1054 n = dirreadall(f, &d);
1057 for(f = 0; f < n; f++){
1058 if((d[f].mode & DMDIR) == 0 || strncmp(d[f].name, "ether", 5) != 0)
1060 snprint(buf, sizeof buf, "%s/%s", mntpt, d[f].name);
1061 if(myetheraddr(addr, buf) >= 0){
1062 snprint(eaddr, sizeof(eaddr), "%E", addr);
1063 free(ndbgetvalue(db, &s, "ether", eaddr, "sys", &t));
1072 /* nothing else worked, use ip address */
1074 t = s.t = myipinfo(db, &attr, 1);
1079 /* found in database */
1080 if((tt = ndbfindattr(t, s.t, "sys")) != nil)
1081 mysysname = estrdup(tt->val);
1082 else if((tt = ndbfindattr(t, s.t, "ip")) != nil)
1083 mysysname = estrdup(tt->val);
1086 if(mysysname == nil)
1090 /* set /dev/sysname if we now know it */
1091 f = open("/dev/sysname", OWRITE);
1093 write(f, mysysname, strlen(mysysname));
1099 * Set up a list of default networks by looking for
1103 netinit(int background)
1105 char clone[Maxpath];
1109 if(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT) != 0)
1114 /* add the mounted networks to the default list */
1115 for(np = network; np->net != nil; np++){
1118 snprint(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net);
1119 if(access(clone, AEXIST) < 0)
1130 /* find out what our ip addresses are */
1133 /* set the system name if we need to, these days ip is all we have */
1137 syslog(0, logfile, "mysysname %s", mysysname?mysysname:"???");
1146 * add networks to the standard list
1155 n = getfields(p, field, 12, 1, " ");
1156 for(i = 0; i < n; i++){
1157 for(np = network; np->net != nil; np++){
1158 if(strcmp(field[i], np->net) != 0)
1174 lookforproto(Ndbtuple *t, char *proto)
1176 for(; t != nil; t = t->entry)
1177 if(strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0)
1183 * lookup a request. the network "net" means we should pick the
1184 * best network to get there.
1192 char reply[Maxreply];
1193 int i, rv, fasttimeout;
1195 /* open up the standard db files */
1199 error("can't open mf->network database\n");
1202 return 0; /* must have been a genquery */
1205 if(strcmp(mf->net, "net") == 0){
1207 * go through set of default nets
1209 for(np = mf->nextnet; np != nil && rv == 0; np = np->next){
1210 nt = (*np->lookup)(np, mf->host, mf->serv);
1213 fasttimeout = np->fasttimeout && !lookforproto(nt, np->net);
1214 for(t = nt; mf->nreply < Nreply && t != nil; t = t->entry){
1215 cp = (*np->trans)(t, np, mf->serv, mf->rem, fasttimeout);
1217 /* avoid duplicates */
1218 for(i = 0; i < mf->nreply; i++)
1219 if(strcmp(mf->reply[i], cp) == 0)
1221 if(i == mf->nreply){
1222 /* save the reply */
1223 mf->replylen[mf->nreply] = strlen(cp);
1224 mf->reply[mf->nreply++] = cp;
1237 * if not /net, we only get one lookup
1243 * look for a specific network
1245 for(np = network; np->net != nil; np++){
1248 if(strcmp(np->net, mf->net) == 0)
1256 nt = (*np->lookup)(np, mf->host, mf->serv);
1257 for(t = nt; mf->nreply < Nreply && t != nil; t = t->entry){
1258 cp = (*np->trans)(t, np, mf->serv, mf->rem, 0);
1260 mf->replylen[mf->nreply] = strlen(cp);
1261 mf->reply[mf->nreply++] = cp;
1269 * not a known network, don't translate host or service
1272 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s",
1273 mntpt, mf->net, mf->host, mf->serv);
1275 snprint(reply, sizeof(reply), "%s/%s/clone %s",
1276 mntpt, mf->net, mf->host);
1277 mf->reply[0] = estrdup(reply);
1278 mf->replylen[0] = strlen(reply);
1285 * translate an ip service name into a port number. If it's a numeric port
1286 * number, look for restricted access.
1288 * the service '*' needs no translation.
1291 ipserv(Network *np, char *name, char *buf, int blen)
1299 /* '*' means any service */
1300 if(strcmp(name, "*") == 0){
1301 nstrcpy(buf, name, blen);
1305 /* see if it's numeric or symbolic */
1306 for(p = name; *p; p++){
1309 else if(isalpha(*p) || *p == '-' || *p == '$')
1317 p = ndbgetvalue(db, &s, np->net, name, "port", &t);
1321 /* look up only for tcp ports < 1024 to get the restricted
1324 if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0)
1325 p = ndbgetvalue(db, &s, "port", name, "port", &t);
1331 for(nt = t; nt != nil; nt = nt->entry)
1332 if(strcmp(nt->attr, "restricted") == 0)
1336 snprint(buf, blen, "%s%s", p, restr ? "!r" : "");
1343 myipinfo(Ndb *db, char **list, int n)
1352 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
1353 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1354 snprint(ip, sizeof(ip), "%I", lifc->ip);
1355 nt = ndbipinfo(db, "ip", ip, list, n);
1356 t = ndbconcatenate(t, nt);
1359 qunlock(&ipifclock);
1365 * reorder according to our interfaces
1368 ipreorder(Ndbtuple *t)
1371 uchar ip[IPaddrlen];
1372 uchar net[IPaddrlen];
1373 uchar tnet[IPaddrlen];
1381 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
1382 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1383 maskip(lifc->ip, lifc->mask, net);
1384 for(nt = t; nt != nil; nt = nt->entry){
1385 if(strcmp(nt->attr, "ip") != 0)
1387 if(parseip(ip, nt->val) == -1)
1389 maskip(ip, lifc->mask, tnet);
1390 if(memcmp(net, tnet, IPaddrlen) == 0){
1391 qunlock(&ipifclock);
1392 return ndbreorder(t, nt);
1397 qunlock(&ipifclock);
1403 ndbline(Ndbtuple *t)
1407 for(nt = t; nt != nil; nt = nt->entry){
1408 if(nt->entry == nil)
1411 nt->line = nt->entry;
1417 * lookup an ip destination
1420 iplookuphost(Network *np, char *host)
1422 char *attr, *dnsname;
1427 * turn '[ip address]' into just 'ip address'
1430 char tmp[Maxhost], *x;
1432 nstrcpy(tmp, host, sizeof tmp);
1434 if((x = strchr(++host, ']')) != nil)
1438 /* for dial strings with no host */
1439 if(strcmp(host, "*") == 0)
1440 return ndbline(ndbnew("ip", "*"));
1443 * hack till we go v6 :: = 0.0.0.0
1445 if(strcmp("::", host) == 0)
1446 return ndbline(ndbnew("ip", "*"));
1449 * just accept addresses
1451 attr = ipattr(host);
1452 if(strcmp(attr, "ip") == 0)
1453 return ndbline(ndbnew("ip", host));
1456 * give the domain name server the first opportunity to
1457 * resolve domain names. if that fails try the database.
1460 if(strcmp(attr, "dom") == 0)
1461 t = dnsiplookup(host, &s, np->ipvers);
1463 for(nt = ndbsearch(db, &s, attr, host); nt != nil; nt = ndbsnext(&s, attr, host)){
1464 if(ndbfindattr(nt, s.t, "ip") == nil){
1468 t = ndbconcatenate(t, ndbreorder(nt, s.t));
1473 if(strcmp(attr, "dom") != 0){
1474 dnsname = ndbgetvalue(db, &s, attr, host, "dom", nil);
1476 t = dnsiplookup(dnsname, &s, np->ipvers);
1481 t = dnsiplookup(host, &s, np->ipvers);
1487 * reorder the tuple to have the matched line first and
1488 * save that in the request structure.
1490 return ndbreorder(t, s.t);
1495 iplookup(Network *np, char *host, char *serv)
1497 Ndbtuple *l, *t, *nt;
1498 char ts[Maxservice], *attr;
1501 * start with the service since it's the most likely to fail
1502 * and costs the least
1504 if(serv == nil || ipserv(np, serv, ts, sizeof ts) == nil){
1505 werrstr("can't translate service");
1510 * '$' means the rest of the name is an attribute that we
1511 * need to search for
1513 werrstr("can't translate address");
1517 l = myipinfo(db, &attr, 1);
1518 for(nt = l; nt != nil; nt = nt->entry){
1519 if(strcmp(nt->attr, attr) == 0)
1520 t = ndbconcatenate(t, iplookuphost(np, nt->val));
1524 t = iplookuphost(np, host);
1526 return ipreorder(t);
1531 * translate an ip address
1534 iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int fasttimeout)
1536 char ts[Maxservice];
1537 char reply[Maxreply];
1539 uchar ip[IPaddrlen];
1541 if(strcmp(t->attr, "ip") != 0)
1544 if(serv == nil || ipserv(np, serv, ts, sizeof ts) == nil){
1545 werrstr("can't translate service");
1550 snprint(x, sizeof(x), "!%s", rem);
1555 snprint(reply, sizeof(reply), "%s/%s/clone %s%s",
1556 mntpt, np->net, ts, x);
1558 if(parseip(ip, t->val) == -1)
1560 if((np->ipvers & confipvers & (isv4(ip) ? V4 : V6)) == 0)
1562 snprint(reply, sizeof(reply), "%s/%s/clone %I!%s%s%s",
1563 mntpt, np->net, ip, ts, x, fasttimeout? "!fasttimeout": "");
1566 return estrdup(reply);
1570 * lookup a telephone number
1573 telcolookup(Network *np, char *host, char *serv)
1580 werrstr("can't translate address");
1581 free(ndbgetvalue(db, &s, "sys", host, "telco", &t));
1583 return ndbline(ndbnew("telco", host));
1585 return ndbreorder(t, s.t);
1589 * translate a telephone address
1592 telcotrans(Ndbtuple *t, Network *np, char *serv, char *rem, int)
1594 char reply[Maxreply];
1597 if(strcmp(t->attr, "telco") != 0)
1601 snprint(x, sizeof(x), "!%s", rem);
1605 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net,
1608 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net,
1610 return estrdup(reply);
1614 * create a slave process to handle a request to avoid one request blocking
1615 * another. parent returns to job loop.
1621 return; /* we're already a slave process */
1622 if(ainc(&active) >= Maxactive){
1626 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
1633 syslog(0, logfile, "slave %d", getpid());
1634 procsetname("%s", host);
1637 longjmp(masterjmp, 1);
1645 static QLock mountlock;
1654 snprint(buf, sizeof(buf), "%s/dns", mntpt);
1655 if(access(buf, AEXIST) == 0)
1657 if(strcmp(mntpt, "/net") == 0)
1658 snprint(buf, sizeof(buf), "/srv/dns");
1660 snprint(buf, sizeof(buf), "/srv/dns%s", mntpt);
1661 while((p = strchr(buf+8, '/')) != nil)
1664 if((fd = open(buf, ORDWR)) < 0){
1666 qunlock(&mountlock);
1669 if(mount(fd, -1, mntpt, MAFTER, "") < 0){
1675 qunlock(&mountlock);
1680 dnsip6lookup(char *mntpt, char *buf, Ndbtuple *t)
1684 t6 = dnsquery(mntpt, buf, "ipv6"); /* lookup AAAA dns RRs */
1688 /* convert ipv6 attr to ip */
1689 for (tt = t6; tt != nil; tt = tt->entry)
1690 if (strcmp(tt->attr, "ipv6") == 0)
1691 strcpy(tt->attr, "ip");
1693 /* append t6 list to t list */
1694 return ndbconcatenate(t, t6);
1698 * call the dns process and have it try to translate a name
1701 dnsiplookup(char *host, Ndbs *s, int ipvers)
1706 ipvers &= confipvers & lookipvers;
1708 werrstr("no ip address");
1715 werrstr("too much activity");
1724 if(strcmp(ipattr(host), "ip") == 0)
1725 t = dnsquery(mntpt, host, "ptr");
1729 t = dnsquery(mntpt, host, "ip");
1731 t = dnsip6lookup(mntpt, host, t);
1736 rerrstr(buf, sizeof buf);
1737 if(strstr(buf, "exist") != nil)
1738 werrstr("can't translate address: %s", buf);
1739 else if(strstr(buf, "dns failure") != nil)
1740 werrstr("temporary problem: %s", buf);
1748 qmatch(Ndbtuple *t, char **attr, char **val, int n)
1753 for(i = 1; i < n; i++){
1755 for(nt = t; nt != nil; nt = nt->entry)
1756 if(strcmp(attr[i], nt->attr) == 0)
1757 if(strcmp(val[i], "*") == 0
1758 || strcmp(val[i], nt->val) == 0){
1769 qreply(Mfile *mf, Ndbtuple *t)
1775 for(nt = t; mf->nreply < Nreply && nt != nil; nt = nt->entry){
1776 s_append(s, nt->attr);
1778 s_append(s, nt->val);
1780 if(nt->line != nt->entry){
1781 mf->replylen[mf->nreply] = s_len(s);
1782 mf->reply[mf->nreply++] = estrdup(s_to_c(s));
1796 * generic query lookup. The query is of one of the following
1799 * attr1=val1 attr2=val2 attr3=val3 ...
1801 * returns the matching tuple
1803 * ipinfo attr=val attr1 attr2 attr3 ...
1805 * is like ipinfo and returns the attr{1-n}
1806 * associated with the ip address.
1809 genquery(Mfile *mf, char *query)
1813 char *attr[Maxattr];
1818 n = getfields(query, attr, nelem(attr), 1, " ");
1822 if(strcmp(attr[0], "ipinfo") == 0)
1823 return ipinfoquery(mf, attr, n);
1826 for(i = 0; i < n; i++){
1827 p = strchr(attr[i], '=');
1834 /* give dns a chance */
1835 if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){
1836 t = dnsiplookup(val[0], &s, lookipvers);
1838 if(qmatch(t, attr, val, n)){
1847 /* first pair is always the key. It can't be a '*' */
1848 t = ndbsearch(db, &s, attr[0], val[0]);
1850 /* search is the and of all the pairs */
1852 if(qmatch(t, attr, val, n)){
1859 t = ndbsnext(&s, attr[0], val[0]);
1866 * resolve an ip address
1869 ipresolve(char *attr, char *host)
1871 Ndbtuple *t, *nt, **l;
1873 t = iplookup(&network[Ntcp], host, "*");
1874 for(l = &t; *l != nil; ){
1876 if(strcmp(nt->attr, "ip") != 0){
1882 nstrcpy(nt->attr, attr, sizeof(nt->attr));
1892 ndbdedup(Ndbtuple *t)
1894 Ndbtuple *tt, *nt, **l;
1896 for(nt = t; nt != nil; nt = nt->entry){
1897 for(l = &nt->entry; (tt = *l) != nil;){
1898 if(strcmp(nt->attr, tt->attr) != 0
1899 || strcmp(nt->val, tt->val) != 0){
1912 ipinfoquery(Mfile *mf, char **list, int n)
1915 uchar resolve[Maxattr];
1916 Ndbtuple *t, *nt, **l;
1925 /* get search attribute=value, or assume myip */
1927 if((val = strchr(attr, '=')) != nil){
1940 * don't let ndbipinfo resolve the addresses, we're
1944 for(i = 0; i < n; i++)
1945 if(*list[i] == '@'){ /* @attr=val ? */
1947 resolve[i] = 1; /* we'll resolve it */
1953 t = myipinfo(db, list, n);
1955 t = ndbipinfo(db, attr, val, list, n);
1962 for(l = &t; *l != nil;){
1965 /* already an address? */
1966 if(strcmp(ipattr(nt->val), "ip") == 0){
1971 /* user wants it resolved? */
1972 for(i = 0; i < n; i++)
1973 if(strcmp(list[i], nt->attr) == 0)
1975 if(i >= n || resolve[i] == 0){
1980 /* resolve address and replace entry */
1981 *l = ipresolve(nt->attr, nt->val);
1992 /* make it all one line */
2007 error("out of memory");
2021 error("out of memory");
2022 memmove(p, s, size);