blob: 1ef80f411597923ed8540585b697aece4db5398e [file] [log] [blame]
Andy Green4739e5c2011-01-22 12:51:57 +00001#include "private-libwebsockets.h"
2#include <netdb.h>
3
4
5/*
6 * In-place str to lower case
7 */
8
9void
10strtolower(char *s)
11{
12 while (*s)
13 *s++ = tolower(*s);
14}
15
16void
17libwebsocket_client_close(struct libwebsocket *wsi)
18{
19 int n = wsi->state;
20 struct libwebsocket_context *clients;
21
22 /* mark the WSI as dead and let the callback know */
23
24 wsi->state = WSI_STATE_DEAD_SOCKET;
25
26 if (wsi->protocol) {
27 if (wsi->protocol->callback && n == WSI_STATE_ESTABLISHED)
28 wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
29 wsi->user_space, NULL, 0);
30
31 /* remove it from the client polling list */
32 clients = wsi->protocol->owning_server;
33 if (clients)
34 for (n = 0; n < clients->fds_count; n++) {
35 if (clients->wsi[n] != wsi)
36 continue;
37 while (n < clients->fds_count - 1) {
38 clients->fds[n] = clients->fds[n + 1];
39 clients->wsi[n] = clients->wsi[n + 1];
40 }
41 /* we only have to deal with one */
42 n = clients->fds_count;
43 }
44
45 }
46
47 /* clean out any parsing allocations */
48
49 for (n = 0; n < WSI_TOKEN_COUNT; n++)
50 if (wsi->utf8_token[n].token)
51 free(wsi->utf8_token[n].token);
52
53 /* shut down reasonably cleanly */
54
55#ifdef LWS_OPENSSL_SUPPORT
56 if (use_ssl) {
57 n = SSL_get_fd(wsi->ssl);
58 SSL_shutdown(wsi->ssl);
59 close(n);
60 SSL_free(wsi->ssl);
61 } else {
62#endif
63 shutdown(wsi->sock, SHUT_RDWR);
64 close(wsi->sock);
65#ifdef LWS_OPENSSL_SUPPORT
66 }
67#endif
68}
69
70struct libwebsocket *
71libwebsocket_client_connect(struct libwebsocket_context *clients,
72 const char *address,
73 int port,
74 const char *path,
75 const char *host,
76 const char *origin,
77 const char *protocol)
78{
79 struct hostent *server_hostent;
80 struct sockaddr_in server_addr;
81 char buf[150];
82 char key_b64[150];
83 char hash[20];
84 int fd;
85 struct pollfd pfd;
86 static const char magic_websocket_guid[] =
87 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
88 static const char magic_websocket_04_masking_guid[] =
89 "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
90 char pkt[1024];
91 char *p = &pkt[0];
92 const char * pc;
93 int len;
94 int okay = 0;
95 struct libwebsocket *wsi;
96 int n;
97
98 wsi = malloc(sizeof (struct libwebsocket));
99 if (wsi == NULL)
100 return NULL;
101
102 clients->wsi[clients->fds_count] = wsi;
103
104 wsi->ietf_spec_revision = 4;
105 wsi->name_buffer_pos = 0;
106 wsi->user_space = NULL;
107 wsi->state = WSI_STATE_CLIENT_UNCONNECTED;
108 wsi->pings_vs_pongs = 0;
109
110 for (n = 0; n < WSI_TOKEN_COUNT; n++) {
111 wsi->utf8_token[n].token = NULL;
112 wsi->utf8_token[n].token_len = 0;
113 }
114
115 /*
116 * prepare the actual connection
117 */
118
119 server_hostent = gethostbyname(address);
120 if (server_hostent == NULL) {
121 fprintf(stderr, "Unable to get host name from %s\n", address);
122 goto bail1;
123 }
124
125 wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
126
127 if (wsi->sock < 0) {
128 fprintf(stderr, "Unable to open socket\n");
129 goto bail1;
130 }
131
132
133 server_addr.sin_family = AF_INET;
134 server_addr.sin_port = htons(port);
135 server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
136 bzero(&server_addr.sin_zero, 8);
137
138 if (connect(wsi->sock, (struct sockaddr *)&server_addr,
139 sizeof(struct sockaddr)) == -1) {
140 fprintf(stderr, "Connect failed");
141 goto bail1;
142 }
143
144 /*
145 * create the random key
146 */
147
148 fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
149 if (fd < 1) {
150 fprintf(stderr, "Unable to open random device %s\n",
151 SYSTEM_RANDOM_FILEPATH);
152 goto bail2;
153 }
154 n = read(fd, hash, 16);
155 if (n != 16) {
156 fprintf(stderr, "Unable to read from random device %s\n",
157 SYSTEM_RANDOM_FILEPATH);
158 close(fd);
159 goto bail2;
160 }
161 close(fd);
162
163 lws_b64_encode_string(hash, 16, key_b64, sizeof key_b64);
164
165 /*
166 * 04 example client handshake
167 *
168 * GET /chat HTTP/1.1
169 * Host: server.example.com
170 * Upgrade: websocket
171 * Connection: Upgrade
172 * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
173 * Sec-WebSocket-Origin: http://example.com
174 * Sec-WebSocket-Protocol: chat, superchat
175 * Sec-WebSocket-Version: 4
176 */
177
178 p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a", path);
179 p += sprintf(p, "Host: %s\x0d\x0a", host);
180 p += sprintf(p, "Upgrade: websocket\x0d\x0a");
181 p += sprintf(p, "Connection: Upgrade\x0d\x0aSec-WebSocket-Key: ");
182 strcpy(p, key_b64);
183 p += strlen(key_b64);
184 p += sprintf(p, "\x0d\x0aSec-WebSocket-Origin: %s\x0d\x0a", origin);
185 if (protocol != NULL)
186 p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", protocol);
187 p += sprintf(p, "Sec-WebSocket-Version: 4\x0d\x0a\x0d\x0a");
188
189
190 /* prepare the expected server accept response */
191
192 strcpy(buf, key_b64);
193 strcpy(&buf[strlen(buf)], magic_websocket_guid);
194
195 SHA1((unsigned char *)buf, strlen(buf), (unsigned char *)hash);
196
197 lws_b64_encode_string(hash, 20, wsi->initial_handshake_hash_base64,
198 sizeof wsi->initial_handshake_hash_base64);
199
200 /* send our request to the server */
201
202 n = send(wsi->sock, pkt, p - pkt, 0);
203
204 wsi->parser_state = WSI_TOKEN_NAME_PART;
205
206 pfd.fd = wsi->sock;
207 pfd.events = POLLIN;
208 pfd.revents = 0;
209
210 n = poll(&pfd, 1, 5000);
211 if (n < 0) {
212 fprintf(stderr, "libwebsocket_client_handshake socket error "
213 "while waiting for handshake response");
214 goto bail2;
215 }
216 if (n == 0) {
217 fprintf(stderr, "libwebsocket_client_handshake timeout "
218 "while waiting for handshake response");
219 goto bail2;
220 }
221
222 /* interpret the server response */
223
224 /*
225 * HTTP/1.1 101 Switching Protocols
226 * Upgrade: websocket
227 * Connection: Upgrade
228 * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
229 * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
230 * Sec-WebSocket-Protocol: chat
231 */
232
233 len = recv(wsi->sock, pkt, sizeof pkt, 0);
234 if (len < 0) {
235 fprintf(stderr, "libwebsocket_client_handshake read error\n");
236 goto bail2;
237 }
238
239 p = pkt;
240 for (n = 0; n < len; n++)
241 libwebsocket_parse(wsi, *p++);
242
243 if (wsi->parser_state != WSI_PARSING_COMPLETE) {
244 fprintf(stderr, "libwebsocket_client_handshake server response"
245 " failed parsing\n");
246 goto bail2;
247 }
248
249 /*
250 * well, what the server sent looked reasonable for syntax.
251 * Now let's confirm it sent all the necessary headers
252 */
253
254 if (!wsi->utf8_token[WSI_TOKEN_HTTP].token_len ||
255 !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
256 !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len ||
257 !wsi->utf8_token[WSI_TOKEN_ACCEPT].token_len ||
258 !wsi->utf8_token[WSI_TOKEN_NONCE].token_len ||
259 (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len &&
260 protocol != NULL)) {
261 fprintf(stderr, "libwebsocket_client_handshake "
262 "missing required header\n");
263 goto bail2;
264 }
265
266 /*
267 * Everything seems to be there, now take a closer look at what is in
268 * each header
269 */
270
271 strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token);
272 if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token,
273 "101 switching protocols")) {
274 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
275 " HTTP response '%s'\n",
276 wsi->utf8_token[WSI_TOKEN_HTTP].token);
277 goto bail2;
278 }
279
280 strtolower(wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
281 if (strcmp(wsi->utf8_token[WSI_TOKEN_UPGRADE].token, "websocket")) {
282 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
283 " Upgrade header '%s'\n",
284 wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
285 goto bail2;
286 }
287
288 strtolower(wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
289 if (strcmp(wsi->utf8_token[WSI_TOKEN_CONNECTION].token, "upgrade")) {
290 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
291 " Connection hdr '%s'\n",
292 wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
293 goto bail2;
294 }
295 /*
296 * confirm the protocol the server wants to talk was in the list of
297 * protocols we offered
298 */
299
300 if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
301
302 /* no protocol name to work from, default to first protocol */
303 wsi->protocol = &clients->protocols[0];
304
305 goto check_accept;
306 }
307
308 pc = protocol;
309 while (*pc && !okay) {
310 if ((!strncmp(pc, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
311 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len)) &&
312 (pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == ',' ||
313 pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == '\0')) {
314 okay = 1;
315 continue;
316 }
317 while (*pc && *pc != ',')
318 pc++;
319 while (*pc && *pc != ' ')
320 pc++;
321 }
322 if (!okay) {
323 fprintf(stderr, "libwebsocket_client_handshake server "
324 "sent bad protocol '%s'\n",
325 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
326 goto bail2;
327 }
328
329 /*
330 * identify the selected protocol struct and set it
331 */
332 n = 0;
333 wsi->protocol = NULL;
334 while (clients->protocols[n].callback) {
335 if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
336 clients->protocols[n].name) == 0)
337 wsi->protocol = &clients->protocols[n];
338 n++;
339 }
340
341 if (wsi->protocol == NULL) {
342 fprintf(stderr, "libwebsocket_client_handshake server "
343 "requested protocol '%s', which we "
344 "said we supported but we don't!\n",
345 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
346 goto bail2;
347 }
348
349check_accept:
350 /*
351 * Confirm his accept token is the same as the one we precomputed
352 */
353
354 if (strcmp(wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
355 wsi->initial_handshake_hash_base64)) {
356 fprintf(stderr, "libwebsocket_client_handshake server sent "
357 "bad ACCEPT '%s' vs computed '%s'\n",
358 wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
359 wsi->initial_handshake_hash_base64);
360 goto bail2;
361 }
362
363 /*
364 * Calculate the masking key to use when sending data to server
365 */
366
367 strcpy(buf, key_b64);
368 p = buf + strlen(key_b64);
369 strcpy(p, wsi->utf8_token[WSI_TOKEN_NONCE].token);
370 p += wsi->utf8_token[WSI_TOKEN_NONCE].token_len;
371 strcpy(p, magic_websocket_04_masking_guid);
372 SHA1((unsigned char *)buf, strlen(buf), wsi->masking_key_04);
373
374 /* okay he is good to go */
375
376 clients->fds[clients->fds_count].fd = wsi->sock;
377 clients->fds[clients->fds_count].revents = 0;
378 clients->fds[clients->fds_count++].events = POLLIN;
379
380 wsi->state = WSI_STATE_ESTABLISHED;
381 wsi->client_mode = 1;
382
383 fprintf(stderr, "handshake OK for protocol %s\n", wsi->protocol->name);
384
385 return wsi;
386
387
388bail2:
389 libwebsocket_client_close(wsi);
390bail1:
391 free(wsi);
392
393 return NULL;
394}