code: 9ferno

ref: c0a28bad03c29788d9fc18b5ab56c13a6222bf7d
dir: /appl/lib/ipattr.b/

View raw version
implement IPattr;

include "sys.m";

include "bufio.m";
include "attrdb.m";
	attrdb: Attrdb;
	Db, Dbentry, Tuples: import attrdb;

include "ip.m";
	ip: IP;
	IPaddr: import ip;

include "ipattr.m";

init(m: Attrdb, ipa: IP)
{
#	sys = load Sys Sys->PATH;
	attrdb = m;
	ip = ipa;
}

dbattr(s: string): string
{
	digit := 0;
	dot := 0;
	alpha := 0;
	hex := 0;
	colon := 0;
	for(i := 0; i < len s; i++){
		case c := s[i] {
		'0' to '9' =>
			digit = 1;
		'a' to 'f' or 'A' to 'F' =>
			hex = 1;
		'.' =>
			dot = 1;
		':' =>
			colon = 1;
		* =>
			if(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '-' || c == '&')
				alpha = 1;
		}
	}
	if(alpha){
		if(dot)
			return "dom";
		return "sys";
	}
	if(colon)
		return "ip";
	if(dot){
		if(!hex)
			return "ip";
		return "dom";
	}
	return "sys";
}

findnetattr(ndb: ref Db, attr: string, val: string, rattr: string): (string, string)
{
	(matches, err) := findnetattrs(ndb, attr, val, rattr::nil);
	if(matches == nil)
		return (nil, err);
	(nil, nattr) := hd matches;
	na := hd nattr;
#{sys := load Sys Sys->PATH; sys->print("%q=%q->%q ::", attr, val, rattr);for(al:=na.pairs; al != nil; al = tl al)sys->print(" %q=%q", (hd al).attr, (hd al).val); sys->print("\n");}
	if(na.name == rattr && na.pairs != nil)
		return ((hd na.pairs).val, nil);
	return (nil, nil);
}

reverse(l: list of string): list of string
{
	rl: list of string;
	for(; l != nil; l = tl l)
		rl = hd l :: rl;
	return rl;
}

valueof(l: list of ref Netattr, attr: string): list of string
{
	rl: list of string;
	for(; l != nil; l = tl l){
		na := hd l;
		if(na.name == attr){
			for(p := na.pairs; p != nil; p = tl p)
				rl = (hd p).val :: rl;
		}
	}
	return reverse(rl);
}

netvalueof(l: list of ref Netattr, attr: string, a: IP->IPaddr): list of string
{
	rl: list of string;
	for(; l != nil; l = tl l){
		na := hd l;
		if(na.name == attr && a.mask(na.mask).eq(na.net)){
			for(p := na.pairs; p != nil; p = tl p)
				rl = (hd p).val :: rl;
		}
	}
	return reverse(rl);
}

findnetattrs(ndb: ref Db, attr: string, val: string, rattrs: list of string): (list of (IPaddr, list of ref Netattr), string)
{
	rl: list of (IPaddr, list of ref Netattr);
	if(ndb == nil)
		return (nil, "no database");
	(e, ptr) := ndb.findbyattr(nil, attr, val, "ip");
	if(e == nil){
		if(attr != "ip")
			return (nil, "ip attribute not found");
		# look for attributes associated with networks that include `a'
		(ok, a) := IPaddr.parse(val);
		if(ok < 0)
			return (nil, "invalid ip address in db");
		netattrs := mkattrlist(rattrs);
		netattributes(ndb, a, netattrs);
		rl = (a, netattrs) :: nil;
	}else{
		netattrs: list of ref Netattr;
		for(matches := e.findbyattr(attr, val, "ip"); matches != nil; matches = tl matches){
			for((nil, allip) := hd matches; allip != nil; allip = tl allip){
				ipa := (hd allip).val;
				(ok, a) := IPaddr.parse(ipa);
				if(ok < 0)
					return (nil, "invalid ip address in db");
				netattrs = mkattrlist(rattrs);
				pptr := ptr;
				pe := e;
				for(;;){
					attribute(pe, a, ip->allbits, netattrs, 1);
					(pe, pptr) = ndb.findpair(pptr, attr, val);
					if(pe == nil)
						break;
				}
				netattributes(ndb, a, netattrs);
				rl = (a, netattrs) :: rl;
			}
		}
	}
	results: list of (IPaddr, list of ref Netattr);
	for(; rl != nil; rl = tl rl)
		results = hd rl :: results;
	return (results, nil);
}

netattributes(ndb: ref Db, a: IPaddr, nas: list of ref Netattr): string
{
	e: ref Dbentry;
	ptr: ref Attrdb->Dbptr;
	for(;;){
		(e, ptr) = ndb.find(ptr, "ipnet");
		if(e == nil)
			break;
		ipaddr := e.findfirst("ip");
		if(ipaddr == nil)
			continue;
		(ok, netip) := IPaddr.parse(ipaddr);
		if(ok < 0)
			return "bad ip address in db";
		netmask: IPaddr;
		mask := e.findfirst("ipmask");
		if(mask == nil){
			if(!netip.isv4())
				continue;
			netmask = netip.classmask();
		}else{
			(ok, netmask) = IPaddr.parsemask(mask);
			if(ok < 0)
				return "bad ipmask in db";
		}
		if(a.mask(netmask).eq(netip))
			attribute(e, netip, netmask, nas, 0);
	}
	return nil;
}

attribute(e: ref Dbentry, netip: IPaddr, netmask: IPaddr, nas: list of ref Netattr, ishost: int)
{
	for(; nas != nil; nas = tl nas){
		na := hd nas;
		if(na.pairs != nil){
			if(!na.mask.mask(netmask).eq(na.mask))
				continue;
			# new one is at least as specific
		}
		matches := e.find(na.name);
		if(matches == nil){
			if(na.name != "ipmask" || ishost)
				continue;
			matches = (nil, ref Attrdb->Attr("ipmask", netmask.masktext(), 0)::nil) :: nil;
		}
		na.net = netip;
		na.mask = netmask;
		rl: list of ref Attrdb->Attr;
		for(; matches != nil; matches = tl matches){
			(nil, al) := hd matches;
			for(; al != nil; al = tl al)
				rl = hd al :: rl;
		}
		na.pairs = nil;
		for(; rl != nil; rl = tl rl)
			na.pairs = hd rl :: na.pairs;
	}
}

mkattrlist(rattrs: list of string): list of ref Netattr
{
	netattrs: list of ref Netattr;
	for(; rattrs != nil; rattrs = tl rattrs)
		netattrs = ref Netattr(hd rattrs, nil, ip->noaddr, ip->noaddr) :: netattrs;
	return netattrs;
}