blob: 53da4e7ad5a1e94b58c455f96a09dd007c72d716 [file] [log] [blame]
Andy Greena1ce6be2013-01-18 11:43:21 +08001/*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301 USA
20 */
21
22#include "private-libwebsockets.h"
23
24#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
Andy Greena1ce6be2013-01-18 11:43:21 +080025
Andy Greena1ce6be2013-01-18 11:43:21 +080026/*
27 * Perform the newer BASE64-encoded handshake scheme
28 */
29
30int
31handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
32{
Andy Greena1ce6be2013-01-18 11:43:21 +080033 unsigned char hash[20];
34 int n;
35 char *response;
36 char *p;
Andy Greena1ce6be2013-01-18 11:43:21 +080037 int accept_len;
Andy Green3182ece2013-01-20 17:08:31 +080038#ifndef LWS_NO_EXTENSIONS
Andy Greena1ce6be2013-01-18 11:43:21 +080039 char *c;
40 char ext_name[128];
41 struct libwebsocket_extension *ext;
42 int ext_count = 0;
43 int more = 1;
Andy Green3182ece2013-01-20 17:08:31 +080044#endif
Andy Greena1ce6be2013-01-18 11:43:21 +080045
Andy Green16ab3182013-02-10 18:02:31 +080046 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
47 !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
Andy Greena1ce6be2013-01-18 11:43:21 +080048 lwsl_parser("handshake_04 missing pieces\n");
49 /* completed header processing, but missing some bits */
50 goto bail;
51 }
52
Andy Greenb5b23192013-02-11 17:13:32 +080053 if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >=
54 MAX_WEBSOCKET_04_KEY_LEN) {
55 lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
Andy Greena1ce6be2013-01-18 11:43:21 +080056 goto bail;
57 }
58
Andy Greencecf5e72013-02-12 10:07:22 +080059 /*
60 * since key length is restricted above (currently 128), cannot
61 * overflow
62 */
63 n = sprintf((char *)context->service_buffer,
Andy Green16ab3182013-02-10 18:02:31 +080064 "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
65 lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
Andy Greena1ce6be2013-01-18 11:43:21 +080066
Andy Greene48ba312013-02-10 15:34:59 +080067 SHA1(context->service_buffer, n, hash);
Andy Greena1ce6be2013-01-18 11:43:21 +080068
Andy Greene48ba312013-02-10 15:34:59 +080069 accept_len = lws_b64_encode_string((char *)hash, 20,
70 (char *)context->service_buffer,
Andy Greenb5b23192013-02-11 17:13:32 +080071 sizeof(context->service_buffer));
Andy Greena1ce6be2013-01-18 11:43:21 +080072 if (accept_len < 0) {
73 lwsl_warn("Base64 encoded hash too long\n");
74 goto bail;
75 }
76
77 /* allocate the per-connection user memory (if any) */
Andy Green2af4d5b2013-02-18 16:30:10 +080078 if (libwebsocket_ensure_user_space(wsi))
Andy Greena1ce6be2013-01-18 11:43:21 +080079 goto bail;
80
81 /* create the response packet */
82
83 /* make a buffer big enough for everything */
84
Andy Green60fbc632013-02-11 09:37:23 +080085 response = (char *)context->service_buffer + MAX_WEBSOCKET_04_KEY_LEN;
Andy Greena1ce6be2013-01-18 11:43:21 +080086 p = response;
87 LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
88 "Upgrade: WebSocket\x0d\x0a"
89 "Connection: Upgrade\x0d\x0a"
90 "Sec-WebSocket-Accept: ");
Andy Greene48ba312013-02-10 15:34:59 +080091 strcpy(p, (char *)context->service_buffer);
Andy Greena1ce6be2013-01-18 11:43:21 +080092 p += accept_len;
93
Andy Green16ab3182013-02-10 18:02:31 +080094 if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
Andy Greena1ce6be2013-01-18 11:43:21 +080095 LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
Andy Green16ab3182013-02-10 18:02:31 +080096 n = lws_hdr_copy(wsi, p, 128, WSI_TOKEN_PROTOCOL);
97 if (n < 0)
98 goto bail;
99 p += n;
Andy Greena1ce6be2013-01-18 11:43:21 +0800100 }
101
Andy Green3182ece2013-01-20 17:08:31 +0800102#ifndef LWS_NO_EXTENSIONS
Andy Greena1ce6be2013-01-18 11:43:21 +0800103 /*
104 * Figure out which extensions the client has that we want to
105 * enable on this connection, and give him back the list
106 */
107
Andy Green16ab3182013-02-10 18:02:31 +0800108 if (lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
Andy Greena1ce6be2013-01-18 11:43:21 +0800109
110 /*
111 * break down the list of client extensions
112 * and go through them
113 */
114
Andy Greenb5b23192013-02-11 17:13:32 +0800115 if (lws_hdr_copy(wsi, (char *)context->service_buffer,
116 sizeof(context->service_buffer),
117 WSI_TOKEN_EXTENSIONS) < 0)
Andy Green16ab3182013-02-10 18:02:31 +0800118 goto bail;
119
120 c = (char *)context->service_buffer;
121 lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
Andy Greena1ce6be2013-01-18 11:43:21 +0800122 wsi->count_active_extensions = 0;
123 n = 0;
124 while (more) {
125
126 if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
127 ext_name[n] = *c++;
128 if (n < sizeof(ext_name) - 1)
129 n++;
130 continue;
131 }
132 ext_name[n] = '\0';
133 if (!*c)
134 more = 0;
135 else {
136 c++;
137 if (!n)
138 continue;
139 }
140
141 /* check a client's extension against our support */
142
143 ext = wsi->protocol->owning_server->extensions;
144
145 while (ext && ext->callback) {
146
147 if (strcmp(ext_name, ext->name)) {
148 ext++;
149 continue;
150 }
151
152 /*
153 * oh, we do support this one he
154 * asked for... but let's ask user
155 * code if it's OK to apply it on this
156 * particular connection + protocol
157 */
158
159 n = wsi->protocol->owning_server->
160 protocols[0].callback(
161 wsi->protocol->owning_server,
162 wsi,
163 LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
164 wsi->user_space, ext_name, 0);
165
166 /*
167 * zero return from callback means
168 * go ahead and allow the extension,
169 * it's what we get if the callback is
170 * unhandled
171 */
172
173 if (n) {
174 ext++;
175 continue;
176 }
177
178 /* apply it */
179
180 if (ext_count)
181 *p++ = ',';
182 else
183 LWS_CPYAPP(p,
184 "\x0d\x0aSec-WebSocket-Extensions: ");
185 p += sprintf(p, "%s", ext_name);
186 ext_count++;
187
188 /* instantiate the extension on this conn */
189
190 wsi->active_extensions_user[
191 wsi->count_active_extensions] =
192 malloc(ext->per_session_data_size);
193 if (wsi->active_extensions_user[
Andy Greenb5b23192013-02-11 17:13:32 +0800194 wsi->count_active_extensions] == NULL) {
Andy Greena1ce6be2013-01-18 11:43:21 +0800195 lwsl_err("Out of mem\n");
196 free(response);
197 goto bail;
198 }
199 memset(wsi->active_extensions_user[
200 wsi->count_active_extensions], 0,
201 ext->per_session_data_size);
202
203 wsi->active_extensions[
204 wsi->count_active_extensions] = ext;
205
206 /* allow him to construct his context */
207
208 ext->callback(wsi->protocol->owning_server,
209 ext, wsi,
210 LWS_EXT_CALLBACK_CONSTRUCT,
211 wsi->active_extensions_user[
212 wsi->count_active_extensions], NULL, 0);
213
214 wsi->count_active_extensions++;
Andy Greenb5b23192013-02-11 17:13:32 +0800215 lwsl_parser("count_active_extensions <- %d\n",
Andy Greena1ce6be2013-01-18 11:43:21 +0800216 wsi->count_active_extensions);
217
218 ext++;
219 }
220
221 n = 0;
222 }
223 }
Andy Green3182ece2013-01-20 17:08:31 +0800224#endif
Andy Greena1ce6be2013-01-18 11:43:21 +0800225 /* end of response packet */
226
227 LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");
228
Andy Green3182ece2013-01-20 17:08:31 +0800229#ifndef LWS_NO_EXTENSIONS
Andy Greena1ce6be2013-01-18 11:43:21 +0800230 if (!lws_any_extension_handled(context, wsi,
231 LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
Andy Greenb5b23192013-02-11 17:13:32 +0800232 response, p - response)) {
233#else
Andy Green3182ece2013-01-20 17:08:31 +0800234 {
Andy Greenb5b23192013-02-11 17:13:32 +0800235#endif
Andy Greena1ce6be2013-01-18 11:43:21 +0800236 /* okay send the handshake response accepting the connection */
237
Andy Greenb5b23192013-02-11 17:13:32 +0800238 lwsl_parser("issuing resp pkt %d len\n", (int)(p - response));
Andy Greena1ce6be2013-01-18 11:43:21 +0800239 #ifdef DEBUG
240 fwrite(response, 1, p - response, stderr);
241 #endif
242 n = libwebsocket_write(wsi, (unsigned char *)response,
243 p - response, LWS_WRITE_HTTP);
Andy Greenfc7c5e42013-02-23 10:50:10 +0800244 if (n != (p - response)) {
Andy Greena1ce6be2013-01-18 11:43:21 +0800245 lwsl_debug("handshake_0405: ERROR writing to socket\n");
246 goto bail;
247 }
248
249 }
250
251 /* alright clean up and set ourselves into established state */
252
Andy Greena1ce6be2013-01-18 11:43:21 +0800253 wsi->state = WSI_STATE_ESTABLISHED;
254 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
Andy Greena1ce6be2013-01-18 11:43:21 +0800255
256 /* notify user code that we're ready to roll */
257
258 if (wsi->protocol->callback)
259 wsi->protocol->callback(wsi->protocol->owning_server,
260 wsi, LWS_CALLBACK_ESTABLISHED,
261 wsi->user_space, NULL, 0);
262
263 return 0;
264
265
266bail:
Andy Green2b57a342013-02-06 15:15:25 +0900267 /* free up his parsing allocations */
268
Andy Green16ab3182013-02-10 18:02:31 +0800269 if (wsi->u.hdr.ah)
270 free(wsi->u.hdr.ah);
Andy Green2b57a342013-02-06 15:15:25 +0900271
Andy Greena1ce6be2013-01-18 11:43:21 +0800272 return -1;
273}
274