1029 lines · 23.7 KB · Brainfuck
RawDownload
implement Dhcpclient; ## DHCP and BOOTP clients# Copyright © 2004-2006 Vita Nuova Holdings Limited# include "sys.m";	sys: Sys; include "ip.m";	ip: IP;	IPv4off, IPaddrlen, Udphdrlen, Udpraddr, Udpladdr, Udprport, Udplport: import IP;	IPaddr: import ip;	get2, get4, put2, put4: import ip; include "keyring.m";include "security.m";	# for Random include "dhcp.m"; debug := 0; xidgen: int; init(){	sys = load Sys Sys->PATH;	random := load Random Random->PATH;	if(random != nil)		xidgen = random->randomint(Random->NotQuiteRandom);	else		xidgen = sys->pctl(0, nil)*sys->millisec();	random = nil;	ip = load IP IP->PATH;	ip->init();} tracing(d: int){	debug = d;} Bootconf.new(): ref Bootconf{	bc := ref Bootconf;	bc.lease = 0;	bc.options = array[256] of array of byte;	return bc;} Bootconf.get(c: self ref Bootconf, n: int): array of byte{	a := c.options;	if(n & Ovendor){		a = c.vendor;		n &= ~Ovendor;	}	if(n < 0 || n >= len a)		return nil;	return a[n];} Bootconf.getint(c: self ref Bootconf, n: int): int{	a := c.get(n);	v := 0;	for(i := 0; i < len a; i++)		v = (v<<8) | int a[i];	return v;} Bootconf.getip(c: self ref Bootconf, n: int): string{	l := c.getips(n);	if(l == nil)		return nil;	return hd l;} Bootconf.getips(c: self ref Bootconf, n: int): list of string{	a := c.get(n);	rl: list of string;	while(len a >= 4){		rl = v4text(a) :: rl;		a = a[4:];	}	l: list of string;	for(; rl != nil; rl = tl rl)		l = hd rl :: l;	return l;} Bootconf.gets(c: self ref Bootconf, n: int): string{	a := c.get(n);	if(a == nil)		return nil;	for(i:=0; i<len a; i++)		if(a[i] == byte 0)			break;	return string a[0:i];} Bootconf.put(c: self ref Bootconf, n: int, a: array of byte){	if(n < 0 || n >= len c.options)		return;	ca := array[len a] of byte;	ca[0:] = a;	c.options[n] = ca;} Bootconf.putint(c: self ref Bootconf, n: int, v: int){	if(n < 0 || n >= len c.options)		return;	a := array[4] of byte;	put4(a, 0, v);	c.options[n] = a;} Bootconf.putips(c: self ref Bootconf, n: int, ips: list of string){	if(n < 0 || n >= len c.options)		return;	na := len ips;	a := array[na*4] of byte;	na = 0;	for(; ips != nil; ips = tl ips){		(nil, ipa) := IPaddr.parse(hd ips);		a[na++:] = ipa.v4();	}	c.options[n] = a;} Bootconf.puts(c: self ref Bootconf, n: int, s: string){	if(n < 0 || n >= len c.options)		return;	c.options[n] = array of byte s;} ### DHCP## # BOOTP operationsBootprequest, Bootpreply: con 1+iota; # DHCP operationsNotDHCP, Discover, Offer, Request, Decline, Ack, Nak, Release, Inform: con iota; Dhcp: adt {	udphdr:	array of byte;	op:		int;	htype:	int;	hops:	int;	xid:		int;	secs:		int;	flags:	int;	ciaddr:	IPaddr;	yiaddr:	IPaddr;	siaddr:	IPaddr;	giaddr:	IPaddr;	chaddr:	array of byte;	sname:	string;	file:		string;	options:	list of (int, array of byte);	dhcpop:	int;}; opnames := array[] of {	Discover => "Discover",	Offer => "Offer",	Request => "Request",	Decline => "Decline",	Ack => "Ack",	Nak => "Nak",	Release => "Release",	Inform => "Inform"}; opname(op: int): string{	if(op >= 0 && op < len opnames)		return opnames[op];	return sys->sprint("OP%d", op);} stringget(buf: array of byte): string{	for(x := 0; x < len buf; x++)		if(buf[x] == byte 0)			break;	if(x == 0)		return nil;	return string buf[0 : x];} eqbytes(b1: array of byte, b2: array of byte): int{	l := len b1;	if(l != len b2)		return 0;	for(i := 0; i < l; i++)		if(b1[i] != b2[i])			return 0;	return 1;} magic := array[] of {byte 99, byte 130, byte 83, byte 99};	# RFC2132 (replacing RFC1048) dhcpsend(fd: ref Sys->FD, xid: int, dhcp: ref Dhcp){	dhcp.xid = xid;	abuf := array[576+Udphdrlen] of {* => byte 0};	abuf[0:] = dhcp.udphdr;	buf := abuf[Udphdrlen:];	buf[0] = byte dhcp.op;	buf[1] = byte dhcp.htype;	buf[2] = byte len dhcp.chaddr;	buf[3] = byte dhcp.hops;	put4(buf, 4, xid);	put2(buf, 8, dhcp.secs);	put2(buf, 10, dhcp.flags);	buf[12:] = dhcp.ciaddr.v4();	buf[16:] = dhcp.yiaddr.v4();	buf[20:] = dhcp.siaddr.v4();	buf[24:] = dhcp.giaddr.v4();	buf[28:] = dhcp.chaddr;	buf[44:] = array of byte dhcp.sname;	# [64]	buf[108:] = array of byte dhcp.file;	# [128]	o := 236;	# RFC1542 suggests including magic and Oend as a minimum, even in BOOTP	buf[o:] = magic;	o += 4;	if(dhcp.dhcpop != NotDHCP){		buf[o++] = byte Otype;		buf[o++] = byte 1;		buf[o++] = byte dhcp.dhcpop;	}	for(ol := dhcp.options; ol != nil; ol = tl ol){		(opt, val) := hd ol;		buf[o++] = byte opt;		buf[o++] = byte len val;		if(len val > 0){			buf[o:] = val;			o += len val;		}	}	buf[o++] = byte Oend;	if(debug)		dumpdhcp(dhcp, "->");	sys->write(fd, abuf, len abuf);} kill(pid: int, grp: string){	fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);	if(fd != nil)		sys->fprint(fd, "kill%s", grp);} v4text(a: array of byte): string{	return sys->sprint("%ud.%ud.%ud.%ud", int a[0], int a[1], int a[2], int a[3]);} parseopt(a: array of byte, isdhcp: int): (int, list of (int, array of byte)){	opts: list of (int, array of byte);	xop := NotDHCP;	for(i := 0; i < len a;){		op := int a[i++];		if(op == Opad)			continue;		if(op == Oend || i >= len a)			break;		l := int a[i++];		if(i+l > len a)			break;		if(isdhcp && op == Otype)			xop = int a[i];		else			opts = (op, a[i:i+l]) :: opts;		i += l;	}	rl := opts;	opts = nil;	for(; rl != nil; rl = tl rl)		opts = hd rl :: opts;	return (xop, opts);} dhcpreader(pidc: chan of int, srv: ref DhcpIO){	pidc <-= sys->pctl(0, nil);	for(;;){		abuf := array [576+Udphdrlen] of byte;		n := sys->read(srv.fd, abuf, len abuf);		if(n < 0){			if(debug)				sys->print("read error: %r\n");			sys->sleep(1000);			continue;		}		if(n < Udphdrlen+236){			if(debug)				sys->print("short read: %d\n", n);			continue;		}		buf := abuf[Udphdrlen:n];		n -= Udphdrlen;		dhcp := ref Dhcp;		dhcp.op = int buf[0];		if(dhcp.op != Bootpreply){			if(debug)				sys->print("bootp: not reply, discarded\n");			continue;		}		dhcp.dhcpop = NotDHCP;		if(n >= 240 && eqbytes(buf[236:240], magic))	# otherwise it's something we won't understand			(dhcp.dhcpop, dhcp.options) = parseopt(buf[240:n], 1);		case dhcp.dhcpop {		NotDHCP or Ack or Nak or Offer =>			;		* =>			if(debug)				sys->print("dhcp: ignore dhcp op %d\n", dhcp.dhcpop);			continue;		}		dhcp.udphdr = abuf[0:Udphdrlen];		dhcp.htype = int buf[1];		hlen := int buf[2];		dhcp.hops = int buf[3];		dhcp.xid = get4(buf, 4);		dhcp.secs = get2(buf, 8);		dhcp.flags = get2(buf, 10);		dhcp.ciaddr = IPaddr.newv4(buf[12:]);		dhcp.yiaddr = IPaddr.newv4(buf[16:]);		dhcp.siaddr = IPaddr.newv4(buf[20:]);		dhcp.giaddr = IPaddr.newv4(buf[24:]);		dhcp.chaddr = buf[28 : 28 + hlen];		dhcp.sname = stringget(buf[44 : 108]);		dhcp.file = stringget(buf[108 : 236]);		srv.dc <-= dhcp;	}} timeoutstart(msecs: int): (int, chan of int){	tc := chan of int;	spawn timeoutproc(tc, msecs);	return (<-tc, tc);} timeoutproc(c: chan of int, msecs: int){	c <-= sys->pctl(0, nil);	sys->sleep(msecs);	c <-= 1;} hex(b: int): int{	if(b >= '0' && b <= '9')		return b-'0';	if(b >= 'A' && b <= 'F')		return b-'A' + 10;	if(b >= 'a' && b <= 'f')		return b-'a' + 10;	return -1;} gethaddr(device: string): (int, string, array of byte){	fd := sys->open(device, Sys->OREAD);	if(fd == nil)		return (-1, sys->sprint("%r"), nil);	buf := array [100] of byte;	n := sys->read(fd, buf, len buf);	if(n < 0)		return (-1, sys->sprint("%r"), nil);	if(n == 0)		return (-1, "empty address file", nil);	addr := array [n/2] of byte;	for(i := 0; i < len addr; i++){		u := hex(int buf[2*i]);		l := hex(int buf[2*i+1]);		if(u < 0 || l < 0)			return (-1, "bad address syntax", nil);		addr[i] = byte ((u<<4)|l);	}	return (1, nil, addr);} newrequest(dest: IPaddr, bootfile: string, htype: int, haddr: array of byte, ipaddr: IPaddr, options: array of array of byte): ref Dhcp{	dhcp := ref Dhcp;	dhcp.op = Bootprequest;	hdr := array[Udphdrlen] of {* => byte 0};	hdr[Udpraddr:] = dest.v6();	put2(hdr, Udprport, 67);	dhcp.udphdr = hdr;	dhcp.htype = htype;	dhcp.chaddr = haddr;	dhcp.hops = 0;	dhcp.secs = 0;	dhcp.flags = 0;	dhcp.xid = 0;	dhcp.ciaddr = ipaddr;	dhcp.yiaddr = ip->v4noaddr;	dhcp.siaddr = ip->v4noaddr;	dhcp.giaddr = ip->v4noaddr;	dhcp.file = bootfile;	dhcp.dhcpop = NotDHCP;	if(options != nil){		for(i := 0; i < len options; i++)			if(options[i] != nil)				dhcp.options = (i, options[i]) :: dhcp.options;	}	clientid := array[len haddr + 1] of byte;	clientid[0] = byte htype;	clientid[1:] = haddr;	dhcp.options = (Oclientid, clientid) :: dhcp.options;	dhcp.options = (Ovendorclass, array of byte "plan9_386") :: dhcp.options;	# 386 will do because type doesn't matter	return dhcp;} udpannounce(net: string): (ref Sys->FD, string){	if(net == nil)		net = "/net";	(ok, conn) := sys->announce(net+"/udp!*!68");	if(ok < 0)		return (nil, sys->sprint("can't announce dhcp port: %r"));	if(sys->fprint(conn.cfd, "headers") < 0)		return (nil, sys->sprint("can't set headers mode on dhcp port: %r"));	conn.dfd = sys->open(conn.dir+"/data", Sys->ORDWR);	if(conn.dfd == nil)		return (nil, sys->sprint("can't open %s: %r", conn.dir+"/data"));	return (conn.dfd, nil);} ifcnoaddr(fd: ref Sys->FD, s: string){	if(fd != nil && sys->fprint(fd, "%s %s %s", s, (ip->noaddr).text(), (ip->noaddr).text()) < 0){		if(debug)			sys->print("dhcp: ctl %s: %r\n", s);	}} setup(net: string, device: string, init: ref Bootconf): (ref Dhcp, ref DhcpIO, string){	(htype, err, mac) := gethaddr(device);	if(htype < 0)		return (nil, nil, sys->sprint("can't get hardware MAC address: %s", err));	ciaddr := ip->v4noaddr;	if(init != nil && init.ip != nil){		valid: int;		(valid, ciaddr) = IPaddr.parse(init.ip);		if(valid < 0)			return (nil, nil, sys->sprint("invalid ip address: %s", init.ip));	}	(dfd, err2) := udpannounce(net);	if(err2 != nil)		return (nil, nil, err);	bootfile: string;	options: array of array of byte;	if(init != nil){		bootfile = init.bootf;		options = init.options;	}	return (newrequest(ip->v4bcast, bootfile, htype, mac, ciaddr, options), DhcpIO.new(dfd), nil);} ## BOOTP (RFC951) is used by Inferno only during net boots, to get initial IP address and TFTP address and parameters#bootp(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf): (ref Bootconf, string){	(req, srv, err) := setup(net, device, init);	if(err != nil)		return (nil, err);	ifcnoaddr(ctlifc, "add");	rdhcp := exchange(srv, ++xidgen, req, 1<<NotDHCP);	srv.rstop();	ifcnoaddr(ctlifc, "remove");	if(rdhcp == nil)		return (nil, "no response to BOOTP request");	return (fillbootconf(init, rdhcp), nil);} defparams := array[] of {	byte Omask, byte Orouter, byte Odnsserver, byte Ohostname, byte Odomainname, byte Ontpserver,};

Lines 1–500 of 1029.