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
14 #include <sys/types.h>
33 Maxhost= 64, /* maximum host name size */
34 Maxservice= 64, /* maximum service name size */
40 typedef struct Mfile Mfile;
41 typedef struct Mlist Mlist;
42 typedef struct Network Network;
43 typedef struct Flushreq Flushreq;
44 typedef struct Job Job;
46 int vers; /* incremented each clone/attach */
47 /* need to resolve the #inluce for all this stuff. */
48 #define DMDIR 0x80000000 /* mode bit for directories */
70 * result of the last lookup
106 char *dbfile = "lib/ndb/local";
107 struct ndb *db, *netdb;
111 void rattach(Job*, Mfile*);
112 char* rwalk(Job*, Mfile*);
113 void ropen(Job*, Mfile*);
114 void rcreate(Job*, Mfile*);
115 void rread(Job*, Mfile*);
116 void rwrite(Job*, Mfile*);
117 void rclunk(Job*, Mfile*);
118 void rremove(Job*, Mfile*);
119 void rstat(Job*, Mfile*);
120 void rwstat(Job*, Mfile*);
122 void sendmsg(Job*, char*);
123 void mountinit(char*, char*);
128 char *genquery(Mfile*, char*);
129 char* ipinfoquery(Mfile*, char**, int);
130 int needproto(Network*, struct ndbtuple*);
132 struct ndbtuple* reorder(struct ndbtuple*, struct ndbtuple*);
134 void readipinterfaces(void);
136 char* estrdup(char*);
139 void setext(char*, int, char*);
140 void cleanmf(Mfile*);
142 extern void paralloc(void);
144 spinlock_t dblock; /* mutex on database operations */
145 spinlock_t netlock; /* mutex for netinit() */
147 char *logfile = "cs";
148 char *paranoiafile = "cs.paranoia";
151 char netndb[Maxpath];
154 * Network specific translators
156 struct ndbtuple* iplookup(Network*, char*, char*, int);
157 char* iptrans(struct ndbtuple*, Network*, char*, char*, int);
158 struct ndbtuple* telcolookup(Network*, char*, char*, int);
159 char* telcotrans(struct ndbtuple*, Network*, char*, char*, int);
160 struct ndbtuple* dnsiplookup(char*, struct ndbs*);
165 struct ndbtuple *(*lookup)(Network*, char*, char*, int);
166 char *(*trans)(struct ndbtuple*, Network*, char*, char*, int);
167 int considered; /* flag: ignored for "net!"? */
168 int fasttimeouthack; /* flag. was for IL */
178 * net doesn't apply to (r)udp, icmp(v6), or telco (for speed).
180 Network network[] = {
181 [Ntcp] { "tcp", iplookup, iptrans, 0 },
182 { "udp", iplookup, iptrans, 1 },
183 { "icmp", iplookup, iptrans, 1 },
184 { "icmpv6", iplookup, iptrans, 1 },
185 { "rudp", iplookup, iptrans, 1 },
186 { "ssh", iplookup, iptrans, 1 },
187 { "telco", telcolookup, telcotrans, 1 },
191 spinlock_t ipifclock;
192 struct ipifc *ipifcs;
194 char eaddr[16]; /* ascii ethernet address */
195 char ipaddr[64]; /* ascii internet address */
196 uint8_t ipa[IPaddrlen]; /* binary internet address */
199 Network *netlist; /* networks ordered by preference */
203 nstrcpy(char *to, char *from, int len)
205 strncpy(to, from, len);
213 fprintf(stderr, "CS:usage: %s [-dn] [-f ndb-file] [-x netmtpt]\n", argv0);
214 fprintf(stderr, "CS:usage");
219 * based on libthread's threadsetname, but drags in less library code.
220 * actually just sets the arguments displayed.
223 procsetname(char *fmt, ...)
233 cmdname = vsmprint(fmt, arg);
237 snprintf(buf, sizeof buf, "#p/%d/args", getpid());
238 if((fd = open(buf, OWRITE)) >= 0){
239 write(fd, cmdname, strlen(cmdname)+1);
247 main(int argc, char *argv[])
250 char ext[Maxpath], servefile[Maxpath];
253 setnetmtpt(mntpt, sizeof(mntpt), NULL);
256 while (argc && **argv == '-'){
276 setnetmtpt(mntpt, sizeof(mntpt), argv[1]);
278 setext(ext, sizeof(ext), mntpt);
284 //rfork(RFREND|RFNOTEG);
286 snprintf(servefile, sizeof(servefile), "#s/cs%s", ext);
287 snprintf(netndb, sizeof(netndb), "%s/ndb", mntpt);
288 syscall(SYS_nunmount, (unsigned long)servefile, (unsigned long)mntpt);
295 mountinit(servefile, mntpt);
302 * if a mount point is specified, set the cs extention to be the mount point
303 * with '_'s replacing '/'s
306 setext(char *ext, int n, char *p)
311 for(i = 0; i < n; i++){
323 mountinit(char *service, char *mntpt)
330 ret = syscall(SYS_pipe, (unsigned long)p);
332 error(1, 0, "pipe: %r");
338 * ORCLOSE means remove on last close. Handy. Not here yet.
340 f = open(service, O_WRONLY|O_CREAT/*|ORCLOSE*/, 0666);
342 error(1, 0, "%s: %r",service);
343 snprintf(buf, sizeof(buf), "%d", p[1]);
344 if(write(f, buf, strlen(buf)) != strlen(buf))
345 error(1, 0, "Write %s: %r", service);
346 /* using #s: we create a pipe and drop it into #s.
347 * we no longer mount. That's up to you.
348 * #s will route requests to us.
352 mfd[0] = mfd[1] = p[0];
358 db = ndbopen(dbfile);
360 error(1, 0, "%s: %r","can't open network database");
362 netdb = ndbopen(netndb);
365 db = ndbcat(netdb, db);
376 for(f = mlist; f; f = f->next)
377 if(f->mf.busy && f->mf.fid == fid)
379 else if(!ff && !f->mf.busy)
382 ff = emalloc(sizeof *f);
387 memset(mf, 0, sizeof *mf);
397 job = calloc(1, sizeof(Job));
399 error(1, 0, "%s: %r","job calloc");
405 job->request.tag = -1;
406 // //unlock(&joblock);
416 for(l = &joblist; *l; l = &(*l)->next){
432 for(job = joblist; job; job = job->next){
433 if(job->request.tag == tag && job->request.type != Tflush){
441 void *job_thread(void* arg)
446 mf = newfid(job->request.fid);
449 fprintf(stderr, "CS:%F", &job->request);
450 switch(job->request.type){
452 fprintf(stderr, "CS:unknown request type %d", job->request.type);
499 fprintf(stderr, "CS:Job done\n");
509 uint8_t mdata[IOHDRSZ + Maxfdata];
513 * each request is handled via a thread. Somewhat less efficient than the old
514 * cs but way cleaner.
518 n = read9pmsg(mfd[0], mdata, sizeof mdata);
520 error(1, 0, "%s: %r","mount read");
522 if(convM2S(mdata, n, &job->request) != n){
523 error(1, 0, "format error %ux %ux %ux %ux %ux",
524 mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]);
528 /* stash the thread in the job so we can join them all
529 * later if we want to.
532 if (pthread_create(&job->thread, NULL, &job_thread, job)) {
533 error(1, 0, "%s: %r","Failed to create job");
546 if(job->request.msize > IOHDRSZ + Maxfdata)
547 job->reply.msize = IOHDRSZ + Maxfdata;
549 job->reply.msize = job->request.msize;
550 if(strncmp(job->request.version, "9P2000", 6) != 0)
551 sendmsg(job, "unknown 9P version");
553 job->reply.version = "9P2000";
561 sendmsg(job, "cs: authentication not required");
565 * don't flush till all the threads are done
570 flushjob(job->request.oldtag);
575 rattach(Job *job, Mfile *mf)
579 mf->user = estrdup(job->request.uname);
581 mf->qid.vers = vers++;
582 mf->qid.type = QTDIR;
584 job->reply.qid = mf->qid;
590 rwalk(Job *job, Mfile *mf)
601 elems = job->request.wname;
602 nelems = job->request.nwname;
603 job->reply.nwqid = 0;
605 if(job->request.newfid != job->request.fid){
607 nmf = newfid(job->request.newfid);
610 err = "clone to used channel";
614 nmf->user = estrdup(mf->user);
615 nmf->fid = job->request.newfid;
616 nmf->qid.vers = vers++;
619 /* else nmf will be nil */
624 for(i=0; i<nelems && i<MAXWELEM; i++){
625 if((qid.type & QTDIR) == 0){
626 err = "not a directory";
629 if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
633 job->reply.wqid[i] = qid;
637 if(strcmp(elems[i], "cs") == 0){
643 snprintf(err, 4096,"%s:file does not exist", elems[i]);
649 if(nmf != NULL && (err!=NULL || job->reply.nwqid<nelems)){
661 /* we can fix this or we can get a real language. Guess how Ron votes? */
662 err = "file does not exist";
667 ropen(Job *job, Mfile *mf)
673 mode = job->request.mode;
674 if(mf->qid.type & QTDIR){
676 err = "permission denied";
678 job->reply.qid = mf->qid;
679 job->reply.iounit = 0;
684 rcreate(Job *job, Mfile *mf)
686 sendmsg(job, "creation permission denied");
690 rread(Job *job, Mfile *mf)
693 long off, toff, clock;
695 uint8_t buf[Maxfdata];
700 off = job->request.offset;
701 cnt = job->request.count;
702 if(mf->qid.type & QTDIR){
705 memset(&dir, 0, sizeof dir);
707 dir.qid.type = QTFILE;
715 dir.atime = clock; /* wrong */
716 dir.mtime = clock; /* wrong */
717 n = convD2M(&dir, buf, sizeof buf);
719 job->reply.data = (char*)buf;
722 /* look for an answer at the right offset */
724 for(i = 0; mf->reply[i] && i < mf->nreply; i++){
731 break; /* got something to return */
733 /* try looking up more answers */
741 /* give back a single reply (or part of one) */
742 job->reply.data = mf->reply[i] + (off - toff);
743 if(cnt > toff - off + n)
749 job->reply.count = n;
761 if(mf->host != NULL){
765 if(mf->serv != NULL){
773 for(i = 0; i < mf->nreply; i++){
779 mf->nextnet = netlist;
783 rwrite(Job *job, Mfile *mf)
791 cnt = job->request.count;
792 if(mf->qid.type & QTDIR){
793 err = "can't write directory";
796 if(cnt >= Maxrequest){
797 err = "request too long";
800 job->request.data[cnt] = 0;
804 if(strncmp(job->request.data, "debug", 5)==0){
806 fprintf(stderr, "CS:debug %d", debug);
811 * toggle ipv6 lookups
813 if(strncmp(job->request.data, "ipv6", 4)==0){
815 fprintf(stderr, "CS:ipv6lookups %d", ipv6lookups);
822 if(strncmp(job->request.data, "paranoia", 8)==0){
824 fprintf(stderr, "CS:paranoia %d", paranoia);
829 * add networks to the default list
831 if(strncmp(job->request.data, "add ", 4)==0){
832 if(job->request.data[cnt-1] == '\n')
833 job->request.data[cnt-1] = 0;
834 netadd(job->request.data+4);
842 if(strncmp(job->request.data, "refresh", 7)==0){
847 /* start transaction with a clean slate */
851 * look for a general query
853 if(*job->request.data == '!'){
854 err = genquery(mf, job->request.data+1);
859 fprintf(stderr, "CS:write %s", job->request.data);
861 fprintf(stderr, "CS:write %s by %s", job->request.data, mf->user);
866 n = getfields(job->request.data, field, 4, 1, "!");
869 mf->net = strdup("net");
870 mf->host = strdup(field[0]);
873 mf->rem = strdup(field[3]);
876 mf->serv = strdup(field[2]);
879 mf->host = strdup(field[1]);
880 mf->net = strdup(field[0]);
884 * do the first net worth of lookup
887 snprintf(curerr, sizeof curerr, "%r");
891 job->reply.count = cnt;
896 rclunk(Job *job, Mfile *mf)
907 rremove(Job *job, Mfile *mf)
909 sendmsg(job, "remove permission denied");
913 rstat(Job *job, Mfile *mf)
916 uint8_t buf[IOHDRSZ+Maxfdata];
918 memset(&dir, 0, sizeof dir);
919 if(mf->qid.type & QTDIR){
921 dir.mode = DMDIR|0555;
931 //dir.atime = dir.mtime = time(0);
932 job->reply.nstat = convD2M(&dir, buf, sizeof buf);
933 job->reply.stat = buf;
938 rwstat(Job *job, Mfile *mf)
940 sendmsg(job, "wstat permission denied");
944 sendmsg(Job *job, char *err)
947 uint8_t mdata[IOHDRSZ + Maxfdata];
951 job->reply.type = Rerror;
952 snprintf(ename, sizeof(ename), "cs: %s", err);
953 job->reply.ename = ename;
955 job->reply.type = job->request.type+1;
957 job->reply.tag = job->request.tag;
958 n = convS2M(&job->reply, mdata, sizeof mdata);
960 fprintf(stderr, "CS:sendmsg convS2M of %F returns 0", &job->reply);
964 if(job->flushed == 0)
965 if(write(mfd[1], mdata, n)!=n)
966 error(1, 0, "%s: %r","mount write");
969 fprintf(stderr, "CS:%F %d", &job->reply, n);
973 isvalidip(uint8_t *ip)
975 return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0;
978 static uint8_t loopbacknet[IPaddrlen] = {
984 static uint8_t loopbackmask[IPaddrlen] = {
985 0xff, 0xff, 0xff, 0xff,
986 0xff, 0xff, 0xff, 0xff,
987 0xff, 0xff, 0xff, 0xff,
992 readipinterfaces(void)
994 if(myipaddr(ipa, mntpt) != 0)
995 ipmove(ipa, IPnoaddr);
996 snprintf(ipaddr, sizeof(ipaddr), "%I", ipa);
998 fprintf(stderr, "CS:dns", "ipaddr is %s\n", ipaddr);
1002 * get the system name
1008 struct ndbtuple *t, *tt;
1014 /* use environment, ether addr, or ipaddr to get system name */
1017 * environment has priority.
1019 * on the sgi power the default system name
1020 * is the ip address. ignore that.
1023 p = getenv("sysname");
1026 if(strcmp(attr, "ip") != 0)
1027 mysysname = strdup(p);
1031 * the /net/ndb contains what the network
1032 * figured out from DHCP. use that name if
1035 if(mysysname == 0 && netdb != NULL){
1037 for(tt = t = ndbparse(netdb); t != NULL; t = t->entry){
1038 if(strcmp(t->attr, "sys") == 0){
1039 mysysname = strdup(t->val);
1046 /* next network database, ip address, and ether address to find a name */
1050 free(ndbgetvalue(db, &s, "ip", ipaddr, "sys", &t));
1052 for(f = 0; f < 3; f++){
1053 snprintf(buf, sizeof buf, "%s/ether%d", mntpt, f);
1054 if(myetheraddr(addr, buf) >= 0){
1055 snprintf(eaddr, sizeof(eaddr), "%E", addr);
1056 free(ndbgetvalue(db, &s, "ether", eaddr, "sys", &t));
1062 for(tt = t; tt != NULL; tt = tt->entry){
1063 if(strcmp(tt->attr, "sys") == 0){
1064 mysysname = strdup(tt->val);
1071 /* nothing else worked, use the ip address */
1072 if(mysysname == 0 && isvalidip(ipa))
1073 mysysname = strdup(ipaddr);
1076 /* set /dev/sysname if we now know it */
1078 f = open("/dev/sysname", O_RDWR);
1080 write(f, mysysname, strlen(mysysname));
1088 * Set up a list of default networks by looking for
1090 * For now, never background.
1093 netinit(int background)
1095 char clone[Maxpath];
1099 /* add the mounted networks to the default list */
1100 for(np = network; np->net; np++){
1104 snprintf(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net);
1105 fuckup = open(clone, O_RDONLY);
1109 //if(access(clone, R_OK))
1120 /* find out what our ip address is */
1123 /* set the system name if we need to, these days ip is all we have */
1127 fprintf(stderr, logfile, "CS:mysysname %s eaddr %s ipaddr %s ipa %I\n",
1128 mysysname?mysysname:"???", eaddr, ipaddr, ipa);
1133 * add networks to the standard list
1142 n = getfields(p, field, 12, 1, " ");
1143 for(i = 0; i < n; i++){
1144 for(np = network; np->net; np++){
1145 if(strcmp(field[i], np->net) != 0)
1161 lookforproto(struct ndbtuple *t, char *proto)
1163 for(; t != NULL; t = t->entry)
1164 if(strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0)
1170 * lookup a request. the network "net" means we should pick the
1171 * best network to get there.
1178 struct ndbtuple *nt, *t;
1179 char reply[Maxreply];
1183 /* open up the standard db files */
1187 error(1, 0, "%s: %r","can't open mf->network database\n");
1192 return 0; /* must have been a genquery */
1194 if(strcmp(mf->net, "net") == 0){
1196 * go through set of default nets
1198 for(np = mf->nextnet; np; np = np->next){
1199 nt = (*np->lookup)(np, mf->host, mf->serv, 1);
1202 hack = np->fasttimeouthack && !lookforproto(nt, np->net);
1203 for(t = nt; mf->nreply < Nreply && t; t = t->entry){
1204 cp = (*np->trans)(t, np, mf->serv, mf->rem, hack);
1206 /* avoid duplicates */
1207 for(i = 0; i < mf->nreply; i++)
1208 if(strcmp(mf->reply[i], cp) == 0)
1210 if(i == mf->nreply){
1211 /* save the reply */
1212 mf->replylen[mf->nreply] = strlen(cp);
1213 mf->reply[mf->nreply++] = cp;
1227 * if not /net, we only get one lookup
1232 * look for a specific network
1234 for(np = netlist; np && np->net != NULL; np++){
1235 if(np->fasttimeouthack)
1237 if(strcmp(np->net, mf->net) == 0)
1241 if(np && np->net != NULL){
1245 nt = (*np->lookup)(np, mf->host, mf->serv, 1);
1246 for(t = nt; mf->nreply < Nreply && t; t = t->entry){
1247 cp = (*np->trans)(t, np, mf->serv, mf->rem, 0);
1249 mf->replylen[mf->nreply] = strlen(cp);
1250 mf->reply[mf->nreply++] = cp;
1258 * not a known network, don't translate host or service
1261 snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s",
1262 mntpt, mf->net, mf->host, mf->serv);
1264 snprintf(reply, sizeof(reply), "%s/%s/clone %s",
1265 mntpt, mf->net, mf->host);
1266 mf->reply[0] = strdup(reply);
1267 mf->replylen[0] = strlen(reply);
1274 * translate an ip service name into a port number. If it's a numeric port
1275 * number, look for restricted access.
1277 * the service '*' needs no translation.
1280 ipserv(Network *np, char *name, char *buf, int blen)
1286 struct ndbtuple *t, *nt;
1289 /* '*' means any service */
1290 if(strcmp(name, "*")==0){
1295 /* see if it's numeric or symbolic */
1297 for(p = name; *p; p++){
1300 else if(isalpha(*p) || *p == '-' || *p == '$')
1308 p = ndbgetvalue(db, &s, np->net, name, "port", &t);
1312 /* look up only for tcp ports < 1024 to get the restricted
1315 if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0)
1316 p = ndbgetvalue(db, &s, "port", name, "port", &t);
1322 for(nt = t; nt; nt = nt->entry)
1323 if(strcmp(nt->attr, "restricted") == 0)
1327 snprintf(buf, blen, "%s%s", p, restr ? "!r" : "");
1333 * lookup an ip attribute
1336 ipattrlookup(struct ndb *db, char *ipa, char *attr, char *val, int vlen)
1339 struct ndbtuple *t, *nt;
1343 t = ndbipinfo(db, "ip", ipa, alist, 1);
1346 for(nt = t; nt != NULL; nt = nt->entry){
1347 if(strcmp(nt->attr, attr) == 0){
1348 nstrcpy(val, nt->val, vlen);
1354 /* we shouldn't get here */
1360 * lookup (and translate) an ip destination
1363 iplookup(Network *np, char *host, char *serv, int nolookup)
1365 char *attr, *dnsname;
1366 struct ndbtuple *t, *nt;
1368 char ts[Maxservice];
1369 char dollar[Maxhost];
1370 uint8_t ip[IPaddrlen];
1371 uint8_t net[IPaddrlen];
1372 uint8_t tnet[IPaddrlen];
1374 struct iplifc *lifc;
1377 * start with the service since it's the most likely to fail
1378 * and costs the least
1380 werrstr("can't translate address");
1381 if(serv==0 || ipserv(np, serv, ts, sizeof ts) == 0){
1382 werrstr("can't translate service");
1386 /* for dial strings with no host */
1387 if(strcmp(host, "*") == 0)
1388 return ndbnew("ip", "*");
1391 * hack till we go v6 :: = 0.0.0.0
1393 if(strcmp("::", host) == 0)
1394 return ndbnew("ip", "*");
1397 * '$' means the rest of the name is an attribute that we
1398 * need to search for
1401 if(ipattrlookup(db, ipaddr, host+1, dollar, sizeof dollar))
1406 * turn '[ip address]' into just 'ip address'
1408 if(*host == '[' && host[strlen(host)-1] == ']'){
1410 host[strlen(host)-1] = 0;
1414 * just accept addresses
1416 attr = ipattr(host);
1417 if(strcmp(attr, "ip") == 0)
1418 return ndbnew("ip", host);
1421 * give the domain name server the first opportunity to
1422 * resolve domain names. if that fails try the database.
1425 werrstr("can't translate address");
1426 if(strcmp(attr, "dom") == 0)
1427 t = dnsiplookup(host, &s);
1429 free(ndbgetvalue(db, &s, attr, host, "ip", &t));
1431 dnsname = ndbgetvalue(db, &s, attr, host, "dom", NULL);
1433 t = dnsiplookup(dnsname, &s);
1438 t = dnsiplookup(host, &s);
1443 * reorder the tuple to have the matched line first and
1444 * save that in the request structure.
1446 t = reorder(t, s.t);
1449 * reorder according to our interfaces
1452 for(ifc = ipifcs; ifc != NULL; ifc = ifc->next){
1453 for(lifc = ifc->lifc; lifc != NULL; lifc = lifc->next){
1454 maskip(lifc->ip, lifc->mask, net);
1455 for(nt = t; nt; nt = nt->entry){
1456 if(strcmp(nt->attr, "ip") != 0)
1458 parseip(ip, nt->val);
1459 maskip(ip, lifc->mask, tnet);
1460 if(memcmp(net, tnet, IPaddrlen) == 0){
1462 //unlock(&ipifclock);
1468 //unlock(&ipifclock);
1474 * translate an ip address
1477 iptrans(struct ndbtuple *t, Network *np, char *serv, char *rem, int hack)
1479 char ts[Maxservice];
1480 char reply[Maxreply];
1483 if(strcmp(t->attr, "ip") != 0)
1486 if(serv == 0 || ipserv(np, serv, ts, sizeof ts) == 0){
1487 werrstr("can't translate service");
1491 snprintf(x, sizeof(x), "!%s", rem);
1496 snprintf(reply, sizeof(reply), "%s/%s/clone %s%s",
1497 mntpt, np->net, ts, x);
1499 snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s",
1500 mntpt, np->net, t->val, ts, x, hack? "!fasttimeout": "");
1502 return strdup(reply);
1506 * lookup a telephone number
1509 telcolookup(Network *np, char *host, char *serv, int nolookup)
1514 werrstr("can't translate address");
1515 free(ndbgetvalue(db, &s, "sys", host, "telco", &t));
1517 return ndbnew("telco", host);
1519 return reorder(t, s.t);
1523 * translate a telephone address
1526 telcotrans(struct ndbtuple *t, Network *np, char *serv, char *rem, int unused)
1528 char reply[Maxreply];
1531 if(strcmp(t->attr, "telco") != 0)
1535 snprintf(x, sizeof(x), "!%s", rem);
1539 snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net,
1542 snprintf(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net,
1544 return strdup(reply);
1548 * reorder the tuple to put x's line first in the entry
1551 reorder(struct ndbtuple *t, struct ndbtuple *x)
1553 struct ndbtuple *nt;
1554 struct ndbtuple *line;
1556 /* find start of this entry's line */
1557 for(line = x; line->entry == line->line; line = line->line)
1561 return t; /* already the first line */
1563 /* remove this line and everything after it from the entry */
1564 for(nt = t; nt->entry != line; nt = nt->entry)
1568 /* make that the start of the entry */
1569 for(nt = line; nt->entry; nt = nt->entry)
1575 static struct ndbtuple*
1576 dnsip6lookup(char *mntpt, char *buf, struct ndbtuple *t)
1578 struct ndbtuple *t6, *tt;
1580 t6 = dnsquery(mntpt, buf, "ipv6"); /* lookup AAAA dns RRs */
1584 /* convert ipv6 attr to ip */
1585 for (tt = t6; tt != NULL; tt = tt->entry)
1586 if (strcmp(tt->attr, "ipv6") == 0)
1587 strncpy(tt->attr, "ip", sizeof tt->attr - 1);
1592 /* append t6 list to t list */
1593 for (tt = t; tt->entry != NULL; tt = tt->entry)
1600 * call the dns process and have it try to translate a name
1603 dnsiplookup(char *host, struct ndbs *s)
1611 snprintf(buf, sizeof(buf), "%s", host);
1613 if(strcmp(ipattr(buf), "ip") == 0)
1614 t = dnsquery(mntpt, buf, "ptr");
1616 t = dnsquery(mntpt, buf, "ip");
1617 /* special case: query ipv6 (AAAA dns RR) too */
1619 t = dnsip6lookup(mntpt, buf, t);
1624 snprintf(buf, sizeof buf, "%r");
1625 if(strstr(buf, "exist"))
1626 werrstr("can't translate address: %s", buf);
1627 else if(strstr(buf, "dns failure"))
1628 werrstr("temporary problem: %s", buf);
1636 qmatch(struct ndbtuple *t, char **attr, char **val, int n)
1639 struct ndbtuple *nt;
1641 for(i = 1; i < n; i++){
1643 for(nt = t; nt; nt = nt->entry)
1644 if(strcmp(attr[i], nt->attr) == 0)
1645 if(strcmp(val[i], "*") == 0
1646 || strcmp(val[i], nt->val) == 0){
1656 /* this is awful but I don't want to bring in libstring just for this.
1657 * you want real strings don't use C
1660 qreply(Mfile *mf, struct ndbtuple *t)
1662 struct ndbtuple *nt;
1670 for(nt = t; mf->nreply < Nreply && nt; nt = nt->entry){
1671 amt = snprintf(cur, len, "%s=%s", nt->attr, nt->val);
1680 if(nt->line != nt->entry){
1681 mf->replylen[mf->nreply] = strlen(s);
1682 mf->reply[mf->nreply++] = strdup(s);
1686 amt = snprintf(cur, len, " ");
1705 * generic query lookup. The query is of one of the following
1708 * attr1=val1 attr2=val2 attr3=val3 ...
1710 * returns the matching tuple
1712 * ipinfo attr=val attr1 attr2 attr3 ...
1714 * is like ipinfo and returns the attr{1-n}
1715 * associated with the ip address.
1718 genquery(Mfile *mf, char *query)
1722 char *attr[Maxattr];
1727 n = getfields(query, attr, ARRAY_SIZE(attr), 1, " ");
1731 if(strcmp(attr[0], "ipinfo") == 0)
1732 return ipinfoquery(mf, attr, n);
1735 for(i = 0; i < n; i++){
1736 p = strchr(attr[i], '=');
1743 /* give dns a chance */
1744 if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){
1745 t = dnsiplookup(val[0], &s);
1747 if(qmatch(t, attr, val, n)){
1756 /* first pair is always the key. It can't be a '*' */
1757 t = ndbsearch(db, &s, attr[0], val[0]);
1759 /* search is the and of all the pairs */
1761 if(qmatch(t, attr, val, n)){
1768 t = ndbsnext(&s, attr[0], val[0]);
1775 * resolve an ip address
1777 static struct ndbtuple*
1778 ipresolve(char *attr, char *host)
1780 struct ndbtuple *t, *nt, **l;
1782 t = iplookup(&network[Ntcp], host, "*", 0);
1783 for(l = &t; *l != NULL; ){
1785 if(strcmp(nt->attr, "ip") != 0){
1791 strcpy(nt->attr, attr);
1798 ipinfoquery(Mfile *mf, char **list, int n)
1801 int resolve[Maxattr];
1802 struct ndbtuple *t, *nt, **l;
1811 /* get search attribute=value, or assume ip=myipaddr */
1813 if((val = strchr(attr, '=')) != NULL){
1826 * don't let ndbipinfo resolve the addresses, we're
1830 for(i = 0; i < n; i++)
1831 if(*list[i] == '@'){ /* @attr=val ? */
1833 resolve[i] = 1; /* we'll resolve it */
1838 t = ndbipinfo(db, attr, val, list, n);
1843 for(l = &t; *l != NULL;){
1846 /* already an address? */
1847 if(strcmp(ipattr(nt->val), "ip") == 0){
1852 /* user wants it resolved? */
1853 for(i = 0; i < n; i++)
1854 if(strcmp(list[i], nt->attr) == 0)
1856 if(i >= n || resolve[i] == 0){
1861 /* resolve address and replace entry */
1862 *l = ipresolve(nt->attr, nt->val);
1872 /* make it all one line */
1873 for(nt = t; nt != NULL; nt = nt->entry){
1874 if(nt->entry == NULL)
1877 nt->line = nt->entry;
1890 x = calloc(size, 1);
1904 p = calloc(size, 1);
1907 memmove(p, s, size);