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