xref: /plan9-contrib/sys/src/cmd/auth/factotum/p9any.c (revision 260f7b65f27bafa63b361ec761042c0d76ce7e36)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier  * p9any - protocol negotiator.
39a747e4fSDavid du Colombier  *
49a747e4fSDavid du Colombier  * Protocol:
59a747e4fSDavid du Colombier  *	Server->Client: list of proto@domain, tokenize separated, nul terminated
69a747e4fSDavid du Colombier  *	Client->Server: proto domain, tokenize separated (not proto@domain), nul terminated
79a747e4fSDavid du Colombier  *
89a747e4fSDavid du Colombier  * Server protocol:
99a747e4fSDavid du Colombier  * 	read list of protocols.
109a747e4fSDavid du Colombier  *	write null-terminated
119a747e4fSDavid du Colombier  */
129a747e4fSDavid du Colombier 
139a747e4fSDavid du Colombier #include "dat.h"
149a747e4fSDavid du Colombier 
159a747e4fSDavid du Colombier static Proto *negotiable[] = {
169a747e4fSDavid du Colombier 	&p9sk1,
179a747e4fSDavid du Colombier };
189a747e4fSDavid du Colombier 
199a747e4fSDavid du Colombier struct State
209a747e4fSDavid du Colombier {
219a747e4fSDavid du Colombier 	Fsstate subfss;
229a747e4fSDavid du Colombier 	State *substate;	/* be very careful; this is not one of our States */
239a747e4fSDavid du Colombier 	Proto *subproto;
249a747e4fSDavid du Colombier 	int keyasked;
259a747e4fSDavid du Colombier 	String *subdom;
269a747e4fSDavid du Colombier 	int version;
279a747e4fSDavid du Colombier };
289a747e4fSDavid du Colombier 
299a747e4fSDavid du Colombier enum
309a747e4fSDavid du Colombier {
319a747e4fSDavid du Colombier 	CNeedProtos,
329a747e4fSDavid du Colombier 	CHaveProto,
339a747e4fSDavid du Colombier 	CNeedOK,
349a747e4fSDavid du Colombier 	CRelay,
359a747e4fSDavid du Colombier 
369a747e4fSDavid du Colombier 	SHaveProtos,
379a747e4fSDavid du Colombier 	SNeedProto,
389a747e4fSDavid du Colombier 	SHaveOK,
399a747e4fSDavid du Colombier 	SRelay,
409a747e4fSDavid du Colombier 
419a747e4fSDavid du Colombier 	Maxphase,
429a747e4fSDavid du Colombier };
439a747e4fSDavid du Colombier 
449a747e4fSDavid du Colombier static char *phasenames[Maxphase] =
459a747e4fSDavid du Colombier {
469a747e4fSDavid du Colombier [CNeedProtos]	"CNeedProtos",
479a747e4fSDavid du Colombier [CHaveProto]	"CHaveProto",
489a747e4fSDavid du Colombier [CNeedOK]	"CNeedOK",
499a747e4fSDavid du Colombier [CRelay]	"CRelay",
509a747e4fSDavid du Colombier [SHaveProtos]	"SHaveProtos",
519a747e4fSDavid du Colombier [SNeedProto]	"SNeedProto",
529a747e4fSDavid du Colombier [SHaveOK]	"SHaveOK",
539a747e4fSDavid du Colombier [SRelay]	"SRelay",
549a747e4fSDavid du Colombier };
559a747e4fSDavid du Colombier 
569a747e4fSDavid du Colombier static int
p9anyinit(Proto *,Fsstate * fss)579a747e4fSDavid du Colombier p9anyinit(Proto*, Fsstate *fss)
589a747e4fSDavid du Colombier {
599a747e4fSDavid du Colombier 	int iscli;
609a747e4fSDavid du Colombier 	State *s;
619a747e4fSDavid du Colombier 
622ebbfa15SDavid du Colombier 	if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
639a747e4fSDavid du Colombier 		return failure(fss, nil);
649a747e4fSDavid du Colombier 
659a747e4fSDavid du Colombier 	s = emalloc(sizeof *s);
669a747e4fSDavid du Colombier 	fss = fss;
679a747e4fSDavid du Colombier 	fss->phasename = phasenames;
689a747e4fSDavid du Colombier 	fss->maxphase = Maxphase;
699a747e4fSDavid du Colombier 	if(iscli)
709a747e4fSDavid du Colombier 		fss->phase = CNeedProtos;
719a747e4fSDavid du Colombier 	else
729a747e4fSDavid du Colombier 		fss->phase = SHaveProtos;
739a747e4fSDavid du Colombier 	s->version = 1;
749a747e4fSDavid du Colombier 	fss->ps = s;
759a747e4fSDavid du Colombier 	return RpcOk;
769a747e4fSDavid du Colombier }
779a747e4fSDavid du Colombier 
789a747e4fSDavid du Colombier static void
p9anyclose(Fsstate * fss)799a747e4fSDavid du Colombier p9anyclose(Fsstate *fss)
809a747e4fSDavid du Colombier {
819a747e4fSDavid du Colombier 	State *s;
829a747e4fSDavid du Colombier 
839a747e4fSDavid du Colombier 	s = fss->ps;
849a747e4fSDavid du Colombier 	if(s->subproto && s->subfss.ps && s->subproto->close)
859a747e4fSDavid du Colombier 		(*s->subproto->close)(&s->subfss);
869a747e4fSDavid du Colombier 	s->subproto = nil;
879a747e4fSDavid du Colombier 	s->substate = nil;
889a747e4fSDavid du Colombier 	s_free(s->subdom);
899a747e4fSDavid du Colombier 	s->subdom = nil;
909a747e4fSDavid du Colombier 	s->keyasked = 0;
919a747e4fSDavid du Colombier 	memset(&s->subfss, 0, sizeof s->subfss);
929a747e4fSDavid du Colombier 	free(s);
939a747e4fSDavid du Colombier }
949a747e4fSDavid du Colombier 
959a747e4fSDavid du Colombier static void
setupfss(Fsstate * fss,State * s,Key * k)969a747e4fSDavid du Colombier setupfss(Fsstate *fss, State *s, Key *k)
979a747e4fSDavid du Colombier {
989a747e4fSDavid du Colombier 	fss->attr = setattr(fss->attr, "proto=%q", s->subproto->name);
992ebbfa15SDavid du Colombier 	fss->attr = setattr(fss->attr, "dom=%q", _strfindattr(k->attr, "dom"));
1009a747e4fSDavid du Colombier 	s->subfss.attr = fss->attr;
1019a747e4fSDavid du Colombier 	s->subfss.phase = Notstarted;
1029a747e4fSDavid du Colombier 	s->subfss.sysuser = fss->sysuser;
1039a747e4fSDavid du Colombier 	s->subfss.seqnum = fss->seqnum;
1049a747e4fSDavid du Colombier 	s->subfss.conf = fss->conf;
1059a747e4fSDavid du Colombier 	s->subfss.nconf = fss->nconf;
1069a747e4fSDavid du Colombier }
1079a747e4fSDavid du Colombier 
1089a747e4fSDavid du Colombier static int
passret(Fsstate * fss,State * s,int ret)1099a747e4fSDavid du Colombier passret(Fsstate *fss, State *s, int ret)
1109a747e4fSDavid du Colombier {
1119a747e4fSDavid du Colombier 	switch(ret){
1129a747e4fSDavid du Colombier 	default:
1139a747e4fSDavid du Colombier 		return ret;
1149a747e4fSDavid du Colombier 	case RpcFailure:
1159a747e4fSDavid du Colombier 		if(s->subfss.phase == Broken)
1169a747e4fSDavid du Colombier 			fss->phase = Broken;
1179a747e4fSDavid du Colombier 		memmove(fss->err, s->subfss.err, sizeof fss->err);
1189a747e4fSDavid du Colombier 		return ret;
1199a747e4fSDavid du Colombier 	case RpcNeedkey:
1209a747e4fSDavid du Colombier 		memmove(fss->keyinfo, s->subfss.keyinfo, sizeof fss->keyinfo);
1219a747e4fSDavid du Colombier 		return ret;
1229a747e4fSDavid du Colombier 	case RpcOk:
1239a747e4fSDavid du Colombier 		if(s->subfss.haveai){
1249a747e4fSDavid du Colombier 			fss->haveai = 1;
1259a747e4fSDavid du Colombier 			fss->ai = s->subfss.ai;
1269a747e4fSDavid du Colombier 			s->subfss.haveai = 0;
1279a747e4fSDavid du Colombier 		}
1289a747e4fSDavid du Colombier 		if(s->subfss.phase == Established)
1299a747e4fSDavid du Colombier 			fss->phase = Established;
1309a747e4fSDavid du Colombier 		return ret;
1319a747e4fSDavid du Colombier 	case RpcToosmall:
1329a747e4fSDavid du Colombier 		fss->rpc.nwant = s->subfss.rpc.nwant;
1339a747e4fSDavid du Colombier 		return ret;
1349a747e4fSDavid du Colombier 	case RpcConfirm:
1359a747e4fSDavid du Colombier 		fss->conf = s->subfss.conf;
1369a747e4fSDavid du Colombier 		fss->nconf = s->subfss.nconf;
1379a747e4fSDavid du Colombier 		return ret;
1389a747e4fSDavid du Colombier 	}
1399a747e4fSDavid du Colombier }
1409a747e4fSDavid du Colombier 
1419a747e4fSDavid du Colombier static int
p9anyread(Fsstate * fss,void * a,uint * n)1429a747e4fSDavid du Colombier p9anyread(Fsstate *fss, void *a, uint *n)
1439a747e4fSDavid du Colombier {
144*260f7b65SDavid du Colombier 	int i, m, ophase, ret;
1459a747e4fSDavid du Colombier 	Attr *anew;
1469a747e4fSDavid du Colombier 	Key *k;
147*260f7b65SDavid du Colombier 	Keyinfo ki;
1489a747e4fSDavid du Colombier 	String *negstr;
1499a747e4fSDavid du Colombier 	State *s;
1509a747e4fSDavid du Colombier 
1519a747e4fSDavid du Colombier 	s = fss->ps;
1529a747e4fSDavid du Colombier 	switch(fss->phase){
1539a747e4fSDavid du Colombier 	default:
1549a747e4fSDavid du Colombier 		return phaseerror(fss, "read");
1559a747e4fSDavid du Colombier 
1569a747e4fSDavid du Colombier 	case SHaveProtos:
1579a747e4fSDavid du Colombier 		m = 0;
1589a747e4fSDavid du Colombier 		negstr = s_new();
159*260f7b65SDavid du Colombier 		mkkeyinfo(&ki, fss, nil);
160*260f7b65SDavid du Colombier 		ki.attr = nil;
161*260f7b65SDavid du Colombier 		ki.noconf = 1;
162*260f7b65SDavid du Colombier 		ki.user = nil;
1639a747e4fSDavid du Colombier 		for(i=0; i<nelem(negotiable); i++){
1649a747e4fSDavid du Colombier 			anew = setattr(_copyattr(fss->attr), "proto=%q dom?", negotiable[i]->name);
165*260f7b65SDavid du Colombier 			ki.attr = anew;
166*260f7b65SDavid du Colombier 			for(ki.skip=0; findkey(&k, &ki, nil)==RpcOk; ki.skip++){
1679a747e4fSDavid du Colombier 				if(m++)
1689a747e4fSDavid du Colombier 					s_append(negstr, " ");
1699a747e4fSDavid du Colombier 				s_append(negstr, negotiable[i]->name);
1709a747e4fSDavid du Colombier 				s_append(negstr, "@");
1712ebbfa15SDavid du Colombier 				s_append(negstr, _strfindattr(k->attr, "dom"));
1729a747e4fSDavid du Colombier 				closekey(k);
1739a747e4fSDavid du Colombier 			}
1749a747e4fSDavid du Colombier 			_freeattr(anew);
1759a747e4fSDavid du Colombier 		}
1769a747e4fSDavid du Colombier 		if(m == 0){
1779a747e4fSDavid du Colombier 			s_free(negstr);
1789a747e4fSDavid du Colombier 			return failure(fss, Enegotiation);
1799a747e4fSDavid du Colombier 		}
1809a747e4fSDavid du Colombier 		i = s_len(negstr)+1;
1819a747e4fSDavid du Colombier 		if(*n < i){
1829a747e4fSDavid du Colombier 			s_free(negstr);
1839a747e4fSDavid du Colombier 			return toosmall(fss, i);
1849a747e4fSDavid du Colombier 		}
1859a747e4fSDavid du Colombier 		*n = i;
1869a747e4fSDavid du Colombier 		memmove(a, s_to_c(negstr), i+1);
1879a747e4fSDavid du Colombier 		fss->phase = SNeedProto;
1889a747e4fSDavid du Colombier 		s_free(negstr);
1899a747e4fSDavid du Colombier 		return RpcOk;
1909a747e4fSDavid du Colombier 
1919a747e4fSDavid du Colombier 	case CHaveProto:
1929a747e4fSDavid du Colombier 		i = strlen(s->subproto->name)+1+s_len(s->subdom)+1;
1939a747e4fSDavid du Colombier 		if(*n < i)
1949a747e4fSDavid du Colombier 			return toosmall(fss, i);
1959a747e4fSDavid du Colombier 		*n = i;
1969a747e4fSDavid du Colombier 		strcpy(a, s->subproto->name);
1979a747e4fSDavid du Colombier 		strcat(a, " ");
1989a747e4fSDavid du Colombier 		strcat(a, s_to_c(s->subdom));
1999a747e4fSDavid du Colombier 		if(s->version == 1)
2009a747e4fSDavid du Colombier 			fss->phase = CRelay;
2019a747e4fSDavid du Colombier 		else
2029a747e4fSDavid du Colombier 			fss->phase = CNeedOK;
2039a747e4fSDavid du Colombier 		return RpcOk;
2049a747e4fSDavid du Colombier 
2059a747e4fSDavid du Colombier 	case SHaveOK:
2069a747e4fSDavid du Colombier 		i = 3;
2079a747e4fSDavid du Colombier 		if(*n < i)
2089a747e4fSDavid du Colombier 			return toosmall(fss, i);
2099a747e4fSDavid du Colombier 		*n = i;
2109a747e4fSDavid du Colombier 		strcpy(a, "OK");
2119a747e4fSDavid du Colombier 		fss->phase = SRelay;
2129a747e4fSDavid du Colombier 		return RpcOk;
2139a747e4fSDavid du Colombier 
2149a747e4fSDavid du Colombier 	case CRelay:
2159a747e4fSDavid du Colombier 	case SRelay:
2169a747e4fSDavid du Colombier 		ophase = s->subfss.phase;
2179a747e4fSDavid du Colombier 		ret = (*s->subproto->read)(&s->subfss, a, n);
2189a747e4fSDavid du Colombier 		rpcrdwrlog(&s->subfss, "read", *n, ophase, ret);
2199a747e4fSDavid du Colombier 		return passret(fss, s, ret);
2209a747e4fSDavid du Colombier 	}
2219a747e4fSDavid du Colombier }
2229a747e4fSDavid du Colombier 
2239a747e4fSDavid du Colombier static char*
getdom(char * p)2249a747e4fSDavid du Colombier getdom(char *p)
2259a747e4fSDavid du Colombier {
2269a747e4fSDavid du Colombier 	p = strchr(p, '@');
2279a747e4fSDavid du Colombier 	if(p == nil)
2289a747e4fSDavid du Colombier 		return "";
2299a747e4fSDavid du Colombier 	return p+1;
2309a747e4fSDavid du Colombier }
2319a747e4fSDavid du Colombier 
2329a747e4fSDavid du Colombier static Proto*
findneg(char * name)2339a747e4fSDavid du Colombier findneg(char *name)
2349a747e4fSDavid du Colombier {
2359a747e4fSDavid du Colombier 	int i, len;
2369a747e4fSDavid du Colombier 	char *p;
2379a747e4fSDavid du Colombier 
2389a747e4fSDavid du Colombier 	if(p = strchr(name, '@'))
2399a747e4fSDavid du Colombier 		len = p-name;
2409a747e4fSDavid du Colombier 	else
2419a747e4fSDavid du Colombier 		len = strlen(name);
2429a747e4fSDavid du Colombier 
2439a747e4fSDavid du Colombier 	for(i=0; i<nelem(negotiable); i++)
2449a747e4fSDavid du Colombier 		if(strncmp(negotiable[i]->name, name, len) == 0 && negotiable[i]->name[len] == 0)
2459a747e4fSDavid du Colombier 			return negotiable[i];
2469a747e4fSDavid du Colombier 	return nil;
2479a747e4fSDavid du Colombier }
2489a747e4fSDavid du Colombier 
2499a747e4fSDavid du Colombier static int
p9anywrite(Fsstate * fss,void * va,uint n)2509a747e4fSDavid du Colombier p9anywrite(Fsstate *fss, void *va, uint n)
2519a747e4fSDavid du Colombier {
2529a747e4fSDavid du Colombier 	char *a, *dom, *user, *token[20];
2539a747e4fSDavid du Colombier 	int asking, i, m, ophase, ret;
2549a747e4fSDavid du Colombier 	Attr *anew, *anewsf, *attr;
2559a747e4fSDavid du Colombier 	Key *k;
256*260f7b65SDavid du Colombier 	Keyinfo ki;
2579a747e4fSDavid du Colombier 	Proto *p;
2589a747e4fSDavid du Colombier 	State *s;
2599a747e4fSDavid du Colombier 
2609a747e4fSDavid du Colombier 	s = fss->ps;
2619a747e4fSDavid du Colombier 	a = va;
2629a747e4fSDavid du Colombier 	switch(fss->phase){
2639a747e4fSDavid du Colombier 	default:
2649a747e4fSDavid du Colombier 		return phaseerror(fss, "write");
2659a747e4fSDavid du Colombier 
2669a747e4fSDavid du Colombier 	case CNeedProtos:
2679a747e4fSDavid du Colombier 		if(n==0 || a[n-1] != '\0')
2689a747e4fSDavid du Colombier 			return toosmall(fss, 2048);
2699a747e4fSDavid du Colombier 		a = estrdup(a);
2709a747e4fSDavid du Colombier 		m = tokenize(a, token, nelem(token));
2719a747e4fSDavid du Colombier 		if(m > 0 && strncmp(token[0], "v.", 2) == 0){
2729a747e4fSDavid du Colombier 			s->version = atoi(token[0]+2);
273d9306527SDavid du Colombier 			if(s->version != 2){
274d9306527SDavid du Colombier 				free(a);
2759a747e4fSDavid du Colombier 				return failure(fss, "unknown version of p9any");
2769a747e4fSDavid du Colombier 			}
277d9306527SDavid du Colombier 		}
2789a747e4fSDavid du Colombier 
2799a747e4fSDavid du Colombier 		/*
2809a747e4fSDavid du Colombier 		 * look for a key
2819a747e4fSDavid du Colombier 		 */
2829a747e4fSDavid du Colombier 		anew = _delattr(_delattr(_copyattr(fss->attr), "proto"), "role");
2839a747e4fSDavid du Colombier 		anewsf = _delattr(_copyattr(anew), "user");
2842ebbfa15SDavid du Colombier 		user = _strfindattr(anew, "user");
2859a747e4fSDavid du Colombier 		k = nil;
2869a747e4fSDavid du Colombier 		p = nil;
2879a747e4fSDavid du Colombier 		dom = nil;
2889a747e4fSDavid du Colombier 		for(i=(s->version==1?0:1); i<m; i++){
2899a747e4fSDavid du Colombier 			p = findneg(token[i]);
2909a747e4fSDavid du Colombier 			if(p == nil)
2919a747e4fSDavid du Colombier 				continue;
2929a747e4fSDavid du Colombier 			dom = getdom(token[i]);
2939a747e4fSDavid du Colombier 			ret = RpcFailure;
294*260f7b65SDavid du Colombier 			mkkeyinfo(&ki, fss, nil);
295*260f7b65SDavid du Colombier 			if(user==nil || strcmp(user, fss->sysuser)==0){
296*260f7b65SDavid du Colombier 				ki.attr = anewsf;
297*260f7b65SDavid du Colombier 				ki.user = nil;
298*260f7b65SDavid du Colombier 				ret = findkey(&k, &ki, "proto=%q dom=%q role=speakfor %s",
2999a747e4fSDavid du Colombier 						p->name, dom, p->keyprompt);
300*260f7b65SDavid du Colombier 			}
301*260f7b65SDavid du Colombier 			if(ret == RpcFailure){
302*260f7b65SDavid du Colombier 				ki.attr = anew;
303*260f7b65SDavid du Colombier 				ki.user = fss->sysuser;
304*260f7b65SDavid du Colombier 				ret = findkey(&k, &ki,
3059a747e4fSDavid du Colombier 					"proto=%q dom=%q role=client %s",
3069a747e4fSDavid du Colombier 					p->name, dom, p->keyprompt);
307*260f7b65SDavid du Colombier 			}
308d9306527SDavid du Colombier 			if(ret == RpcConfirm){
309d9306527SDavid du Colombier 				free(a);
3109a747e4fSDavid du Colombier 				return ret;
311d9306527SDavid du Colombier 			}
3129a747e4fSDavid du Colombier 			if(ret == RpcOk)
3139a747e4fSDavid du Colombier 				break;
3149a747e4fSDavid du Colombier 		}
3159a747e4fSDavid du Colombier 		_freeattr(anewsf);
3169a747e4fSDavid du Colombier 
3179a747e4fSDavid du Colombier 		/*
3189a747e4fSDavid du Colombier 		 * no acceptable key, go through the proto@domains one at a time.
3199a747e4fSDavid du Colombier 		 */
3209a747e4fSDavid du Colombier 		asking = 0;
3219a747e4fSDavid du Colombier 		if(k == nil){
3229a747e4fSDavid du Colombier 			while(!asking && s->keyasked < m){
3239a747e4fSDavid du Colombier 				p = findneg(token[s->keyasked]);
3249a747e4fSDavid du Colombier 				if(p == nil){
3259a747e4fSDavid du Colombier 					s->keyasked++;
3269a747e4fSDavid du Colombier 					continue;
3279a747e4fSDavid du Colombier 				}
3289a747e4fSDavid du Colombier 				dom = getdom(token[s->keyasked]);
329*260f7b65SDavid du Colombier 				mkkeyinfo(&ki, fss, nil);
330*260f7b65SDavid du Colombier 				ki.attr = anew;
331*260f7b65SDavid du Colombier 				ret = findkey(&k, &ki,
3329a747e4fSDavid du Colombier 					"proto=%q dom=%q role=client %s",
3339a747e4fSDavid du Colombier 					p->name, dom, p->keyprompt);
3349a747e4fSDavid du Colombier 				s->keyasked++;
3359a747e4fSDavid du Colombier 				if(ret == RpcNeedkey){
3369a747e4fSDavid du Colombier 					asking = 1;
3379a747e4fSDavid du Colombier 					break;
3389a747e4fSDavid du Colombier 				}
3399a747e4fSDavid du Colombier 			}
3409a747e4fSDavid du Colombier 		}
3419a747e4fSDavid du Colombier 		if(k == nil){
3429a747e4fSDavid du Colombier 			free(a);
3439a747e4fSDavid du Colombier 			_freeattr(anew);
3449a747e4fSDavid du Colombier 			if(asking)
3459a747e4fSDavid du Colombier 				return RpcNeedkey;
3469a747e4fSDavid du Colombier 			else if(s->keyasked)
3479a747e4fSDavid du Colombier 				return failure(fss, nil);
3489a747e4fSDavid du Colombier 			else
3499a747e4fSDavid du Colombier 				return failure(fss, Enegotiation);
3509a747e4fSDavid du Colombier 		}
3519a747e4fSDavid du Colombier 		s->subdom = s_copy(dom);
3529a747e4fSDavid du Colombier 		s->subproto = p;
3539a747e4fSDavid du Colombier 		free(a);
3549a747e4fSDavid du Colombier 		_freeattr(anew);
3559a747e4fSDavid du Colombier 		setupfss(fss, s, k);
3569a747e4fSDavid du Colombier 		closekey(k);
3579a747e4fSDavid du Colombier 		ret = (*s->subproto->init)(p, &s->subfss);
3589a747e4fSDavid du Colombier 		rpcstartlog(s->subfss.attr, &s->subfss, ret);
3599a747e4fSDavid du Colombier 		if(ret == RpcOk)
3609a747e4fSDavid du Colombier 			fss->phase = CHaveProto;
3619a747e4fSDavid du Colombier 		return passret(fss, s, ret);
3629a747e4fSDavid du Colombier 
3639a747e4fSDavid du Colombier 	case SNeedProto:
3649a747e4fSDavid du Colombier 		if(n==0 || a[n-1] != '\0')
3659a747e4fSDavid du Colombier 			return toosmall(fss, n+1);
3669a747e4fSDavid du Colombier 		a = estrdup(a);
3679a747e4fSDavid du Colombier 		m = tokenize(a, token, nelem(token));
368d9306527SDavid du Colombier 		if(m != 2){
369d9306527SDavid du Colombier 			free(a);
3709a747e4fSDavid du Colombier 			return failure(fss, Ebadarg);
371d9306527SDavid du Colombier 		}
3729a747e4fSDavid du Colombier 		p = findneg(token[0]);
373d9306527SDavid du Colombier 		if(p == nil){
374d9306527SDavid du Colombier 			free(a);
3759a747e4fSDavid du Colombier 			return failure(fss, Enegotiation);
376d9306527SDavid du Colombier 		}
3779a747e4fSDavid du Colombier 		attr = _delattr(_copyattr(fss->attr), "proto");
378*260f7b65SDavid du Colombier 		mkkeyinfo(&ki, fss, nil);
379*260f7b65SDavid du Colombier 		ki.attr = attr;
380*260f7b65SDavid du Colombier 		ki.user = nil;
381*260f7b65SDavid du Colombier 		ret = findkey(&k, &ki, "proto=%q dom=%q role=server", token[0], token[1]);
382d9306527SDavid du Colombier 		free(a);
3839a747e4fSDavid du Colombier 		_freeattr(attr);
3849a747e4fSDavid du Colombier 		if(ret == RpcConfirm)
3859a747e4fSDavid du Colombier 			return ret;
3869a747e4fSDavid du Colombier 		if(ret != RpcOk)
3879a747e4fSDavid du Colombier 			return failure(fss, Enegotiation);
3889a747e4fSDavid du Colombier 		s->subproto = p;
3899a747e4fSDavid du Colombier 		setupfss(fss, s, k);
3909a747e4fSDavid du Colombier 		closekey(k);
3919a747e4fSDavid du Colombier 		ret = (*s->subproto->init)(p, &s->subfss);
3929a747e4fSDavid du Colombier 		if(ret == RpcOk){
3939a747e4fSDavid du Colombier 			if(s->version == 1)
3949a747e4fSDavid du Colombier 				fss->phase = SRelay;
3959a747e4fSDavid du Colombier 			else
3969a747e4fSDavid du Colombier 				fss->phase = SHaveOK;
3979a747e4fSDavid du Colombier 		}
3989a747e4fSDavid du Colombier 		return passret(fss, s, ret);
3999a747e4fSDavid du Colombier 
4009a747e4fSDavid du Colombier 	case CNeedOK:
4019a747e4fSDavid du Colombier 		if(n < 3)
4029a747e4fSDavid du Colombier 			return toosmall(fss, 3);
4039a747e4fSDavid du Colombier 		if(strcmp("OK", a) != 0)
4049a747e4fSDavid du Colombier 			return failure(fss, "server gave up");
4059a747e4fSDavid du Colombier 		fss->phase = CRelay;
4069a747e4fSDavid du Colombier 		return RpcOk;
4079a747e4fSDavid du Colombier 
4089a747e4fSDavid du Colombier 	case CRelay:
4099a747e4fSDavid du Colombier 	case SRelay:
4109a747e4fSDavid du Colombier 		ophase = s->subfss.phase;
4119a747e4fSDavid du Colombier 		ret = (*s->subproto->write)(&s->subfss, va, n);
4129a747e4fSDavid du Colombier 		rpcrdwrlog(&s->subfss, "write", n, ophase, ret);
4139a747e4fSDavid du Colombier 		return passret(fss, s, ret);
4149a747e4fSDavid du Colombier 	}
4159a747e4fSDavid du Colombier }
4169a747e4fSDavid du Colombier 
4179a747e4fSDavid du Colombier Proto p9any =
4189a747e4fSDavid du Colombier {
4199a747e4fSDavid du Colombier .name=	"p9any",
4209a747e4fSDavid du Colombier .init=		p9anyinit,
4219a747e4fSDavid du Colombier .write=	p9anywrite,
4229a747e4fSDavid du Colombier .read=	p9anyread,
4239a747e4fSDavid du Colombier .close=	p9anyclose,
4249a747e4fSDavid du Colombier };
425