Blob


1 /*
2 * This is free and unencumbered software released into the public domain.
3 *
4 * Anyone is free to copy, modify, publish, use, compile, sell, or
5 * distribute this software, either in source code form or as a compiled
6 * binary, for any purpose, commercial or non-commercial, and by any
7 * means.
8 *
9 * In jurisdictions that recognize copyright laws, the author or authors
10 * of this software dedicate any and all copyright interest in the
11 * software to the public domain. We make this dedication for the benefit
12 * of the public at large and to the detriment of our heirs and
13 * successors. We intend this dedication to be an overt act of
14 * relinquishment in perpetuity of all present and future rights to this
15 * software under copyright law.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
26 #include "config.h"
28 #include <arpa/inet.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdint.h>
32 #include <string.h>
33 #include <sha1.h>
35 #include "bufio.h"
36 #include "http.h"
37 #include "ws.h"
39 #define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
41 static int
42 tob64(unsigned char ch)
43 {
44 if (ch < 26)
45 return ('A' + ch);
46 ch -= 26;
47 if (ch < 26)
48 return ('a' + ch);
49 ch -= 26;
50 if (ch < 10)
51 return ('0' + ch);
52 ch -= 10;
53 if (ch == 0)
54 return ('+');
55 if (ch == 1)
56 return ('/');
57 errno = EINVAL;
58 return (-1);
59 }
61 static int
62 b64encode(unsigned char *in, size_t ilen, char *out, size_t olen)
63 {
64 int r;
66 #define SET(x) { \
67 if ((r = tob64((x) & 0x3F)) == -1) \
68 return (-1); \
69 *out++ = r; \
70 }
72 while (ilen > 0) {
73 if (olen < 4) {
74 errno = ENOSPC;
75 return (-1);
76 }
77 olen -= 4;
79 switch (ilen) {
80 case 1:
81 SET(in[0] >> 2);
82 SET(in[0] << 4);
83 *out++ = '=';
84 *out++ = '=';
85 ilen = 0;
86 break;
87 case 2:
88 SET(in[0] >> 2);
89 SET(in[0] << 4 | in[1] >> 4);
90 SET(in[1] << 2);
91 *out++ = '=';
92 ilen = 0;
93 break;
94 default:
95 SET(in[0] >> 2);
96 SET(in[0] << 4 | in[1] >> 4);
97 SET(in[1] << 2 | in[2] >> 6);
98 SET(in[2]);
99 ilen -= 3;
100 in += 3;
101 break;
105 #undef SET
107 if (olen < 1) {
108 errno = ENOSPC;
109 return (-1);
111 *out = '\0';
112 return (0);
115 int
116 ws_accept_hdr(const char *secret, char *out, size_t olen)
118 SHA1_CTX ctx;
119 uint8_t hash[SHA1_DIGEST_LENGTH];
121 SHA1Init(&ctx);
122 SHA1Update(&ctx, secret, strlen(secret));
123 SHA1Update(&ctx, WS_GUID, strlen(WS_GUID));
124 SHA1Final(hash, &ctx);
126 return (b64encode(hash, sizeof(hash), out, olen));
129 int
130 ws_read(struct client *clt, int *type, size_t *len)
132 struct buf *rbuf = &clt->bio.rbuf;
133 size_t i;
134 uint32_t mask;
135 uint8_t first, second, op, plen;
137 *type = WST_UNKNOWN, *len = 0;
139 if (rbuf->len < 2) {
140 errno = EAGAIN;
141 return (-1);
144 memcpy(&first, &rbuf->buf[0], sizeof(first));
145 memcpy(&second, &rbuf->buf[1], sizeof(second));
147 /* for the close message this doesn't seem to be the case... */
148 #if 0
149 /* the reserved bits must be zero, don't care about FIN */
150 if ((first & 0x0E) != 0) {
151 errno = EINVAL;
152 return (-1);
154 #endif
156 /* mask must be set for messages sent by the clients */
157 if ((second >> 7) != 1) {
158 errno = EINVAL;
159 return (-1);
162 op = first & 0x0F;
163 plen = second & 0x7F;
165 /* don't support extended payload length for now */
166 if (plen >= 126) {
167 errno = E2BIG;
168 return (-1);
171 *len = plen;
173 switch (op) {
174 case WST_CONT:
175 case WST_TEXT:
176 case WST_BINARY:
177 case WST_CLOSE:
178 case WST_PING:
179 *type = op;
180 break;
183 if (rbuf->len < sizeof(first) + sizeof(second) + sizeof(mask) + plen) {
184 errno = EAGAIN;
185 return (-1);
188 buf_drain(rbuf, 2); /* header */
189 memcpy(&mask, rbuf->buf, sizeof(mask));
190 buf_drain(rbuf, 4);
192 /* decode the payload */
193 for (i = 0; i < plen; ++i)
194 rbuf->buf[i] ^= mask >> (8 * (i % 4));
196 return (0);
199 int
200 ws_compose(struct client *clt, int type, const void *data, size_t len)
202 struct bufio *bio = &clt->bio;
203 uint16_t extlen = 0;
204 uint8_t first, second;
206 first = (type & 0x0F) | 0x80;
208 if (len < 126)
209 second = len;
210 else {
211 second = 126;
213 /*
214 * for the extended length, the most significant bit
215 * must be zero. We could use the 64 bit field but
216 * it's a waste.
217 */
218 if (len > 0x7FFF) {
219 errno = ERANGE;
220 return (-1);
222 extlen = htons(len);
225 if (bufio_compose(bio, &first, 1) == -1 ||
226 bufio_compose(bio, &second, 1) == -1)
227 goto err;
229 if (extlen != 0 && bufio_compose(bio, &extlen, sizeof(extlen)) == -1)
230 goto err;
232 if (bufio_compose(bio, data, len) == -1)
233 goto err;
235 return (0);
237 err:
238 clt->err = 1;
239 return (-1);