forsyth20121205-174063a80cc13 years agoHistory
661 lines · 15.2 KB · Brainfuck
RawDownload
implement Bootpd; ## to do:#	DHCP# include "sys.m";	sys: Sys; include "draw.m"; include "bufio.m";	bufio: Bufio;	Iobuf: import bufio; include "attrdb.m";	attrdb: Attrdb;	Attr, Db, Dbentry, Tuples: import attrdb; include "ip.m";	ip: IP;	IPaddr, Udphdr: import ip; include "ipattr.m";	ipattr: IPattr; include "ether.m";	ether: Ether; include "arg.m"; Bootpd: module{	init: fn(nil: ref Draw->Context, argv: list of string);}; stderr: ref Sys->FD;debug: int;sniff: int;verbose: int; siaddr: IPaddr;netmask: IPaddr;myname: string;progname := "bootpd";net := "/net";ndb: ref Db;ndbfile := "/lib/ndb/local";mtime := 0;testing := 0; Udphdrsize: con IP->Udphdrlen; init(nil: ref Draw->Context, args: list of string){	sys = load Sys Sys->PATH;	stderr = sys->fildes(2);	bufio = load Bufio Bufio->PATH;	if(bufio == nil)		loadfail(Bufio->PATH);	attrdb = load Attrdb Attrdb->PATH;	if(attrdb == nil)		loadfail(Attrdb->PATH);	attrdb->init();	ip = load IP IP->PATH;	if(ip == nil)		loadfail(IP->PATH);	ip->init();	ipattr = load IPattr IPattr->PATH;	if(ipattr == nil)		loadfail(IPattr->PATH);	ipattr->init(attrdb, ip);	ether = load Ether Ether->PATH;	if(ether == nil)		loadfail(Ether->PATH);	ether->init(); 	verbose = 1;	sniff = 0;	debug = 0;	arg := load Arg Arg->PATH;	if(arg == nil)		raise "fail: load Arg";	arg->init(args);	arg->setusage("bootpd [-dsqv] [-f file] [-x network]");	progname = arg->progname();	while((o := arg->opt()) != 0)		case o {		'd' =>	debug++;		's' =>		sniff = 1; debug = 255;		'q' =>	verbose = 0;		'v' =>	verbose = 1;		'x' =>	net = arg->earg();		'f' =>		ndbfile = arg->earg();		't' =>		testing = 1; debug = 1; verbose = 1;		* =>		arg->usage();		}	args = arg->argv();	if(args != nil)		arg->usage();	arg = nil; 	sys->pctl(Sys->FORKFD|Sys->FORKNS, nil); 	if(!sniff && (err := dbread()) != nil)		error(err); 	myname = sysname();	if(myname == nil)		error("system name not set");	(siaddr, err) = csquery(myname);	if(err != nil)		error(sys->sprint("can't find IP address for %s: %s", myname, err));	if(debug)		sys->fprint(stderr, "bootpd: local IP address is %s\n", siaddr.text()); 	addr := net+"/udp!*!67";	if(testing)		addr = net+"/udp!*!499";	if(debug)		sys->fprint(stderr, "bootpd: announcing %s\n", addr);	(ok, c) := sys->announce(addr);	if(ok < 0)		error(sys->sprint("can't announce %s: %r", addr));	if(sys->fprint(c.cfd, "headers") < 0)		error(sys->sprint("can't set headers mode: %r")); 	if(debug)		sys->fprint(stderr, "bootpd: opening %s/data\n", c.dir);	c.dfd = sys->open(c.dir+"/data", sys->ORDWR);	if(c.dfd == nil)		error(sys->sprint("can't open %s/data: %r", c.dir)); 	spawn server(c);} loadfail(s: string){	error(sys->sprint("can't load %s: %r", s));} error(s: string){	sys->fprint(stderr, "bootpd: %s\n", s);	raise "fail:error";} server(c: Sys->Connection){	buf := array[2048] of byte;	badread := 0;	for(;;) {		n := sys->read(c.dfd, buf, len buf);		if(n <0) {			if (badread++ > 10)				break;			continue;		}		badread = 0;		if(n < Udphdrsize) {			if(debug)				sys->fprint(stderr, "bootpd: short Udphdr: %d bytes\n", n);			continue;		}		hdr := Udphdr.unpack(buf, Udphdrsize);		if(debug)			sys->fprint(stderr, "bootpd: received request from udp!%s!%d\n", hdr.raddr.text(), hdr.rport);		if(n < Udphdrsize+300) {			if(debug)				sys->fprint(stderr, "bootpd: short request of %d bytes\n", n - Udphdrsize);			continue;		} 		(bootp, err) := Bootp.unpack(buf[Udphdrsize:]);		if(err != nil) {			if(debug)				sys->fprint(stderr, "bootpd: can't unpack packet: %s\n", err);			continue;		}		if(debug >= 2)			sys->fprint(stderr, "bootpd: recvd {%s}\n", bootp.text());		if(sniff)			continue;		if(bootp.htype != 1 || bootp.hlen != 6) {			# if it isn't ether, we don't do it			if(debug)				sys->fprint(stderr, "bootpd: hardware type not ether; ignoring.\n");			continue;		}		if((err = dbread()) != nil) {			sys->fprint(stderr,  "bootpd: getreply: dbread failed: %s\n", err);			continue;		}		rec := lookup(bootp);		if(rec == nil) {			# we can't answer this request			if(debug)				sys->fprint(stderr, "bootpd: cannot answer request.\n");			continue;		}		if(debug)			sys->fprint(stderr, "bootpd: found a matching entry: {%s}\n", rec.text());		mkreply(bootp, rec);		if(verbose)			sys->print("bootpd: %s -> %s %s\n", ether->text(rec.ha), rec.hostname, rec.ip.text());		if(debug)			sys->fprint(stderr, "bootpd: reply {%s}\n", bootp.text());		repl := bootp.pack();		if(!testing)			arpenter(rec.ip.text(), ether->text(rec.ha));		send(hdr, repl);	}	sys->fprint(stderr, "bootpd: %d read errors: %r\n", badread);} arpenter(ip, ha: string){	if(debug)		sys->fprint(stderr, "bootpd: arp: %s -> %s\n", ip, ha);	fd := sys->open(net+"/arp", Sys->OWRITE);	if(fd == nil) {		if(debug)			sys->fprint(stderr, "bootpd: arp open failed: %r\n");		return;	}	if(sys->fprint(fd, "add %s %s", ip, ha) < 0){		if(debug)			sys->fprint(stderr, "bootpd: error writing arp: %r\n");	}} sysname(): string{	t := rf("/dev/sysname");	if(t != nil)		return t;	return rf("#e/sysname");} rf(name: string): string{	fd := sys->open(name, Sys->OREAD);	buf := array[Sys->NAMEMAX] of byte;	n := sys->read(fd, buf, len buf);	if(n <= 0)		return nil;	return string buf[0:n];} csquery(name: string): (IPaddr, string){	siaddr = ip->noaddr;	# get a local IP address by translating our sysname with cs(8)	csfile := net+"/cs";	fd := sys->open(net+"/cs", Sys->ORDWR);	if(fd == nil)		return (ip->noaddr, sys->sprint("can't open %s/cs: %r", csfile));	if(sys->fprint(fd, "net!%s!0", name) < 0)		return (ip->noaddr, sys->sprint("can't translate net!%s!0: %r", name));	sys->seek(fd, big 0, 0);	a := array[1024] of byte;	n := sys->read(fd, a, len a);	if(n <= 0)		return (ip->noaddr, "no result from "+csfile);	reply := string a[0:n];	(l, addr):= sys->tokenize(reply, " ");	if(l != 2)		return (ip->noaddr, "bad cs reply format");	(l, addr) = sys->tokenize(hd tl addr, "!");	if(l < 2)		return (ip->noaddr, "bad cs reply format");	(ok, ipa) := IPaddr.parse(hd addr);	if(ok < 0 || !ipok(siaddr))		return (ip->noaddr, "can't parse address: "+hd addr);	return (ipa, nil);} Hostinfo: adt {	hostname: string; 	ha: array of byte;	# hardware addr	ip: IPaddr;		# client IP addr	bootf: string;		# boot file path	netmask: IPaddr;	# subnet mask	ipgw: IPaddr;	# gateway IP addr	fs: IPaddr;		# file server IP addr	auth: IPaddr;	# authentication server IP addr 	text:	fn(inf: self ref Hostinfo): string;}; send(hdr: ref Udphdr, msg: array of byte){	replyaddr := net+"/udp!255.255.255.255!68";	# TO DO: gateway	if(testing)		replyaddr = sys->sprint("udp!%s!%d", hdr.raddr.text(), hdr.rport);	lport := "67";	if(testing)		lport = "499";	(n, c) := sys->dial(replyaddr, lport);	if(n < 0) {		sys->fprint(stderr, "bootpd: can't dial %s for reply: %r\n", replyaddr);		return;	}	n = sys->write(c.dfd, msg, len msg);	if(n != len msg)		sys->fprint(stderr, "bootpd: udp write error: %r\n");} mkreply(bootp: ref Bootp, rec: ref Hostinfo){	bootp.op = 2; # boot reply	bootp.yiaddr = rec.ip;	bootp.siaddr = siaddr;	bootp.giaddr = ip->noaddr;	bootp.sname = myname;	bootp.file = string rec.bootf;	bootp.vend = array of byte sys->sprint("p9  %s %s %s %s", rec.netmask.text(), rec.fs.text(), rec.auth.text(), rec.ipgw.text());} dbread(): string{	if(ndb == nil){		ndb = Db.open(ndbfile);		if(ndb == nil)			return sys->sprint("cannot open %s: %r", ndbfile);	}else if(ndb.changed())		ndb.reopen();	return nil;} ipok(a: IPaddr): int{	return a.isv4() && !(a.eq(ip->v4noaddr) || a.eq(ip->noaddr) || a.ismulticast());} lookup(bootp: ref Bootp): ref Hostinfo{	if(ndb == nil)		return nil;	inf: ref Hostinfo;	hwaddr := ether->text(bootp.chaddr);	if(ipok(bootp.ciaddr)){		# client thinks it knows address; check match with MAC address		ipaddr := bootp.ciaddr.text();		ptr: ref Attrdb->Dbptr;		for(;;){			e: ref Dbentry;			(e, ptr) = ndb.findbyattr(ptr, "ip", ipaddr, "ether");			if(e == nil)				break;			# TO DO: check result			inf = matchandfill(e, "ip", ipaddr, "ether", hwaddr);			if(inf != nil)				return inf;		}	}	# look up an ip address associated with given MAC address	ptr: ref Attrdb->Dbptr;	for(;;){		e: ref Dbentry;		(e, ptr) = ndb.findbyattr(ptr, "ether", hwaddr, "ip");		if(e == nil)			break;		# TO DO: check right net etc.		inf = matchandfill(e, "ether", hwaddr, "ip", nil);		if(inf != nil)			return inf;	}	return nil;} matchandfill(e: ref Dbentry, attr: string, val: string, rattr: string, rval: string): ref Hostinfo{	matches := e.findbyattr(attr, val, rattr);	for(; matches != nil; matches = tl matches){		(line, attrs) := hd matches;		for(; attrs != nil; attrs = tl attrs){			if(rval == nil || (hd attrs).val == rval){				inf := fillup(line, e);				if(inf != nil)					return inf;				break;			}		}	}	return nil;} fillup(line: ref Tuples, e: ref Dbentry): ref Hostinfo{	ok: int;	inf := ref Hostinfo;	inf.netmask = ip->noaddr;	inf.ipgw = ip->noaddr;	inf.fs = ip->v4noaddr;	inf.auth = ip->v4noaddr;	inf.hostname = find(line, e, "sys");	s := find(line, e, "ether");	if(s != nil)		inf.ha = ether->parse(s);	s = find(line, e, "ip");	if(s == nil)		return nil;	(ok, inf.ip) = IPaddr.parse(s);	if(ok < 0)		return nil;	(results, err) := ipattr->findnetattrs(ndb, "ip", s, list of{"ipmask", "ipgw", "fs", "FILESERVER", "SIGNER", "auth", "bootf"});	if(err != nil)		return nil;	for(; results != nil; results = tl results){		(a, nattrs) := hd results;		if(!a.eq(inf.ip))			continue;	# different network		for(; nattrs != nil; nattrs = tl nattrs){			na := hd nattrs;			case na.name {			"ipmask" =>				inf.netmask = takeipmask(na.pairs, inf.netmask);			"ipgw" =>				inf.ipgw = takeipattr(na.pairs, inf.ipgw);			"fs" or "FILESERVER" =>				inf.fs = takeipattr(na.pairs, inf.fs);			"auth" or "SIGNER" =>				inf.auth = takeipattr(na.pairs, inf.auth);			"bootf" =>				inf.bootf = takeattr(na.pairs, inf.bootf);			}		}	}	return inf;} takeattr(pairs: list of ref Attr, s: string): string{	if(s != nil || pairs == nil)		return s;	return (hd pairs).val;} takeipattr(pairs: list of ref Attr, a: IPaddr): IPaddr{	if(pairs == nil || !(a.eq(ip->noaddr) || a.eq(ip->v4noaddr)))		return a;	(ok, na) := parseip((hd pairs).val);	if(ok < 0)		return a;	return na;} takeipmask(pairs: list of ref Attr, a: IPaddr): IPaddr{	if(pairs == nil || !(a.eq(ip->noaddr) || a.eq(ip->v4noaddr)))		return a;	(ok, na) := IPaddr.parsemask((hd pairs).val);	if(ok < 0)		return a;	return na;} findip(line: ref Tuples, e: ref Dbentry, attr: string): (int, IPaddr){	s := find(line, e, attr);	if(s == nil)		return (-1, ip->noaddr);	return parseip(s);} parseip(s: string): (int, IPaddr){	(ok, a) := IPaddr.parse(s);	if(ok < 0){		# look it up if it's a system name		s = findbyattr("sys", s, "ip");		(ok, a) = IPaddr.parse(s);	}	return (ok, a);} find(line: ref Tuples, e: ref Dbentry, attr: string): string{	if(line != nil){		a := line.find(attr);		if(a != nil)			return (hd a).val;	}	if(e != nil){		for(matches := e.find(attr); matches != nil; matches = tl matches){			(nil, a) := hd matches;			if(a != nil)				return (hd a).val;		}	}	return nil;} findbyattr(attr: string, val: string, rattr: string): string{	ptr: ref Attrdb->Dbptr;

Lines 1–500 of 661.