Charles.Forsyth20080319-16353c9eba118 years agoHistory
310 lines · 6.8 KB · Brainfuck
RawDownload
implement Sntp; ## rfc1361 (simple network time protocol)# include "sys.m";	sys: Sys; include "draw.m"; include "ip.m";	ip: IP;	IPaddr: import ip; include "dial.m";	dial: Dial; include "timers.m";	timers: Timers;	Timer: import timers; include "arg.m"; Sntp: module{	init:	fn(nil: ref Draw->Context, nil: list of string);}; debug := 0; Retries: con 4;Delay: con 3*1000;	# milliseconds SNTP: adt {	li:	int;	vn:	int;	mode:	int;	stratum:	int;	# level of local clock	poll:	int;	# log2(maximum interval in seconds between successive messages)	precision:	int;	# log2(seconds precision of local clock) [eg, -6 for mains, -18 for microsec]	rootdelay:	int;	# round trip delay in seconds to reference (16:16 fraction)	dispersion:	int;	# maximum error relative to primary reference	clockid:	string;	# reference clock identifier		reftime:	big;	# local time at which clock last set/corrected	orgtime:	big;	# local time at which client transmitted request	rcvtime:	big;	# time at which request arrived at server	xmttime:	big;	# time server transmitted reply	auth:	array of byte;	# auth field (ignored by this implementation) 	new:	fn(vn, mode: int): ref SNTP;	pack:	fn(s: self ref SNTP): array of byte;	unpack:	fn(a: array of byte): ref SNTP;};SNTPlen: con 4+3*4+4*8; Version: con 1;	# accepted by version 2 and version 3 serversStratum: con 0;Poll: con 0;LI: con 0;Symmetric: con 2;ClientMode: con 3;ServerMode: con 4;Epoch: con big 86400*big (365*70 + 17);	# seconds between 1 Jan 1900 and 1 Jan 1970 Microsec: con big 1000000; server := "$ntp";stderr: ref Sys->FD; init(nil: ref Draw->Context, args: list of string){	sys = load Sys Sys->PATH;	ip = load IP IP->PATH;	timers = load Timers Timers->PATH;	dial = load Dial Dial->PATH; 	ip->init();	arg := load Arg Arg->PATH;	arg->init(args);	arg->setusage("sntp [-d] [server]"); 	doset := 1;	while((o := arg->opt()) != 0)		case o {		'd' => debug++;		'i' => doset = 0;		* =>	arg->usage();		}	args = arg->argv();	if(len args > 1)		arg->usage();	arg = nil; 	if(args != nil)		server = hd args; 	sys->pctl(Sys->NEWPGRP|Sys->FORKFD, nil);	stderr = sys->fildes(2);	timers->init(100); 	conn := dial->dial(dial->netmkaddr(server, "udp", "ntp"), nil);	if(conn == nil){		sys->fprint(stderr, "sntp: can't dial %s: %r\n", server);		raise "fail:dial";	} 	replies := chan of ref SNTP;	spawn reader(conn.dfd, replies); 	for(i:=0; i<Retries; i++){		request := SNTP.new(Version, ClientMode);		request.poll = 6;		request.orgtime = (big time() + Epoch)<<32;		b := request.pack();		if(sys->write(conn.dfd, b, len b) != len b){			sys->fprint(stderr, "sntp: UDP write failed: %r\n");			continue;		}		t := Timer.start(Delay);		alt{		reply := <-replies =>			t.stop();			if(reply == nil)				quit("read error");			if(debug){				sys->fprint(stderr, "LI = %d, version = %d, mode = %d\n", reply.li, reply.vn, reply.mode);				if(reply.stratum == 1)					sys->fprint(stderr, "stratum = 1 (%s), ", reply.clockid);				else					sys->fprint(stderr, "stratum = %d, ", reply.stratum);				sys->fprint(stderr, "poll = %d, prec = %d\n", reply.poll, reply.precision);				sys->fprint(stderr, "rootdelay = %d, dispersion = %d\n", reply.rootdelay, reply.dispersion);			}			if(reply.vn == 0 || reply.vn > 3)				continue;	# unsupported version, ignored			if(reply.mode >= 6 || reply.mode == ClientMode)				continue;			now := ((reply.xmttime>>32)&16rFFFFFFFF) - Epoch;			if(now <= big 1120000000)				continue;			if(reply.li == 3 || reply.stratum == 0)	# unsynchronised				sys->fprint(stderr, "sntp: time server not synchronised to reference time\n");			if(debug)				sys->print("%bd\n", now);			if(doset){				settime("#r/rtc", now);				settime("/dev/time", now*Microsec);			}			quit(nil);		<-t.timeout =>			continue;		}	}	sys->fprint(sys->fildes(2), "sntp: no response from server %s\n", server);	quit("timeout");} reader(fd: ref Sys->FD, replies: chan of ref SNTP){	for(;;){		buf := array[512] of byte;		nb := sys->read(fd, buf, len buf);		if(nb <= 0)			break;		reply := SNTP.unpack(buf[0:nb]);		if(reply == nil){			# ignore bad replies			if(debug)				sys->fprint(stderr, "sntp: invalid reply (len %d)\n", nb);			continue;		}		replies <-= reply;	}	if(debug)		sys->fprint(stderr, "sntp: UDP read failed: %r\n");	replies <-= nil;} quit(s: string){	pid := sys->pctl(0, nil);	timers->shutdown();	fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);	if(fd != nil)		sys->fprint(fd, "killgrp");	if(s != nil)		raise "fail:"+s;	exit;} time(): int{	n := rdn("#r/rtc");	if(n > big 300)	# ie, possibly set		return int n;	n = rdn("/dev/time");	if(n <= big 0)		return 0;	return int(n/big Microsec);} rdn(f: string): big{	fd := sys->open(f, Sys->OREAD);	if(fd == nil)		return big -1;	b := array[128] of byte;	n := sys->read(fd, b, len b);	if(n <= 0)		return big 0;	return big string b[0:n];} settime(f: string, t: big){	fd := sys->open(f, Sys->OWRITE);	if(fd != nil)		sys->fprint(fd, "%bd", t);} get8(a: array of byte, i: int): big{	b := big ip->get4(a, i+4) & 16rFFFFFFFF;	return (big ip->get4(a, i) << 32) | b;} put8(a: array of byte, o: int, v: big){	ip->put4(a, o, int (v>>32));	ip->put4(a, o+4, int v);} SNTP.unpack(a: array of byte): ref SNTP{	if(len a < SNTPlen)		return nil;	s := ref SNTP;	mode := int a[0];	s.li = mode>>6;	s.vn = (mode>>3);	s.mode = mode & 3;	s.stratum = int a[1];	s.poll = int a[2];	if(s.poll & 16r80)		s.poll |= ~0 << 8;	s.precision = int a[3];	if(s.precision & 16r80)		s.precision |= ~0 << 8;	s.rootdelay = ip->get4(a, 4);	s.dispersion = ip->get4(a, 8);	if(s.stratum <= 1){		for(i := 12; i < 16; i++)			if(a[i] == byte 0)				break;		s.clockid = string a[12:i];	}else		s.clockid = sys->sprint("%d.%d.%d.%d", int a[12], int a[13], int a[14], int a[15]);	s.reftime = get8(a, 16);	s.orgtime = get8(a, 24);	s.rcvtime = get8(a, 32);	s.xmttime = get8(a, 40);	if(len a > SNTPlen)		s.auth = a[48:];	return s;} SNTP.pack(s: self ref SNTP): array of byte{	a := array[SNTPlen + len s.auth] of byte;	a[0] = byte ((s.li<<6) | (s.vn<<3) | s.mode);	a[1] = byte s.stratum;	a[2] = byte s.poll;	a[3] = byte s.precision;	ip->put4(a, 4, s.rootdelay);	ip->put4(a, 8, s.dispersion);	ip->put4(a, 12, 0);	# clockid field	if(s.clockid != nil){		if(s.stratum <= 1){			b := array of byte s.clockid;			for(i := 0; i < len b && i < 4; i++)				a[12+i] = b[i];		}else			a[12:] = IPaddr.parse(s.clockid).t1.v4();	}	put8(a, 16, s.reftime);	put8(a, 24, s.orgtime);	put8(a, 32, s.rcvtime);	put8(a, 40, s.xmttime);	if(s.auth != nil)		a[48:] = s.auth;	return a;} SNTP.new(vn, mode: int): ref SNTP{	s := ref SNTP;	s.vn = vn;	s.mode = mode;	s.li = 0;	s.stratum = 0;	s.poll = 0;	s.precision = 0;	s.clockid = nil;	s.reftime = big 0;	s.orgtime = big 0;	s.rcvtime = big 0;	s.xmttime = big 0;	return s;}