/* * look through a containing subset */ static Ndbtuple* subnet(Ndb *db, uint8_t *net, Ndbtuple *f, int prefix) { Ndbs s; Ndbtuple *t, *nt, *xt; char netstr[128]; uint8_t mask[IPaddrlen]; int masklen; t = nil; sprint(netstr, "%I", net); nt = ndbsearch(db, &s, "ip", netstr); while(nt != nil){ xt = ndbfindattr(nt, nt, "ipnet"); if(xt){ xt = ndbfindattr(nt, nt, "ipmask"); if(xt) parseipmask(mask, xt->val); else ipmove(mask, defmask(net)); masklen = prefixlen(mask); if(masklen <= prefix){ t = ndbconcatenate(t, filter(db, nt, f)); nt = nil; } } ndbfree(nt); nt = ndbsnext(&s, "ip", netstr); } ndbsetmalloctag(t, getcallerpc(&db)); return t; }
/* * return only the attr/value pairs in t maching the filter, f. * others are freed. line structure is preserved. */ static Ndbtuple* filter(Ndb *db, Ndbtuple *t, Ndbtuple *f) { Ndbtuple *nt, *nf, *next; /* filter out what we don't want */ for(nt = t; nt; nt = next){ next = nt->entry; /* look through filter */ for(nf = f; nf != nil; nf = nf->entry){ if(!(nf->ptr&Fignore) && strcmp(nt->attr, nf->attr) == 0) break; } if(nf == nil){ /* remove nt from t */ t = ndbdiscard(t, nt); } else { if(nf->ptr & Faddr) t = ndbsubstitute(t, nt, setattr(ndbgetipaddr(db, nt->val), nt->attr)); nf->ptr |= Ffound; } } /* remember filter etnries that matched */ for(nf = f; nf != nil; nf = nf->entry) if(nf->ptr & Ffound) nf->ptr = (nf->ptr & ~Ffound) | Fignore; ndbsetmalloctag(t, getcallerpc(&db)); return t; }
/* * parse the next entry in the file */ struct ndbtuple* ndbparse(struct ndb *db) { char *line; struct ndbtuple *t; struct ndbtuple *first, *last; int len; first = last = 0; for(;;){ if((line = fgets(db->buf, sizeof(db->buf),db->b)) == 0) break; len = strlen(db->buf); if(line[len-1] != '\n') break; if(first && !isspace(*line) && *line != '#'){ fseek(db->b, -len, 1); break; } t = _ndbparseline(line); if(t == 0) continue; if(first) last->entry = t; else first = t; last = t; while(last->entry) last = last->entry; } ndbsetmalloctag(first, getcallerpc(&db)); return first; }
/* * parse all tuples in a line. we assume that the * line ends in a '\n'. * * the tuples are linked as a list using ->entry and * as a ring using ->line. */ Ndbtuple* _ndbparseline(char *cp) { Ndbtuple *t; Ndbtuple *first, *last; first = last = 0; while(*cp != '#' && *cp != '\n'){ t = 0; cp = _ndbparsetuple(cp, &t); if(cp == 0) break; if(first){ last->line = t; last->entry = t; } else first = t; last = t; t->line = 0; t->entry = 0; setmalloctag(t, getcallerpc()); } if(first) last->line = first; ndbsetmalloctag(first, getcallerpc()); return first; }
/* * search for a tuple that has the given 'attr=val' and also 'rattr=x'. * copy 'x' into 'buf' and return the whole tuple. * * return 0 if not found. */ Ndbtuple* dnsquery(char *net, char *val, char *type) { char rip[128]; char *p; Ndbtuple *t; int fd; /* if the address is V4 or V6 null address, give up early */ if(strcmp(val, "::") == 0 || strcmp(val, "0.0.0.0") == 0) return nil; if(net == nil) net = "/net"; snprint(rip, sizeof(rip), "%s/dns", net); fd = open(rip, ORDWR); if(fd < 0) { if(strcmp(net, "/net") == 0) snprint(rip, sizeof(rip), "/srv/dns"); else { snprint(rip, sizeof(rip), "/srv/dns%s", net); p = strrchr(rip, '/'); *p = '_'; } fd = open(rip, ORDWR); if(fd < 0) return nil; if(mount(fd, -1, net, MBEFORE, "") < 0) { close(fd); return nil; } /* fd is now closed */ snprint(rip, sizeof(rip), "%s/dns", net); fd = open(rip, ORDWR); if(fd < 0) return nil; } /* zero out the error string */ werrstr(""); /* if this is a reverse lookup, first lookup the domain name */ if(strcmp(type, "ptr") == 0) { mkptrname(val, rip, sizeof rip); t = doquery(fd, rip, "ptr"); } else t = doquery(fd, val, type); /* * TODO: make fd static and keep it open to reduce 9P traffic * walking to /net*^/dns. */ close(fd); ndbsetmalloctag(t, getcallerpc(&net)); return t; }
/* return list of ip addresses for a name */ Ndbtuple* ndbgetipaddr(Ndb *db, char *val) { char *attr, *p; Ndbtuple *it, *first, *last, *next; Ndbs s; /* already an IP address? */ attr = ipattr(val); if(strcmp(attr, "ip") == 0){ it = ndbnew("ip", val); ndbsetmalloctag(it, getcallerpc(&db)); return it; } /* look it up */ p = ndbgetvalue(db, &s, attr, val, "ip", &it); if(p == nil) return nil; free(p); /* remove the non-ip entries */ first = last = nil; for(; it; it = next){ next = it->entry; if(strcmp(it->attr, "ip") == 0){ if(first == nil) first = it; else last->entry = it; it->entry = nil; it->line = first; last = it; } else { it->entry = nil; ndbfree(it); } } ndbsetmalloctag(first, getcallerpc(&db)); return first; }
static Ndbtuple* doquery(int fd, char *dn, char *type) { char buf[1024]; int n; Ndbtuple *t, *first, *last; seek(fd, 0, 0); snprint(buf, sizeof(buf), "!%s %s", dn, type); if(write(fd, buf, strlen(buf)) < 0) return nil; seek(fd, 0, 0); first = last = nil; for(;;) { n = read(fd, buf, sizeof(buf)-2); if(n <= 0) break; if(buf[n-1] != '\n') buf[n++] = '\n'; /* ndbparsline needs a trailing new line */ buf[n] = 0; /* check for the error condition */ if(buf[0] == '!') { werrstr("%s", buf+1); return nil; } t = _ndbparseline(buf); if(t != nil) { if(first) last->entry = t; else first = t; last = t; while(last->entry) last = last->entry; } } ndbsetmalloctag(first, getcallerpc(&fd)); return first; }
/* * allocate a tuple */ struct ndbtuple* ndbnew(char *attr, char *val) { struct ndbtuple *t; t = calloc(1, sizeof(*t)); if(t == NULL){ fprintf(stderr, "ndbnew %r"); exit(1); } if(attr != NULL) strncpy(t->attr, attr, sizeof(t->attr)-1); t->val = t->valbuf; if(val != NULL) ndbsetval(t, val, strlen(val)); ndbsetmalloctag(t, getcallerpc(&attr)); return t; }
struct ndbtuple* ndbgetval(struct ndb *db, struct ndbs *s, char *attr, char *val, char *rattr, char *buf) { struct ndbtuple *t; char *p; p = ndbgetvalue(db, s, attr, val, rattr, &t); if(p == NULL){ if(buf != NULL) *buf = 0; } else { if(buf != NULL){ strncpy(buf, p, Ndbvlen-1); buf[Ndbvlen-1] = 0; } free(p); } ndbsetmalloctag(t, getcallerpc(&db)); return t; }
static Ndbtuple* ndbcopy(Ndb *db, Ndbtuple *from_t, Ndbs *from_s, Ndbs *to_s) { Ndbtuple *first, *to_t, *last, *line; int newline; *to_s = *from_s; to_s->t = nil; to_s->db = db; newline = 1; last = nil; first = nil; line = nil; for(; from_t != nil; from_t = from_t->entry){ to_t = ndbnew(from_t->attr, from_t->val); /* have s point to matching tuple */ if(from_s->t == from_t) to_s->t = to_t; if(newline) line = to_t; else last->line = to_t; if(last != nil) last->entry = to_t; else { first = to_t; line = to_t; } to_t->entry = nil; to_t->line = line; last = to_t; newline = from_t->line != from_t->entry; } ndbsetmalloctag(first, getcallerpc(&db)); return first; }
/* make a filter to be used in filter */ static Ndbtuple* mkfilter(int argc, char **argv) { Ndbtuple *t, *first, *last; char *p; last = first = nil; while(argc-- > 0){ t = ndbnew(0, 0); if(first) last->entry = t; else first = t; last = t; p = *argv++; if(*p == '@'){ /* @attr=val ? */ t->ptr |= Faddr; /* return resolved address(es) */ p++; } strncpy(t->attr, p, sizeof(t->attr)-1); } ndbsetmalloctag(first, getcallerpc(&argc)); return first; }
/* remove a from t and free it */ struct ndbtuple* ndbdiscard(struct ndbtuple *t, struct ndbtuple *a) { struct ndbtuple *nt; /* unchain a */ for(nt = t; nt != NULL; nt = nt->entry){ if(nt->line == a) nt->line = a->line; if(nt->entry == a) nt->entry = a->entry; } /* a may be start of chain */ if(t == a) t = a->entry; /* free a */ a->entry = NULL; ndbfree(a); ndbsetmalloctag(t, getcallerpc(&t)); return t; }
Ndbtuple* _ndbcacheadd(Ndb *db, Ndbs *s, char *attr, char *val, Ndbtuple *t) { Ndbcache *c, **l; c = mallocz(sizeof *c, 1); if(c == nil) return nil; c->attr = strdup(attr); if(c->attr == nil) goto err; c->val = strdup(val); if(c->val == nil) goto err; c->t = ndbcopy(db, t, s, &c->s); if(c->t == nil && t != nil) goto err; /* add to front */ c->next = db->cache; db->cache = c; /* trim list */ if(db->ncache < Maxcached){ db->ncache++; return t; } for(l = &db->cache; (*l)->next; l = &(*l)->next) ; c = *l; *l = nil; err: ndbcachefree(c); ndbsetmalloctag(t, getcallerpc(&db)); return t; }
/* * fill in all the requested attributes for a system. * if the system's entry doesn't have all required, * walk through successively more inclusive networks * for inherited attributes. */ Ndbtuple* ndbipinfo(Ndb *db, char *attr, char *val, char **alist, int n) { Ndbtuple *t, *nt, *f; Ndbs s; char *ipstr; uint8_t net[IPaddrlen], ip[IPaddrlen]; int prefix, smallestprefix, force; int64_t r; /* just in case */ fmtinstall('I', eipfmt); fmtinstall('M', eipfmt); /* get needed attributes */ f = mkfilter(n, alist); /* * first look for a matching entry with an ip address */ t = nil; ipstr = ndbgetvalue(db, &s, attr, val, "ip", &nt); if(ipstr == nil){ /* none found, make one up */ if(strcmp(attr, "ip") != 0) { ndbfree(f); return nil; } t = ndbnew("ip", val); t->line = t; t->entry = nil; r = parseip(net, val); if(r == -1) ndbfree(t); } else { /* found one */ while(nt != nil){ nt = ndbreorder(nt, s.t); t = ndbconcatenate(t, nt); nt = ndbsnext(&s, attr, val); } r = parseip(net, ipstr); free(ipstr); } if(r < 0){ ndbfree(f); return nil; } ipmove(ip, net); t = filter(db, t, f); /* * now go through subnets to fill in any missing attributes */ if(isv4(net)){ prefix = 127; smallestprefix = 100; force = 0; } else { /* in v6, the last 8 bytes have no structure (we hope) */ prefix = 64; smallestprefix = 2; memset(net+8, 0, 8); force = 1; } /* * to find a containing network, keep turning off * the lower bit and look for a network with * that address and a shorter mask. tedius but * complete, we may need to find a trick to speed this up. */ for(; prefix >= smallestprefix; prefix--){ if(filtercomplete(f)) break; if(!force && (net[prefix/8] & (1<<(7-(prefix%8)))) == 0) continue; force = 0; net[prefix/8] &= ~(1<<(7-(prefix%8))); t = ndbconcatenate(t, subnet(db, net, f, prefix)); } /* * if there's an unfulfilled ipmask, make one up */ nt = ndbfindattr(f, f, "ipmask"); if(nt && !(nt->ptr & Fignore)){ char x[64]; snprint(x, sizeof(x), "%M", defmask(ip)); t = ndbconcatenate(t, ndbnew("ipmask", x)); } ndbfree(f); ndbsetmalloctag(t, getcallerpc(&db)); return t; }
/* * look up the ip attributes 'list' for an entry that has the * given 'attr=val' and a 'ip=' tuples. * * return NULL if not found. */ struct ndbtuple* csipinfo(char *netroot, char *attr, char *val, char **list, int n) { struct ndbtuple *t, *first, *last; int i; char line[1024]; int fd; char *p, *e; int left,amt; if(netroot) snprintf(line, sizeof(line), "%s/cs", netroot); else strcpy(line, "/net/cs"); fd = open(line, O_RDWR); if(fd < 0) return 0; lseek(fd, 0, 0); left = sizeof(line); amt = snprintf(line, left, "!ipinfo %s=%s", attr, val); if(amt < 0) return 0; left -= amt; for(i = 0; i < n; i++){ if(*list == NULL) break; amt = snprintf(p, left, " %s", *list++); if(amt < 0) return 0; left -= amt; } if(write(fd, line, strlen(line)) < 0){ close(fd); return 0; } lseek(fd, 0, 0); first = last = 0; for(;;){ n = read(fd, line, sizeof(line)-2); if(n <= 0) break; line[n] = '\n'; line[n+1] = 0; t = _ndbparseline(line); if(t == 0) continue; if(first) last->entry = t; else first = t; last = t; while(last->entry) last = last->entry; } close(fd); ndbsetmalloctag(first, getcallerpc(&netroot)); return first; }