1 /*
2 * p9any - protocol negotiator.
3 *
4 * Protocol:
5 * Server->Client: list of proto@domain, tokenize separated, nul terminated
6 * Client->Server: proto domain, tokenize separated (not proto@domain), nul terminated
7 *
8 * Server protocol:
9 * read list of protocols.
10 * write null-terminated
11 */
12
13 #include "dat.h"
14
15 static Proto *negotiable[] = {
16 &p9sk1,
17 };
18
19 struct State
20 {
21 Fsstate subfss;
22 State *substate; /* be very careful; this is not one of our States */
23 Proto *subproto;
24 int keyasked;
25 String *subdom;
26 int version;
27 };
28
29 enum
30 {
31 CNeedProtos,
32 CHaveProto,
33 CNeedOK,
34 CRelay,
35
36 SHaveProtos,
37 SNeedProto,
38 SHaveOK,
39 SRelay,
40
41 Maxphase,
42 };
43
44 static char *phasenames[Maxphase] =
45 {
46 [CNeedProtos] "CNeedProtos",
47 [CHaveProto] "CHaveProto",
48 [CNeedOK] "CNeedOK",
49 [CRelay] "CRelay",
50 [SHaveProtos] "SHaveProtos",
51 [SNeedProto] "SNeedProto",
52 [SHaveOK] "SHaveOK",
53 [SRelay] "SRelay",
54 };
55
56 static int
p9anyinit(Proto *,Fsstate * fss)57 p9anyinit(Proto*, Fsstate *fss)
58 {
59 int iscli;
60 State *s;
61
62 if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
63 return failure(fss, nil);
64
65 s = emalloc(sizeof *s);
66 fss = fss;
67 fss->phasename = phasenames;
68 fss->maxphase = Maxphase;
69 if(iscli)
70 fss->phase = CNeedProtos;
71 else
72 fss->phase = SHaveProtos;
73 s->version = 1;
74 fss->ps = s;
75 return RpcOk;
76 }
77
78 static void
p9anyclose(Fsstate * fss)79 p9anyclose(Fsstate *fss)
80 {
81 State *s;
82
83 s = fss->ps;
84 if(s->subproto && s->subfss.ps && s->subproto->close)
85 (*s->subproto->close)(&s->subfss);
86 s->subproto = nil;
87 s->substate = nil;
88 s_free(s->subdom);
89 s->subdom = nil;
90 s->keyasked = 0;
91 memset(&s->subfss, 0, sizeof s->subfss);
92 free(s);
93 }
94
95 static void
setupfss(Fsstate * fss,State * s,Key * k)96 setupfss(Fsstate *fss, State *s, Key *k)
97 {
98 fss->attr = setattr(fss->attr, "proto=%q", s->subproto->name);
99 fss->attr = setattr(fss->attr, "dom=%q", _strfindattr(k->attr, "dom"));
100 s->subfss.attr = fss->attr;
101 s->subfss.phase = Notstarted;
102 s->subfss.sysuser = fss->sysuser;
103 s->subfss.seqnum = fss->seqnum;
104 s->subfss.conf = fss->conf;
105 s->subfss.nconf = fss->nconf;
106 }
107
108 static int
passret(Fsstate * fss,State * s,int ret)109 passret(Fsstate *fss, State *s, int ret)
110 {
111 switch(ret){
112 default:
113 return ret;
114 case RpcFailure:
115 if(s->subfss.phase == Broken)
116 fss->phase = Broken;
117 memmove(fss->err, s->subfss.err, sizeof fss->err);
118 return ret;
119 case RpcNeedkey:
120 memmove(fss->keyinfo, s->subfss.keyinfo, sizeof fss->keyinfo);
121 return ret;
122 case RpcOk:
123 if(s->subfss.haveai){
124 fss->haveai = 1;
125 fss->ai = s->subfss.ai;
126 s->subfss.haveai = 0;
127 }
128 if(s->subfss.phase == Established)
129 fss->phase = Established;
130 return ret;
131 case RpcToosmall:
132 fss->rpc.nwant = s->subfss.rpc.nwant;
133 return ret;
134 case RpcConfirm:
135 fss->conf = s->subfss.conf;
136 fss->nconf = s->subfss.nconf;
137 return ret;
138 }
139 }
140
141 static int
p9anyread(Fsstate * fss,void * a,uint * n)142 p9anyread(Fsstate *fss, void *a, uint *n)
143 {
144 int i, m, ophase, ret;
145 Attr *anew;
146 Key *k;
147 Keyinfo ki;
148 String *negstr;
149 State *s;
150
151 s = fss->ps;
152 switch(fss->phase){
153 default:
154 return phaseerror(fss, "read");
155
156 case SHaveProtos:
157 m = 0;
158 negstr = s_new();
159 mkkeyinfo(&ki, fss, nil);
160 ki.attr = nil;
161 ki.noconf = 1;
162 ki.user = nil;
163 for(i=0; i<nelem(negotiable); i++){
164 anew = setattr(_copyattr(fss->attr), "proto=%q dom?", negotiable[i]->name);
165 ki.attr = anew;
166 for(ki.skip=0; findkey(&k, &ki, nil)==RpcOk; ki.skip++){
167 if(m++)
168 s_append(negstr, " ");
169 s_append(negstr, negotiable[i]->name);
170 s_append(negstr, "@");
171 s_append(negstr, _strfindattr(k->attr, "dom"));
172 closekey(k);
173 }
174 _freeattr(anew);
175 }
176 if(m == 0){
177 s_free(negstr);
178 return failure(fss, Enegotiation);
179 }
180 i = s_len(negstr)+1;
181 if(*n < i){
182 s_free(negstr);
183 return toosmall(fss, i);
184 }
185 *n = i;
186 memmove(a, s_to_c(negstr), i+1);
187 fss->phase = SNeedProto;
188 s_free(negstr);
189 return RpcOk;
190
191 case CHaveProto:
192 i = strlen(s->subproto->name)+1+s_len(s->subdom)+1;
193 if(*n < i)
194 return toosmall(fss, i);
195 *n = i;
196 strcpy(a, s->subproto->name);
197 strcat(a, " ");
198 strcat(a, s_to_c(s->subdom));
199 if(s->version == 1)
200 fss->phase = CRelay;
201 else
202 fss->phase = CNeedOK;
203 return RpcOk;
204
205 case SHaveOK:
206 i = 3;
207 if(*n < i)
208 return toosmall(fss, i);
209 *n = i;
210 strcpy(a, "OK");
211 fss->phase = SRelay;
212 return RpcOk;
213
214 case CRelay:
215 case SRelay:
216 ophase = s->subfss.phase;
217 ret = (*s->subproto->read)(&s->subfss, a, n);
218 rpcrdwrlog(&s->subfss, "read", *n, ophase, ret);
219 return passret(fss, s, ret);
220 }
221 }
222
223 static char*
getdom(char * p)224 getdom(char *p)
225 {
226 p = strchr(p, '@');
227 if(p == nil)
228 return "";
229 return p+1;
230 }
231
232 static Proto*
findneg(char * name)233 findneg(char *name)
234 {
235 int i, len;
236 char *p;
237
238 if(p = strchr(name, '@'))
239 len = p-name;
240 else
241 len = strlen(name);
242
243 for(i=0; i<nelem(negotiable); i++)
244 if(strncmp(negotiable[i]->name, name, len) == 0 && negotiable[i]->name[len] == 0)
245 return negotiable[i];
246 return nil;
247 }
248
249 static int
p9anywrite(Fsstate * fss,void * va,uint n)250 p9anywrite(Fsstate *fss, void *va, uint n)
251 {
252 char *a, *dom, *user, *token[20];
253 int asking, i, m, ophase, ret;
254 Attr *anew, *anewsf, *attr;
255 Key *k;
256 Keyinfo ki;
257 Proto *p;
258 State *s;
259
260 s = fss->ps;
261 a = va;
262 switch(fss->phase){
263 default:
264 return phaseerror(fss, "write");
265
266 case CNeedProtos:
267 if(n==0 || a[n-1] != '\0')
268 return toosmall(fss, 2048);
269 a = estrdup(a);
270 m = tokenize(a, token, nelem(token));
271 if(m > 0 && strncmp(token[0], "v.", 2) == 0){
272 s->version = atoi(token[0]+2);
273 if(s->version != 2){
274 free(a);
275 return failure(fss, "unknown version of p9any");
276 }
277 }
278
279 /*
280 * look for a key
281 */
282 anew = _delattr(_delattr(_copyattr(fss->attr), "proto"), "role");
283 anewsf = _delattr(_copyattr(anew), "user");
284 user = _strfindattr(anew, "user");
285 k = nil;
286 p = nil;
287 dom = nil;
288 for(i=(s->version==1?0:1); i<m; i++){
289 p = findneg(token[i]);
290 if(p == nil)
291 continue;
292 dom = getdom(token[i]);
293 ret = RpcFailure;
294 mkkeyinfo(&ki, fss, nil);
295 if(user==nil || strcmp(user, fss->sysuser)==0){
296 ki.attr = anewsf;
297 ki.user = nil;
298 ret = findkey(&k, &ki, "proto=%q dom=%q role=speakfor %s",
299 p->name, dom, p->keyprompt);
300 }
301 if(ret == RpcFailure){
302 ki.attr = anew;
303 ki.user = fss->sysuser;
304 ret = findkey(&k, &ki,
305 "proto=%q dom=%q role=client %s",
306 p->name, dom, p->keyprompt);
307 }
308 if(ret == RpcConfirm){
309 free(a);
310 return ret;
311 }
312 if(ret == RpcOk)
313 break;
314 }
315 _freeattr(anewsf);
316
317 /*
318 * no acceptable key, go through the proto@domains one at a time.
319 */
320 asking = 0;
321 if(k == nil){
322 while(!asking && s->keyasked < m){
323 p = findneg(token[s->keyasked]);
324 if(p == nil){
325 s->keyasked++;
326 continue;
327 }
328 dom = getdom(token[s->keyasked]);
329 mkkeyinfo(&ki, fss, nil);
330 ki.attr = anew;
331 ret = findkey(&k, &ki,
332 "proto=%q dom=%q role=client %s",
333 p->name, dom, p->keyprompt);
334 s->keyasked++;
335 if(ret == RpcNeedkey){
336 asking = 1;
337 break;
338 }
339 }
340 }
341 if(k == nil){
342 free(a);
343 _freeattr(anew);
344 if(asking)
345 return RpcNeedkey;
346 else if(s->keyasked)
347 return failure(fss, nil);
348 else
349 return failure(fss, Enegotiation);
350 }
351 s->subdom = s_copy(dom);
352 s->subproto = p;
353 free(a);
354 _freeattr(anew);
355 setupfss(fss, s, k);
356 closekey(k);
357 ret = (*s->subproto->init)(p, &s->subfss);
358 rpcstartlog(s->subfss.attr, &s->subfss, ret);
359 if(ret == RpcOk)
360 fss->phase = CHaveProto;
361 return passret(fss, s, ret);
362
363 case SNeedProto:
364 if(n==0 || a[n-1] != '\0')
365 return toosmall(fss, n+1);
366 a = estrdup(a);
367 m = tokenize(a, token, nelem(token));
368 if(m != 2){
369 free(a);
370 return failure(fss, Ebadarg);
371 }
372 p = findneg(token[0]);
373 if(p == nil){
374 free(a);
375 return failure(fss, Enegotiation);
376 }
377 attr = _delattr(_copyattr(fss->attr), "proto");
378 mkkeyinfo(&ki, fss, nil);
379 ki.attr = attr;
380 ki.user = nil;
381 ret = findkey(&k, &ki, "proto=%q dom=%q role=server", token[0], token[1]);
382 free(a);
383 _freeattr(attr);
384 if(ret == RpcConfirm)
385 return ret;
386 if(ret != RpcOk)
387 return failure(fss, Enegotiation);
388 s->subproto = p;
389 setupfss(fss, s, k);
390 closekey(k);
391 ret = (*s->subproto->init)(p, &s->subfss);
392 if(ret == RpcOk){
393 if(s->version == 1)
394 fss->phase = SRelay;
395 else
396 fss->phase = SHaveOK;
397 }
398 return passret(fss, s, ret);
399
400 case CNeedOK:
401 if(n < 3)
402 return toosmall(fss, 3);
403 if(strcmp("OK", a) != 0)
404 return failure(fss, "server gave up");
405 fss->phase = CRelay;
406 return RpcOk;
407
408 case CRelay:
409 case SRelay:
410 ophase = s->subfss.phase;
411 ret = (*s->subproto->write)(&s->subfss, va, n);
412 rpcrdwrlog(&s->subfss, "write", n, ophase, ret);
413 return passret(fss, s, ret);
414 }
415 }
416
417 Proto p9any =
418 {
419 .name= "p9any",
420 .init= p9anyinit,
421 .write= p9anywrite,
422 .read= p9anyread,
423 .close= p9anyclose,
424 };
425