xref: /plan9-contrib/sys/src/cmd/ip/httpd/websocket.c (revision 5367413097a46c6f6fc69aad7c8df91da69cc758)
1*53674130SDavid du Colombier /* Copyright © 2013-2014 David Hoskin <root@davidrhoskin.com> */
2*53674130SDavid du Colombier 
3*53674130SDavid du Colombier #include <u.h>
4*53674130SDavid du Colombier #include <libc.h>
5*53674130SDavid du Colombier #include <thread.h>
6*53674130SDavid du Colombier #include <bio.h>
7*53674130SDavid du Colombier #include <mp.h>
8*53674130SDavid du Colombier #include <libsec.h>
9*53674130SDavid du Colombier #include <auth.h>
10*53674130SDavid du Colombier #include "httpd.h"
11*53674130SDavid du Colombier #include "httpsrv.h"
12*53674130SDavid du Colombier 
13*53674130SDavid du Colombier enum
14*53674130SDavid du Colombier {
15*53674130SDavid du Colombier 	/* misc parameters */
16*53674130SDavid du Colombier 	MAXHDRS = 64,
17*53674130SDavid du Colombier 	STACKSZ = 32768,
18*53674130SDavid du Colombier 	BUFSZ = 16384,
19*53674130SDavid du Colombier 	CHANBUF = 8,
20*53674130SDavid du Colombier 
21*53674130SDavid du Colombier 	/* packet types */
22*53674130SDavid du Colombier 	/* standard non-control frames */
23*53674130SDavid du Colombier 	Cont = 0x0,
24*53674130SDavid du Colombier 	Text = 0x1,
25*53674130SDavid du Colombier 	Binary = 0x2,
26*53674130SDavid du Colombier 	/* reserved non-control frames */
27*53674130SDavid du Colombier 	/* standard control frames */
28*53674130SDavid du Colombier 	Close = 0x8,
29*53674130SDavid du Colombier 	Ping = 0x9,
30*53674130SDavid du Colombier 	Pong = 0xA,
31*53674130SDavid du Colombier 	/* reserved control frames */
32*53674130SDavid du Colombier };
33*53674130SDavid du Colombier 
34*53674130SDavid du Colombier typedef struct Procio Procio;
35*53674130SDavid du Colombier struct Procio
36*53674130SDavid du Colombier {
37*53674130SDavid du Colombier 	Channel *c;
38*53674130SDavid du Colombier 	Biobuf *b;
39*53674130SDavid du Colombier 	int fd;
40*53674130SDavid du Colombier 	char **argv;
41*53674130SDavid du Colombier };
42*53674130SDavid du Colombier 
43*53674130SDavid du Colombier typedef struct Buf Buf;
44*53674130SDavid du Colombier struct Buf
45*53674130SDavid du Colombier {
46*53674130SDavid du Colombier 	uchar *buf;
47*53674130SDavid du Colombier 	long n;
48*53674130SDavid du Colombier };
49*53674130SDavid du Colombier 
50*53674130SDavid du Colombier typedef struct Wspkt Wspkt;
51*53674130SDavid du Colombier struct Wspkt
52*53674130SDavid du Colombier {
53*53674130SDavid du Colombier 	Buf;
54*53674130SDavid du Colombier 	int type;
55*53674130SDavid du Colombier };
56*53674130SDavid du Colombier 
57*53674130SDavid du Colombier /* XXX The default was not enough, but this is just a guess. at least 2*sizeof Biobuf */
58*53674130SDavid du Colombier int mainstacksize = 128*1024;
59*53674130SDavid du Colombier 
60*53674130SDavid du Colombier const char wsnoncekey[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
61*53674130SDavid du Colombier const char wsversion[] = "13";
62*53674130SDavid du Colombier 
63*53674130SDavid du Colombier HSPairs *
parseheaders(char * headers)64*53674130SDavid du Colombier parseheaders(char *headers)
65*53674130SDavid du Colombier {
66*53674130SDavid du Colombier 	char *hdrlines[MAXHDRS], *kv[2];
67*53674130SDavid du Colombier 	HSPairs *h, *t, *tmp;
68*53674130SDavid du Colombier 	int nhdr;
69*53674130SDavid du Colombier 	int i;
70*53674130SDavid du Colombier 
71*53674130SDavid du Colombier 	h = t = nil;
72*53674130SDavid du Colombier 
73*53674130SDavid du Colombier 	nhdr = getfields(headers, hdrlines, MAXHDRS, 1, "\r\n");
74*53674130SDavid du Colombier 
75*53674130SDavid du Colombier 	/*
76*53674130SDavid du Colombier 	* XXX I think leading whitespaces signifies a continuation line.
77*53674130SDavid du Colombier 	* Skip the first line, or else getfields(..., " ") picks up the GET.
78*53674130SDavid du Colombier 	*/
79*53674130SDavid du Colombier 	for(i = 1; i < nhdr; ++i){
80*53674130SDavid du Colombier 
81*53674130SDavid du Colombier 		if(hdrlines[i] == nil)
82*53674130SDavid du Colombier 			continue;
83*53674130SDavid du Colombier 
84*53674130SDavid du Colombier 		getfields(hdrlines[i], kv, 2, 1, ": \t");
85*53674130SDavid du Colombier 
86*53674130SDavid du Colombier 		tmp = malloc(sizeof(HSPairs));
87*53674130SDavid du Colombier 		if(tmp == nil)
88*53674130SDavid du Colombier 			goto cleanup;
89*53674130SDavid du Colombier 
90*53674130SDavid du Colombier 		tmp->s = kv[0];
91*53674130SDavid du Colombier 		tmp->t = kv[1];
92*53674130SDavid du Colombier 
93*53674130SDavid du Colombier 		if(h == nil){
94*53674130SDavid du Colombier 			h = t = tmp;
95*53674130SDavid du Colombier 		}else{
96*53674130SDavid du Colombier 			t->next = tmp;
97*53674130SDavid du Colombier 			t = tmp;
98*53674130SDavid du Colombier 		}
99*53674130SDavid du Colombier 		tmp->next = nil;
100*53674130SDavid du Colombier 	}
101*53674130SDavid du Colombier 
102*53674130SDavid du Colombier 	return h;
103*53674130SDavid du Colombier 
104*53674130SDavid du Colombier cleanup:
105*53674130SDavid du Colombier 	for(t = h->next; h != nil; h = t, t = h->next)
106*53674130SDavid du Colombier 		free(h);
107*53674130SDavid du Colombier 	return nil;
108*53674130SDavid du Colombier }
109*53674130SDavid du Colombier 
110*53674130SDavid du Colombier char *
getheader(HSPairs * h,const char * k)111*53674130SDavid du Colombier getheader(HSPairs *h, const char *k)
112*53674130SDavid du Colombier {
113*53674130SDavid du Colombier 	for(; h != nil; h = h->next)
114*53674130SDavid du Colombier 		if(cistrcmp(h->s, k) == 0)
115*53674130SDavid du Colombier 			return h->t;
116*53674130SDavid du Colombier 	return nil;
117*53674130SDavid du Colombier }
118*53674130SDavid du Colombier 
119*53674130SDavid du Colombier int
failhdr(HConnect * c,int code,const char * status,const char * message)120*53674130SDavid du Colombier failhdr(HConnect *c, int code, const char *status, const char *message)
121*53674130SDavid du Colombier {
122*53674130SDavid du Colombier 	Hio *o;
123*53674130SDavid du Colombier 
124*53674130SDavid du Colombier 	o = &c->hout;
125*53674130SDavid du Colombier 	hprint(o, "%s %d %s\r\n", hversion, code, status);
126*53674130SDavid du Colombier 	hprint(o, "Server: Plan9\r\n");
127*53674130SDavid du Colombier 	hprint(o, "Date: %D\r\n", time(nil));
128*53674130SDavid du Colombier 	hprint(o, "Content-type: text/html\r\n");
129*53674130SDavid du Colombier 	hprint(o, "\r\n");
130*53674130SDavid du Colombier 	hprint(o, "<html><head><title>%d %s</title></head>\n", code, status);
131*53674130SDavid du Colombier 	hprint(o, "<body><h1>%d %s</h1>\n", code, status);
132*53674130SDavid du Colombier 	hprint(o, "<p>Failed to establish websocket connection: %s\n", message);
133*53674130SDavid du Colombier 	hprint(o, "</body></html>\n");
134*53674130SDavid du Colombier 	hflush(o);
135*53674130SDavid du Colombier 	return 0;
136*53674130SDavid du Colombier }
137*53674130SDavid du Colombier 
138*53674130SDavid du Colombier void
okhdr(HConnect * c,const char * wshashedkey,const char * proto)139*53674130SDavid du Colombier okhdr(HConnect *c, const char *wshashedkey, const char *proto)
140*53674130SDavid du Colombier {
141*53674130SDavid du Colombier 	Hio *o;
142*53674130SDavid du Colombier 
143*53674130SDavid du Colombier 	o = &c->hout;
144*53674130SDavid du Colombier 	hprint(o, "%s 101 Switching Protocols\r\n", hversion);
145*53674130SDavid du Colombier 	hprint(o, "Upgrade: websocket\r\n");
146*53674130SDavid du Colombier 	hprint(o, "Connection: upgrade\r\n");
147*53674130SDavid du Colombier 	hprint(o, "Sec-WebSocket-Accept: %s\r\n", wshashedkey);
148*53674130SDavid du Colombier 	if(proto != nil)
149*53674130SDavid du Colombier 		hprint(o, "Sec-WebSocket-Protocol: %s\r\n", proto);
150*53674130SDavid du Colombier 	/* we don't handle extensions */
151*53674130SDavid du Colombier 	hprint(o, "\r\n");
152*53674130SDavid du Colombier 	hflush(o);
153*53674130SDavid du Colombier }
154*53674130SDavid du Colombier 
155*53674130SDavid du Colombier int
testwsversion(const char * vs)156*53674130SDavid du Colombier testwsversion(const char *vs)
157*53674130SDavid du Colombier {
158*53674130SDavid du Colombier 	int i, n;
159*53674130SDavid du Colombier 	char *v[16];
160*53674130SDavid du Colombier 
161*53674130SDavid du Colombier 	n = getfields(vs, v, 16, 1, "\t ,");
162*53674130SDavid du Colombier 	for(i = 0; i < n; ++i)
163*53674130SDavid du Colombier 		if(strcmp(v[i], wsversion) == 0)
164*53674130SDavid du Colombier 			return 1;
165*53674130SDavid du Colombier 	return 0;
166*53674130SDavid du Colombier }
167*53674130SDavid du Colombier 
168*53674130SDavid du Colombier uvlong
getbe(uchar * t,int w)169*53674130SDavid du Colombier getbe(uchar *t, int w)
170*53674130SDavid du Colombier {
171*53674130SDavid du Colombier 	uint i;
172*53674130SDavid du Colombier 	uvlong r;
173*53674130SDavid du Colombier 
174*53674130SDavid du Colombier 	r = 0;
175*53674130SDavid du Colombier 	for(i = 0; i < w; i++)
176*53674130SDavid du Colombier 		r = r<<8 | t[i];
177*53674130SDavid du Colombier 	return r;
178*53674130SDavid du Colombier }
179*53674130SDavid du Colombier 
180*53674130SDavid du Colombier int
Bgetbe(Biobuf * b,uvlong * u,int sz)181*53674130SDavid du Colombier Bgetbe(Biobuf *b, uvlong *u, int sz)
182*53674130SDavid du Colombier {
183*53674130SDavid du Colombier 	uchar buf[8];
184*53674130SDavid du Colombier 
185*53674130SDavid du Colombier 	if(Bread(b, buf, sz) != sz)
186*53674130SDavid du Colombier 		return -1;
187*53674130SDavid du Colombier 
188*53674130SDavid du Colombier 	*u = getbe(buf, sz);
189*53674130SDavid du Colombier 	return 1;
190*53674130SDavid du Colombier }
191*53674130SDavid du Colombier 
192*53674130SDavid du Colombier int
sendpkt(Biobuf * b,Wspkt * pkt)193*53674130SDavid du Colombier sendpkt(Biobuf *b, Wspkt *pkt)
194*53674130SDavid du Colombier {
195*53674130SDavid du Colombier 	uchar hdr[2+8];
196*53674130SDavid du Colombier 	long hdrsz, len;
197*53674130SDavid du Colombier 
198*53674130SDavid du Colombier 	hdr[0] = 0x80 | pkt->type;
199*53674130SDavid du Colombier 	len = pkt->n;
200*53674130SDavid du Colombier 
201*53674130SDavid du Colombier 	/* XXX should use putbe(). */
202*53674130SDavid du Colombier 	if(len >= (1 << 16)){
203*53674130SDavid du Colombier 		hdrsz = 2 + 8;
204*53674130SDavid du Colombier 		hdr[1] = 127;
205*53674130SDavid du Colombier 		hdr[2] = hdr[3] = hdr[4] = hdr[5] = 0;
206*53674130SDavid du Colombier 		hdr[6] = len >> 24;
207*53674130SDavid du Colombier 		hdr[7] = len >> 16;
208*53674130SDavid du Colombier 		hdr[8] = len >> 8;
209*53674130SDavid du Colombier 		hdr[9] = len >> 0;
210*53674130SDavid du Colombier 	}else if(len >= 126){
211*53674130SDavid du Colombier 		hdrsz = 2 + 2;
212*53674130SDavid du Colombier 		hdr[1] = 126;
213*53674130SDavid du Colombier 		hdr[2] = len >> 8;
214*53674130SDavid du Colombier 		hdr[3]= len >> 0;
215*53674130SDavid du Colombier 	}else{
216*53674130SDavid du Colombier 		hdrsz = 2;
217*53674130SDavid du Colombier 		hdr[1] = len;
218*53674130SDavid du Colombier 	}
219*53674130SDavid du Colombier 
220*53674130SDavid du Colombier 	if(Bwrite(b, hdr, hdrsz) != hdrsz)
221*53674130SDavid du Colombier 		return -1;
222*53674130SDavid du Colombier 	if(Bwrite(b, pkt->buf, len) != len)
223*53674130SDavid du Colombier 		return -1;
224*53674130SDavid du Colombier 	if(Bflush(b) < 0)
225*53674130SDavid du Colombier 		return -1;
226*53674130SDavid du Colombier 
227*53674130SDavid du Colombier 	return 0;
228*53674130SDavid du Colombier }
229*53674130SDavid du Colombier 
230*53674130SDavid du Colombier int
recvpkt(Wspkt * pkt,Biobuf * b)231*53674130SDavid du Colombier recvpkt(Wspkt *pkt, Biobuf *b)
232*53674130SDavid du Colombier {
233*53674130SDavid du Colombier 	long x;
234*53674130SDavid du Colombier 	int masked;
235*53674130SDavid du Colombier 	uchar mask[4];
236*53674130SDavid du Colombier 
237*53674130SDavid du Colombier 	pkt->type = Bgetc(b);
238*53674130SDavid du Colombier 	if(pkt->type < 0){
239*53674130SDavid du Colombier 		return -1;
240*53674130SDavid du Colombier 	}
241*53674130SDavid du Colombier 	/* Strip FIN/continuation bit. */
242*53674130SDavid du Colombier 	pkt->type &= 0x0F;
243*53674130SDavid du Colombier 
244*53674130SDavid du Colombier 	pkt->n = Bgetc(b);
245*53674130SDavid du Colombier 	if(pkt->n < 0){
246*53674130SDavid du Colombier 		return -1;
247*53674130SDavid du Colombier 	}
248*53674130SDavid du Colombier 	masked = pkt->n & 0x80;
249*53674130SDavid du Colombier 	pkt->n &= 0x7F;
250*53674130SDavid du Colombier 
251*53674130SDavid du Colombier 	if(pkt->n >= 127){
252*53674130SDavid du Colombier 		if(Bgetbe(b, (uvlong *)&pkt->n, 8) != 1)
253*53674130SDavid du Colombier 			return -1;
254*53674130SDavid du Colombier 	}else if(pkt->n == 126){
255*53674130SDavid du Colombier 		if(Bgetbe(b, (uvlong *)&pkt->n, 2) != 1)
256*53674130SDavid du Colombier 			return -1;
257*53674130SDavid du Colombier 	}
258*53674130SDavid du Colombier 
259*53674130SDavid du Colombier 	if(masked){
260*53674130SDavid du Colombier 		if(Bread(b, mask, 4) != 4)
261*53674130SDavid du Colombier 			return -1;
262*53674130SDavid du Colombier 	}
263*53674130SDavid du Colombier 	/* allocate appropriate buffer */
264*53674130SDavid du Colombier 	if(pkt->n > BUFSZ){
265*53674130SDavid du Colombier 		/*
266*53674130SDavid du Colombier 		* buffer is unacceptably large!
267*53674130SDavid du Colombier 		* XXX this should close the connection with a specific error code.
268*53674130SDavid du Colombier 		* See websocket spec.
269*53674130SDavid du Colombier 		*/
270*53674130SDavid du Colombier 		return -1;
271*53674130SDavid du Colombier 	}else if(pkt->n == 0){
272*53674130SDavid du Colombier 		pkt->buf = nil;
273*53674130SDavid du Colombier 		return 1;
274*53674130SDavid du Colombier 	}else{
275*53674130SDavid du Colombier 		pkt->buf = malloc(pkt->n);
276*53674130SDavid du Colombier 		if(pkt->buf == nil)
277*53674130SDavid du Colombier 			return -1;
278*53674130SDavid du Colombier 
279*53674130SDavid du Colombier 		if(Bread(b, pkt->buf, pkt->n) != pkt->n){
280*53674130SDavid du Colombier 			free(pkt->buf);
281*53674130SDavid du Colombier 			return -1;
282*53674130SDavid du Colombier 		}
283*53674130SDavid du Colombier 
284*53674130SDavid du Colombier 		if(masked)
285*53674130SDavid du Colombier 			for(x = 0; x < pkt->n; ++x)
286*53674130SDavid du Colombier 				pkt->buf[x] ^= mask[x % 4];
287*53674130SDavid du Colombier 
288*53674130SDavid du Colombier 		return 1;
289*53674130SDavid du Colombier 	}
290*53674130SDavid du Colombier }
291*53674130SDavid du Colombier 
292*53674130SDavid du Colombier void
wsreadproc(void * arg)293*53674130SDavid du Colombier wsreadproc(void *arg)
294*53674130SDavid du Colombier {
295*53674130SDavid du Colombier 	Procio *pio;
296*53674130SDavid du Colombier 	Channel *c;
297*53674130SDavid du Colombier 	Biobuf *b;
298*53674130SDavid du Colombier 	Wspkt pkt;
299*53674130SDavid du Colombier 
300*53674130SDavid du Colombier 	pio = (Procio *)arg;
301*53674130SDavid du Colombier 	c = pio->c;
302*53674130SDavid du Colombier 	b = pio->b;
303*53674130SDavid du Colombier 
304*53674130SDavid du Colombier 	for(;;){
305*53674130SDavid du Colombier 		if(recvpkt(&pkt, b) < 0)
306*53674130SDavid du Colombier 			break;
307*53674130SDavid du Colombier 		if(send(c, &pkt) < 0){
308*53674130SDavid du Colombier 			free(pkt.buf);
309*53674130SDavid du Colombier 			break;
310*53674130SDavid du Colombier 		}
311*53674130SDavid du Colombier 	}
312*53674130SDavid du Colombier 
313*53674130SDavid du Colombier 	chanclose(c);
314*53674130SDavid du Colombier 	threadexits(nil);
315*53674130SDavid du Colombier }
316*53674130SDavid du Colombier 
317*53674130SDavid du Colombier void
wswriteproc(void * arg)318*53674130SDavid du Colombier wswriteproc(void *arg)
319*53674130SDavid du Colombier {
320*53674130SDavid du Colombier 	Procio *pio;
321*53674130SDavid du Colombier 	Channel *c;
322*53674130SDavid du Colombier 	Biobuf *b;
323*53674130SDavid du Colombier 	Wspkt pkt;
324*53674130SDavid du Colombier 
325*53674130SDavid du Colombier 	pio = (Procio *)arg;
326*53674130SDavid du Colombier 	c = pio->c;
327*53674130SDavid du Colombier 	b = pio->b;
328*53674130SDavid du Colombier 
329*53674130SDavid du Colombier 	for(;;){
330*53674130SDavid du Colombier 		if(recv(c, &pkt) < 0)
331*53674130SDavid du Colombier 			break;
332*53674130SDavid du Colombier 		if(sendpkt(b, &pkt) < 0){
333*53674130SDavid du Colombier 			free(pkt.buf);
334*53674130SDavid du Colombier 			break;
335*53674130SDavid du Colombier 		}
336*53674130SDavid du Colombier 		free(pkt.buf);
337*53674130SDavid du Colombier 	}
338*53674130SDavid du Colombier 
339*53674130SDavid du Colombier 	chanclose(c);
340*53674130SDavid du Colombier 	threadexits(nil);
341*53674130SDavid du Colombier }
342*53674130SDavid du Colombier 
343*53674130SDavid du Colombier void
pipereadproc(void * arg)344*53674130SDavid du Colombier pipereadproc(void *arg)
345*53674130SDavid du Colombier {
346*53674130SDavid du Colombier 	Procio *pio;
347*53674130SDavid du Colombier 	Channel *c;
348*53674130SDavid du Colombier 	int fd;
349*53674130SDavid du Colombier 	Buf b;
350*53674130SDavid du Colombier 
351*53674130SDavid du Colombier 	pio = (Procio *)arg;
352*53674130SDavid du Colombier 	c = pio->c;
353*53674130SDavid du Colombier 	fd = pio->fd;
354*53674130SDavid du Colombier 
355*53674130SDavid du Colombier 	for(;;){
356*53674130SDavid du Colombier 		b.buf = malloc(BUFSZ);
357*53674130SDavid du Colombier 		if(b.buf == nil)
358*53674130SDavid du Colombier 			break;
359*53674130SDavid du Colombier 		b.n = read(fd, b.buf, BUFSZ);
360*53674130SDavid du Colombier 		if(b.n < 1)
361*53674130SDavid du Colombier 			break;
362*53674130SDavid du Colombier 		if(send(c, &b) < 0)
363*53674130SDavid du Colombier 			break;
364*53674130SDavid du Colombier 	}
365*53674130SDavid du Colombier 
366*53674130SDavid du Colombier 	free(b.buf);
367*53674130SDavid du Colombier 	chanclose(c);
368*53674130SDavid du Colombier 	threadexits(nil);
369*53674130SDavid du Colombier }
370*53674130SDavid du Colombier 
371*53674130SDavid du Colombier void
pipewriteproc(void * arg)372*53674130SDavid du Colombier pipewriteproc(void *arg)
373*53674130SDavid du Colombier {
374*53674130SDavid du Colombier 	Procio *pio;
375*53674130SDavid du Colombier 	Channel *c;
376*53674130SDavid du Colombier 	int fd;
377*53674130SDavid du Colombier 	Buf b;
378*53674130SDavid du Colombier 
379*53674130SDavid du Colombier 	pio = (Procio *)arg;
380*53674130SDavid du Colombier 	c = pio->c;
381*53674130SDavid du Colombier 	fd = pio->fd;
382*53674130SDavid du Colombier 
383*53674130SDavid du Colombier 	for(;;){
384*53674130SDavid du Colombier 		if(recv(c, &b) != 1)
385*53674130SDavid du Colombier 			break;
386*53674130SDavid du Colombier 		if(write(fd, b.buf, b.n) != b.n){
387*53674130SDavid du Colombier 			free(b.buf);
388*53674130SDavid du Colombier 			break;
389*53674130SDavid du Colombier 		}
390*53674130SDavid du Colombier 		free(b.buf);
391*53674130SDavid du Colombier 	}
392*53674130SDavid du Colombier 
393*53674130SDavid du Colombier 	chanclose(c);
394*53674130SDavid du Colombier 	threadexits(nil);
395*53674130SDavid du Colombier }
396*53674130SDavid du Colombier 
397*53674130SDavid du Colombier void
mountproc(void * arg)398*53674130SDavid du Colombier mountproc(void *arg)
399*53674130SDavid du Colombier {
400*53674130SDavid du Colombier 	Procio *pio;
401*53674130SDavid du Colombier 	int fd, i;
402*53674130SDavid du Colombier 	char **argv;
403*53674130SDavid du Colombier 
404*53674130SDavid du Colombier 	pio = (Procio *)arg;
405*53674130SDavid du Colombier 	fd = pio->fd;
406*53674130SDavid du Colombier 	argv = pio->argv;
407*53674130SDavid du Colombier 
408*53674130SDavid du Colombier 	for(i = 0; i < 20; ++i){
409*53674130SDavid du Colombier 		if(i != fd)
410*53674130SDavid du Colombier 			close(i);
411*53674130SDavid du Colombier 	}
412*53674130SDavid du Colombier 
413*53674130SDavid du Colombier 	newns("none", nil);
414*53674130SDavid du Colombier 
415*53674130SDavid du Colombier 	if(mount(fd, -1, "/dev/", MBEFORE, "") == -1)
416*53674130SDavid du Colombier 		sysfatal("mount failed: %r");
417*53674130SDavid du Colombier 
418*53674130SDavid du Colombier 	procexec(nil, argv[0], argv);
419*53674130SDavid du Colombier }
420*53674130SDavid du Colombier 
421*53674130SDavid du Colombier void
echoproc(void * arg)422*53674130SDavid du Colombier echoproc(void *arg)
423*53674130SDavid du Colombier {
424*53674130SDavid du Colombier 	Procio *pio;
425*53674130SDavid du Colombier 	int fd;
426*53674130SDavid du Colombier 	char buf[1024];
427*53674130SDavid du Colombier 	int n;
428*53674130SDavid du Colombier 
429*53674130SDavid du Colombier 	pio = (Procio *)arg;
430*53674130SDavid du Colombier 	fd = pio->fd;
431*53674130SDavid du Colombier 
432*53674130SDavid du Colombier 	for(;;){
433*53674130SDavid du Colombier 		n = read(fd, buf, 1024);
434*53674130SDavid du Colombier 		if(n > 0)
435*53674130SDavid du Colombier 			write(fd, buf, n);
436*53674130SDavid du Colombier 	}
437*53674130SDavid du Colombier }
438*53674130SDavid du Colombier 
439*53674130SDavid du Colombier int
wscheckhdr(HConnect * c)440*53674130SDavid du Colombier wscheckhdr(HConnect *c)
441*53674130SDavid du Colombier {
442*53674130SDavid du Colombier 	HSPairs *hdrs;
443*53674130SDavid du Colombier 	char *s, *wsclientkey;
444*53674130SDavid du Colombier 	char *rawproto;
445*53674130SDavid du Colombier 	char *proto;
446*53674130SDavid du Colombier 	char wscatkey[64];
447*53674130SDavid du Colombier 	uchar wshashedkey[SHA1dlen];
448*53674130SDavid du Colombier 	char wsencoded[32];
449*53674130SDavid du Colombier 
450*53674130SDavid du Colombier 	if(strcmp(c->req.meth, "GET") != 0)
451*53674130SDavid du Colombier 		return hunallowed(c, "GET");
452*53674130SDavid du Colombier 
453*53674130SDavid du Colombier 	//return failhdr(c, 403, "Forbidden", "my hair is on fire");
454*53674130SDavid du Colombier 
455*53674130SDavid du Colombier 	hdrs = parseheaders((char *)c->header);
456*53674130SDavid du Colombier 
457*53674130SDavid du Colombier 	s = getheader(hdrs, "upgrade");
458*53674130SDavid du Colombier 	if(s == nil || !cistrstr(s, "websocket"))
459*53674130SDavid du Colombier 		return failhdr(c, 400, "Bad Request", "no <code>upgrade: websocket</code> header.");
460*53674130SDavid du Colombier 	s = getheader(hdrs, "connection");
461*53674130SDavid du Colombier 	if(s == nil || !cistrstr(s, "upgrade"))
462*53674130SDavid du Colombier 		return failhdr(c, 400, "Bad Request", "no <code>connection: upgrade</code> header.");
463*53674130SDavid du Colombier 	wsclientkey = getheader(hdrs, "sec-websocket-key");
464*53674130SDavid du Colombier 	if(wsclientkey == nil || strlen(wsclientkey) != 24)
465*53674130SDavid du Colombier 		return failhdr(c, 400, "Bad Request", "invalid websocket nonce key.");
466*53674130SDavid du Colombier 	s = getheader(hdrs, "sec-websocket-version");
467*53674130SDavid du Colombier 	if(s == nil || !testwsversion(s))
468*53674130SDavid du Colombier 		return failhdr(c, 426, "Upgrade Required", "could not match websocket version.");
469*53674130SDavid du Colombier 	/* XXX should get resource name */
470*53674130SDavid du Colombier 	rawproto = getheader(hdrs, "sec-websocket-protocol");
471*53674130SDavid du Colombier 	proto = rawproto;
472*53674130SDavid du Colombier 	/* XXX should test if proto is acceptable" */
473*53674130SDavid du Colombier 	/* should get sec-websocket-extensions */
474*53674130SDavid du Colombier 
475*53674130SDavid du Colombier 	/* OK, we seem to have a valid Websocket request. */
476*53674130SDavid du Colombier 
477*53674130SDavid du Colombier 	/* Hash websocket key. */
478*53674130SDavid du Colombier 	strcpy(wscatkey, wsclientkey);
479*53674130SDavid du Colombier 	strcat(wscatkey, wsnoncekey);
480*53674130SDavid du Colombier 	sha1((uchar *)wscatkey, strlen(wscatkey), wshashedkey, nil);
481*53674130SDavid du Colombier 	enc64(wsencoded, 32, wshashedkey, SHA1dlen);
482*53674130SDavid du Colombier 
483*53674130SDavid du Colombier 	okhdr(c, wsencoded, proto);
484*53674130SDavid du Colombier 	hflush(&c->hout);
485*53674130SDavid du Colombier 
486*53674130SDavid du Colombier 	/* We should now have an open Websocket connection. */
487*53674130SDavid du Colombier 
488*53674130SDavid du Colombier 	return 1;
489*53674130SDavid du Colombier }
490*53674130SDavid du Colombier 
491*53674130SDavid du Colombier int
dowebsock(void)492*53674130SDavid du Colombier dowebsock(void)
493*53674130SDavid du Colombier {
494*53674130SDavid du Colombier 	Biobuf bin, bout;
495*53674130SDavid du Colombier 	Wspkt pkt;
496*53674130SDavid du Colombier 	Buf buf;
497*53674130SDavid du Colombier 	int p[2];
498*53674130SDavid du Colombier 	Alt a[] = {
499*53674130SDavid du Colombier 	/*	c	v	op */
500*53674130SDavid du Colombier 		{nil, &pkt, CHANRCV},
501*53674130SDavid du Colombier 		{nil, &buf, CHANRCV},
502*53674130SDavid du Colombier 		{nil, nil, CHANEND},
503*53674130SDavid du Colombier 	};
504*53674130SDavid du Colombier 	Procio fromws, tows, frompipe, topipe;
505*53674130SDavid du Colombier 	Procio mountp, echop;
506*53674130SDavid du Colombier 	char *argv[] = {"/bin/rc", "-c", "ramfs && exec acme", nil};
507*53674130SDavid du Colombier 
508*53674130SDavid du Colombier 	fromws.c = chancreate(sizeof(Wspkt), CHANBUF);
509*53674130SDavid du Colombier 	tows.c = chancreate(sizeof(Wspkt), CHANBUF);
510*53674130SDavid du Colombier 	frompipe.c = chancreate(sizeof(Buf), CHANBUF);
511*53674130SDavid du Colombier 	topipe.c = chancreate(sizeof(Buf), CHANBUF);
512*53674130SDavid du Colombier 
513*53674130SDavid du Colombier 	a[0].c = fromws.c;
514*53674130SDavid du Colombier 	a[1].c = frompipe.c;
515*53674130SDavid du Colombier 
516*53674130SDavid du Colombier 	Binit(&bin, 0, OREAD);
517*53674130SDavid du Colombier 	Binit(&bout, 1, OWRITE);
518*53674130SDavid du Colombier 	fromws.b = &bin;
519*53674130SDavid du Colombier 	tows.b = &bout;
520*53674130SDavid du Colombier 
521*53674130SDavid du Colombier 	pipe(p);
522*53674130SDavid du Colombier 	//fd = create("/srv/weebtest", OWRITE, 0666);
523*53674130SDavid du Colombier 	//fprint(fd, "%d", p[0]);
524*53674130SDavid du Colombier 	//close(fd);
525*53674130SDavid du Colombier 	//close(p[0]);
526*53674130SDavid du Colombier 
527*53674130SDavid du Colombier 	frompipe.fd = p[1];
528*53674130SDavid du Colombier 	topipe.fd = p[1];
529*53674130SDavid du Colombier 
530*53674130SDavid du Colombier 	mountp.fd = echop.fd = p[0];
531*53674130SDavid du Colombier 	mountp.argv = argv;
532*53674130SDavid du Colombier 
533*53674130SDavid du Colombier 	proccreate(wsreadproc, &fromws, STACKSZ);
534*53674130SDavid du Colombier 	proccreate(wswriteproc, &tows, STACKSZ);
535*53674130SDavid du Colombier 	proccreate(pipereadproc, &frompipe, STACKSZ);
536*53674130SDavid du Colombier 	proccreate(pipewriteproc, &topipe, STACKSZ);
537*53674130SDavid du Colombier 
538*53674130SDavid du Colombier 	//proccreate(echoproc, &echop, STACKSZ);
539*53674130SDavid du Colombier 	procrfork(mountproc, &mountp, STACKSZ, RFNAMEG|RFFDG);
540*53674130SDavid du Colombier 
541*53674130SDavid du Colombier 	for(;;){
542*53674130SDavid du Colombier 		int i;
543*53674130SDavid du Colombier 
544*53674130SDavid du Colombier 		i = alt(a);
545*53674130SDavid du Colombier 		if(chanclosing(a[i].c) >= 0){
546*53674130SDavid du Colombier 			a[i].op = CHANNOP;
547*53674130SDavid du Colombier 			pkt.type = Close;
548*53674130SDavid du Colombier 			pkt.buf = nil;
549*53674130SDavid du Colombier 			pkt.n = 0;
550*53674130SDavid du Colombier 			send(tows.c, &pkt);
551*53674130SDavid du Colombier 			goto done;
552*53674130SDavid du Colombier 		}
553*53674130SDavid du Colombier 
554*53674130SDavid du Colombier 		switch(i){
555*53674130SDavid du Colombier 		case 0: /* from socket */
556*53674130SDavid du Colombier 			if(pkt.type == Ping){
557*53674130SDavid du Colombier 				pkt.type = Pong;
558*53674130SDavid du Colombier 				send(tows.c, &pkt);
559*53674130SDavid du Colombier 			}else if(pkt.type == Close){
560*53674130SDavid du Colombier 				send(tows.c, &pkt);
561*53674130SDavid du Colombier 				goto done;
562*53674130SDavid du Colombier 			}else{
563*53674130SDavid du Colombier 				send(topipe.c, &pkt.Buf);
564*53674130SDavid du Colombier 			}
565*53674130SDavid du Colombier 			break;
566*53674130SDavid du Colombier 		case 1: /* from pipe */
567*53674130SDavid du Colombier 			pkt.type = Binary;
568*53674130SDavid du Colombier 			pkt.Buf = buf;
569*53674130SDavid du Colombier 			send(tows.c, &pkt);
570*53674130SDavid du Colombier 			break;
571*53674130SDavid du Colombier 		default:
572*53674130SDavid du Colombier 			sysfatal("can't happen");
573*53674130SDavid du Colombier 		}
574*53674130SDavid du Colombier 	}
575*53674130SDavid du Colombier done:
576*53674130SDavid du Colombier 	return 1;
577*53674130SDavid du Colombier }
578*53674130SDavid du Colombier 
579*53674130SDavid du Colombier void
threadmain(int argc,char ** argv)580*53674130SDavid du Colombier threadmain(int argc, char **argv)
581*53674130SDavid du Colombier {
582*53674130SDavid du Colombier 	HConnect *c;
583*53674130SDavid du Colombier 
584*53674130SDavid du Colombier 	c = init(argc, argv);
585*53674130SDavid du Colombier 	if(hparseheaders(c, HSTIMEOUT) >= 0)
586*53674130SDavid du Colombier 		if(wscheckhdr(c) >= 0)
587*53674130SDavid du Colombier 			dowebsock();
588*53674130SDavid du Colombier 
589*53674130SDavid du Colombier 	threadexitsall(nil);
590*53674130SDavid du Colombier }
591