1 /*
2 Copyright (c) 2013 250bpm s.r.o. All rights reserved.
3 Copyright (c) 2014-2016 Jack R. Dunaway. All rights reserved.
4 Copyright 2017 Garrett D'Amore <garrett@damore.org>
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"),
8 to deal in the Software without restriction, including without limitation
9 the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 and/or sell copies of the Software, and to permit persons to whom
11 the Software is furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included
14 in all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 IN THE SOFTWARE.
23 */
24
25 #include "ws_handshake.h"
26 #include "sha1.h"
27
28 #include "../../aio/timer.h"
29
30 #include "../../core/sock.h"
31
32 #include "../utils/base64.h"
33
34 #include "../../utils/alloc.h"
35 #include "../../utils/err.h"
36 #include "../../utils/cont.h"
37 #include "../../utils/fast.h"
38 #include "../../utils/wire.h"
39 #include "../../utils/attr.h"
40 #include "../../utils/random.h"
41 #include "../../utils/strcasestr.h"
42
43 #include <stddef.h>
44 #include <string.h>
45 #include <ctype.h>
46
47 #define CRLF "\r\n"
48
49 /*****************************************************************************/
50 /*** BEGIN undesirable dependency *******************************************/
51 /*****************************************************************************/
52 /* TODO: A transport should be SP agnostic; alas, these includes are */
53 /* required for the map. Ideally, this map would live in another */
54 /* abstraction layer; perhaps a "registry" of Scalability Protocols? */
55 /*****************************************************************************/
56 #include "../../pair.h"
57 #include "../../reqrep.h"
58 #include "../../pubsub.h"
59 #include "../../survey.h"
60 #include "../../pipeline.h"
61 #include "../../bus.h"
62
63 static const struct nn_ws_sp_map NN_WS_HANDSHAKE_SP_MAP[] = {
64 { NN_PAIR, NN_PAIR, "pair.sp.nanomsg.org" },
65 { NN_REQ, NN_REP, "req.sp.nanomsg.org" },
66 { NN_REP, NN_REQ, "rep.sp.nanomsg.org" },
67 { NN_PUB, NN_SUB, "pub.sp.nanomsg.org" },
68 { NN_SUB, NN_PUB, "sub.sp.nanomsg.org" },
69 { NN_SURVEYOR, NN_RESPONDENT, "surveyor.sp.nanomsg.org" },
70 { NN_RESPONDENT, NN_SURVEYOR, "respondent.sp.nanomsg.org" },
71 { NN_PUSH, NN_PULL, "push.sp.nanomsg.org" },
72 { NN_PULL, NN_PUSH, "pull.sp.nanomsg.org" },
73 { NN_BUS, NN_BUS, "bus.sp.nanomsg.org" }
74 };
75
76 const size_t NN_WS_HANDSHAKE_SP_MAP_LEN = sizeof (NN_WS_HANDSHAKE_SP_MAP) /
77 sizeof (NN_WS_HANDSHAKE_SP_MAP [0]);
78 /*****************************************************************************/
79 /*** END undesirable dependency *********************************************/
80 /*****************************************************************************/
81
82 /* State machine finite states. */
83 #define NN_WS_HANDSHAKE_STATE_IDLE 1
84 #define NN_WS_HANDSHAKE_STATE_SERVER_RECV 2
85 #define NN_WS_HANDSHAKE_STATE_SERVER_REPLY 3
86 #define NN_WS_HANDSHAKE_STATE_CLIENT_SEND 4
87 #define NN_WS_HANDSHAKE_STATE_CLIENT_RECV 5
88 #define NN_WS_HANDSHAKE_STATE_HANDSHAKE_SENT 6
89 #define NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR 7
90 #define NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_DONE 8
91 #define NN_WS_HANDSHAKE_STATE_DONE 9
92 #define NN_WS_HANDSHAKE_STATE_STOPPING 10
93
94 /* Subordinate srcptr objects. */
95 #define NN_WS_HANDSHAKE_SRC_USOCK 1
96 #define NN_WS_HANDSHAKE_SRC_TIMER 2
97
98 /* Time allowed to complete handshake. */
99 #define NN_WS_HANDSHAKE_TIMEOUT 5000
100
101 /* Possible return codes internal to the parsing operations. */
102 #define NN_WS_HANDSHAKE_NOMATCH 0
103 #define NN_WS_HANDSHAKE_MATCH 1
104
105 /* Possible return codes from parsing opening handshake from peer. */
106 #define NN_WS_HANDSHAKE_VALID 0
107 #define NN_WS_HANDSHAKE_RECV_MORE 1
108 #define NN_WS_HANDSHAKE_INVALID -1
109
110 /* Possible handshake responses to send to client when acting as server. */
111 #define NN_WS_HANDSHAKE_RESPONSE_NULL -1
112 #define NN_WS_HANDSHAKE_RESPONSE_OK 0
113 #define NN_WS_HANDSHAKE_RESPONSE_TOO_BIG 1
114 #define NN_WS_HANDSHAKE_RESPONSE_UNUSED2 2
115 #define NN_WS_HANDSHAKE_RESPONSE_WSPROTO 3
116 #define NN_WS_HANDSHAKE_RESPONSE_WSVERSION 4
117 #define NN_WS_HANDSHAKE_RESPONSE_NNPROTO 5
118 #define NN_WS_HANDSHAKE_RESPONSE_NOTPEER 6
119 #define NN_WS_HANDSHAKE_RESPONSE_UNKNOWNTYPE 7
120
121 /* Private functions. */
122 static void nn_ws_handshake_handler (struct nn_fsm *self, int src, int type,
123 void *srcptr);
124 static void nn_ws_handshake_shutdown (struct nn_fsm *self, int src, int type,
125 void *srcptr);
126 static void nn_ws_handshake_leave (struct nn_ws_handshake *self, int rc);
127
128 /* WebSocket protocol support functions. */
129 static int nn_ws_handshake_parse_client_opening (struct nn_ws_handshake *self);
130 static void nn_ws_handshake_server_reply (struct nn_ws_handshake *self);
131 static void nn_ws_handshake_client_request (struct nn_ws_handshake *self);
132 static int nn_ws_handshake_parse_server_response (struct nn_ws_handshake *self);
133 static int nn_ws_handshake_hash_key (const char *key, size_t key_len,
134 char *hashed, size_t hashed_len);
135
136 /* String parsing support functions. */
137
138 /* Scans for reference token against subject string, optionally ignoring
139 case sensitivity and/or leading spaces in subject. On match, advances
140 the subject pointer to the next non-ignored character past match. Both
141 strings must be NULL terminated to avoid undefined behavior. Returns
142 NN_WS_HANDSHAKE_MATCH on match; else, NN_WS_HANDSHAKE_NOMATCH. */
143 static int nn_ws_match_token (const char* token, const char **subj,
144 int case_insensitive, int ignore_leading_sp);
145
146 /* Scans subject string for termination sequence, optionally ignoring
147 leading and/or trailing spaces in subject. On match, advances
148 the subject pointer to the next character past match. Both
149 strings must be NULL terminated to avoid undefined behavior. If the
150 match succeeds, values are stored into *addr and *len. */
151 static int nn_ws_match_value (const char* termseq, const char **subj,
152 int ignore_leading_sp, int ignore_trailing_sp, const char **addr,
153 size_t* const len);
154
155 /* Compares subject octet stream to expected value, optionally ignoring
156 case sensitivity. Returns non-zero on success, zero on failure. */
157 static int nn_ws_validate_value (const char* expected, const char *subj,
158 size_t subj_len, int case_insensitive);
159
nn_ws_handshake_init(struct nn_ws_handshake * self,int src,struct nn_fsm * owner)160 void nn_ws_handshake_init (struct nn_ws_handshake *self, int src,
161 struct nn_fsm *owner)
162 {
163 nn_fsm_init (&self->fsm, nn_ws_handshake_handler, nn_ws_handshake_shutdown,
164 src, self, owner);
165 self->state = NN_WS_HANDSHAKE_STATE_IDLE;
166 nn_timer_init (&self->timer, NN_WS_HANDSHAKE_SRC_TIMER, &self->fsm);
167 nn_fsm_event_init (&self->done);
168 self->timeout = NN_WS_HANDSHAKE_TIMEOUT;
169 self->usock = NULL;
170 self->usock_owner.src = -1;
171 self->usock_owner.fsm = NULL;
172 self->pipebase = NULL;
173 }
174
nn_ws_handshake_term(struct nn_ws_handshake * self)175 void nn_ws_handshake_term (struct nn_ws_handshake *self)
176 {
177 nn_assert_state (self, NN_WS_HANDSHAKE_STATE_IDLE);
178
179 nn_fsm_event_term (&self->done);
180 nn_timer_term (&self->timer);
181 nn_fsm_term (&self->fsm);
182 }
183
nn_ws_handshake_isidle(struct nn_ws_handshake * self)184 int nn_ws_handshake_isidle (struct nn_ws_handshake *self)
185 {
186 return nn_fsm_isidle (&self->fsm);
187 }
188
nn_ws_handshake_start(struct nn_ws_handshake * self,struct nn_usock * usock,struct nn_pipebase * pipebase,int mode,const char * resource,const char * host)189 void nn_ws_handshake_start (struct nn_ws_handshake *self,
190 struct nn_usock *usock, struct nn_pipebase *pipebase,
191 int mode, const char *resource, const char *host)
192 {
193 /* It's expected this resource has been allocated during intial connect. */
194 if (mode == NN_WS_CLIENT)
195 nn_assert (strlen (resource) >= 1);
196
197 /* Take ownership of the underlying socket. */
198 nn_assert (self->usock == NULL && self->usock_owner.fsm == NULL);
199 self->usock_owner.src = NN_WS_HANDSHAKE_SRC_USOCK;
200 self->usock_owner.fsm = &self->fsm;
201 nn_usock_swap_owner (usock, &self->usock_owner);
202 self->usock = usock;
203 self->pipebase = pipebase;
204 self->mode = mode;
205 self->resource = resource;
206 self->remote_host = host;
207
208 memset (self->opening_hs, 0, sizeof (self->opening_hs));
209 memset (self->response, 0, sizeof (self->response));
210
211 self->recv_pos = 0;
212 self->retries = 0;
213
214 /* Calculate the absolute minimum length possible for a valid opening
215 handshake. This is an optimization since we must poll for the
216 remainder of the opening handshake in small byte chunks. */
217 switch (self->mode) {
218 case NN_WS_SERVER:
219 self->recv_len = strlen (
220 "GET x HTTP/1.1\r\n"
221 "Upgrade: websocket\r\n"
222 "Connection: Upgrade\r\n"
223 "Host: x\r\n"
224 "Origin: x\r\n"
225 "Sec-WebSocket-Key: xxxxxxxxxxxxxxxxxxxxxxxx\r\n"
226 "Sec-WebSocket-Version: xx\r\n\r\n");
227 break;
228 case NN_WS_CLIENT:
229 /* Shortest conceiveable response from server is a terse status. */
230 self->recv_len = strlen ("HTTP/1.1 xxx\r\n\r\n");
231 break;
232 default:
233 /* Developer error; unexpected mode. */
234 nn_assert (0);
235 break;
236 }
237
238 /* Launch the state machine. */
239 nn_fsm_start (&self->fsm);
240 }
241
nn_ws_handshake_stop(struct nn_ws_handshake * self)242 void nn_ws_handshake_stop (struct nn_ws_handshake *self)
243 {
244 nn_fsm_stop (&self->fsm);
245 }
246
nn_ws_handshake_shutdown(struct nn_fsm * self,int src,int type,NN_UNUSED void * srcptr)247 static void nn_ws_handshake_shutdown (struct nn_fsm *self, int src, int type,
248 NN_UNUSED void *srcptr)
249 {
250 struct nn_ws_handshake *handshaker;
251
252 handshaker = nn_cont (self, struct nn_ws_handshake, fsm);
253
254 if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) {
255 nn_timer_stop (&handshaker->timer);
256 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING;
257 }
258 if (nn_slow (handshaker->state == NN_WS_HANDSHAKE_STATE_STOPPING)) {
259 if (!nn_timer_isidle (&handshaker->timer))
260 return;
261 handshaker->state = NN_WS_HANDSHAKE_STATE_IDLE;
262 nn_fsm_stopped (&handshaker->fsm, NN_WS_HANDSHAKE_STOPPED);
263 return;
264 }
265
266 nn_fsm_bad_state (handshaker->state, src, type);
267 }
268
nn_ws_match_token(const char * token,const char ** subj,int case_insensitive,int ignore_leading_sp)269 static int nn_ws_match_token (const char* token, const char **subj,
270 int case_insensitive, int ignore_leading_sp)
271 {
272 const char *pos;
273
274 nn_assert (token && *subj);
275
276 pos = *subj;
277
278 if (ignore_leading_sp) {
279 while (*pos == '\x20' && *pos) {
280 pos++;
281 }
282 }
283
284 if (case_insensitive) {
285 while (*token && *pos) {
286 if (tolower (*token) != tolower (*pos))
287 return NN_WS_HANDSHAKE_NOMATCH;
288 token++;
289 pos++;
290 }
291 }
292 else {
293 while (*token && *pos) {
294 if (*token != *pos)
295 return NN_WS_HANDSHAKE_NOMATCH;
296 token++;
297 pos++;
298 }
299 }
300
301 /* Encountered end of subject before matching completed. */
302 if (!*pos && *token)
303 return NN_WS_HANDSHAKE_NOMATCH;
304
305 /* Entire token has been matched. */
306 nn_assert (!*token);
307
308 /* On success, advance subject position. */
309 *subj = pos;
310
311 return NN_WS_HANDSHAKE_MATCH;
312 }
313
nn_ws_match_value(const char * termseq,const char ** subj,int ignore_leading_sp,int ignore_trailing_sp,const char ** addr,size_t * const len)314 static int nn_ws_match_value (const char* termseq, const char **subj,
315 int ignore_leading_sp, int ignore_trailing_sp, const char **addr,
316 size_t* const len)
317 {
318 const char *start;
319 const char *end;
320
321 nn_assert (termseq && *subj);
322
323 start = *subj;
324 if (addr)
325 *addr = NULL;
326 if (len)
327 *len = 0;
328
329 /* Find first occurence of termination sequence. */
330 end = strstr (start, termseq);
331
332 /* Was a termination sequence found? */
333 if (end) {
334 *subj = end + strlen (termseq);
335 }
336 else {
337 return NN_WS_HANDSHAKE_NOMATCH;
338 }
339
340 if (ignore_leading_sp) {
341 while (*start == '\x20' && start < end) {
342 start++;
343 }
344 }
345
346 if (addr)
347 *addr = start;
348
349 /* In this special case, the value was "found", but is just empty or
350 ignored space. */
351 if (start == end)
352 return NN_WS_HANDSHAKE_MATCH;
353
354 if (ignore_trailing_sp) {
355 while (*(end - 1) == '\x20' && start < end) {
356 end--;
357 }
358 }
359
360 if (len)
361 *len = end - start;
362
363 return NN_WS_HANDSHAKE_MATCH;
364 }
365
nn_ws_validate_value(const char * expected,const char * subj,size_t subj_len,int case_insensitive)366 static int nn_ws_validate_value (const char* expected, const char *subj,
367 size_t subj_len, int case_insensitive)
368 {
369 if (strlen (expected) != subj_len)
370 return NN_WS_HANDSHAKE_NOMATCH;
371
372 if (case_insensitive) {
373 while (*expected && *subj) {
374 if (tolower (*expected) != tolower (*subj))
375 return NN_WS_HANDSHAKE_NOMATCH;
376 expected++;
377 subj++;
378 }
379 }
380 else {
381 while (*expected && *subj) {
382 if (*expected != *subj)
383 return NN_WS_HANDSHAKE_NOMATCH;
384 expected++;
385 subj++;
386 }
387 }
388
389 return NN_WS_HANDSHAKE_MATCH;
390 }
391
nn_ws_handshake_handler(struct nn_fsm * self,int src,int type,NN_UNUSED void * srcptr)392 static void nn_ws_handshake_handler (struct nn_fsm *self, int src, int type,
393 NN_UNUSED void *srcptr)
394 {
395 struct nn_ws_handshake *handshaker;
396
397 size_t i;
398
399 handshaker = nn_cont (self, struct nn_ws_handshake, fsm);
400
401 switch (handshaker->state) {
402
403 /******************************************************************************/
404 /* IDLE state. */
405 /******************************************************************************/
406 case NN_WS_HANDSHAKE_STATE_IDLE:
407 switch (src) {
408
409 case NN_FSM_ACTION:
410 switch (type) {
411 case NN_FSM_START:
412 nn_assert (handshaker->recv_pos == 0);
413 nn_assert (handshaker->recv_len >= NN_WS_HANDSHAKE_TERMSEQ_LEN);
414
415 nn_timer_start (&handshaker->timer, handshaker->timeout);
416
417 switch (handshaker->mode) {
418 case NN_WS_CLIENT:
419 /* Send opening handshake to server. */
420 nn_assert (handshaker->recv_len <=
421 sizeof (handshaker->response));
422 handshaker->state = NN_WS_HANDSHAKE_STATE_CLIENT_SEND;
423 nn_ws_handshake_client_request (handshaker);
424 return;
425 case NN_WS_SERVER:
426 /* Begin receiving opening handshake from client. */
427 nn_assert (handshaker->recv_len <=
428 sizeof (handshaker->opening_hs));
429 handshaker->state = NN_WS_HANDSHAKE_STATE_SERVER_RECV;
430 nn_usock_recv (handshaker->usock, handshaker->opening_hs,
431 handshaker->recv_len, NULL);
432 return;
433 default:
434 /* Unexpected mode. */
435 nn_assert (0);
436 return;
437 }
438
439 default:
440 nn_fsm_bad_action (handshaker->state, src, type);
441 }
442
443 default:
444 nn_fsm_bad_source (handshaker->state, src, type);
445 }
446
447 /******************************************************************************/
448 /* SERVER_RECV state. */
449 /******************************************************************************/
450 case NN_WS_HANDSHAKE_STATE_SERVER_RECV:
451 switch (src) {
452
453 case NN_WS_HANDSHAKE_SRC_USOCK:
454 switch (type) {
455 case NN_USOCK_RECEIVED:
456 /* Parse bytes received thus far. */
457 switch (nn_ws_handshake_parse_client_opening (handshaker)) {
458 case NN_WS_HANDSHAKE_INVALID:
459 /* Opening handshake parsed successfully but does not
460 contain valid values. Respond failure to client. */
461 handshaker->state = NN_WS_HANDSHAKE_STATE_SERVER_REPLY;
462 nn_ws_handshake_server_reply (handshaker);
463 return;
464 case NN_WS_HANDSHAKE_VALID:
465 /* Opening handshake parsed successfully, and is valid.
466 Respond success to client. */
467 handshaker->state = NN_WS_HANDSHAKE_STATE_SERVER_REPLY;
468 nn_ws_handshake_server_reply (handshaker);
469 return;
470 case NN_WS_HANDSHAKE_RECV_MORE:
471 /* Not enough bytes have been received to determine
472 validity; remain in the receive state, and retrieve
473 more bytes from client. */
474 handshaker->recv_pos += handshaker->recv_len;
475
476 /* Validate the previous recv operation. */
477 nn_assert (handshaker->recv_pos <
478 sizeof (handshaker->opening_hs));
479
480 /* Ensure we can back-track at least the length of the
481 termination sequence to determine how many bytes to
482 receive on the next retry. This is an assertion, not
483 a conditional, since under no condition is it
484 necessary to initially receive so few bytes. */
485 nn_assert (handshaker->recv_pos >=
486 (int) NN_WS_HANDSHAKE_TERMSEQ_LEN);
487
488 /* We only compare if we have at least one byte to
489 compare against. When i drops to zero, it means
490 we don't have any bytes to match against, and it is
491 automatically true. */
492 for (i = NN_WS_HANDSHAKE_TERMSEQ_LEN; i > 0; i--) {
493 if (memcmp (NN_WS_HANDSHAKE_TERMSEQ,
494 handshaker->opening_hs + handshaker->recv_pos - i,
495 i) == 0) {
496 break;
497 }
498 }
499
500 nn_assert (i < NN_WS_HANDSHAKE_TERMSEQ_LEN);
501
502 handshaker->recv_len = NN_WS_HANDSHAKE_TERMSEQ_LEN - i;
503
504 /* In the unlikely case the client would overflow what we
505 assumed was a sufficiently-large buffer to receive the
506 handshake, we fail the client. */
507 if (handshaker->recv_len + handshaker->recv_pos >
508 sizeof (handshaker->opening_hs)) {
509 handshaker->response_code =
510 NN_WS_HANDSHAKE_RESPONSE_TOO_BIG;
511 handshaker->state =
512 NN_WS_HANDSHAKE_STATE_SERVER_REPLY;
513 nn_ws_handshake_server_reply (handshaker);
514 }
515 else {
516 handshaker->retries++;
517 nn_usock_recv (handshaker->usock,
518 handshaker->opening_hs + handshaker->recv_pos,
519 handshaker->recv_len, NULL);
520 }
521 return;
522 default:
523 nn_fsm_error ("Unexpected handshake result",
524 handshaker->state, src, type);
525 }
526 return;
527 case NN_USOCK_SHUTDOWN:
528 /* Ignore it and wait for ERROR event. */
529 return;
530 case NN_USOCK_ERROR:
531 nn_timer_stop (&handshaker->timer);
532 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
533 return;
534 default:
535 nn_fsm_bad_action (handshaker->state, src, type);
536 }
537
538 case NN_WS_HANDSHAKE_SRC_TIMER:
539 switch (type) {
540 case NN_TIMER_TIMEOUT:
541 nn_timer_stop (&handshaker->timer);
542 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
543 return;
544 default:
545 nn_fsm_bad_action (handshaker->state, src, type);
546 }
547
548 default:
549 nn_fsm_bad_source (handshaker->state, src, type);
550 }
551
552 /******************************************************************************/
553 /* SERVER_REPLY state. */
554 /******************************************************************************/
555 case NN_WS_HANDSHAKE_STATE_SERVER_REPLY:
556 switch (src) {
557
558 case NN_WS_HANDSHAKE_SRC_USOCK:
559 switch (type) {
560 case NN_USOCK_SENT:
561 /* As per RFC 6455 4.2.2, the handshake is now complete
562 and the connection is immediately ready for send/recv. */
563 nn_timer_stop (&handshaker->timer);
564 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_DONE;
565 case NN_USOCK_SHUTDOWN:
566 /* Ignore it and wait for ERROR event. */
567 return;
568 case NN_USOCK_ERROR:
569 nn_timer_stop (&handshaker->timer);
570 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
571 return;
572 default:
573 nn_fsm_bad_action (handshaker->state, src, type);
574 }
575
576 case NN_WS_HANDSHAKE_SRC_TIMER:
577 switch (type) {
578 case NN_TIMER_TIMEOUT:
579 nn_timer_stop (&handshaker->timer);
580 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
581 return;
582 default:
583 nn_fsm_bad_action (handshaker->state, src, type);
584 }
585
586 default:
587 nn_fsm_bad_source (handshaker->state, src, type);
588 }
589
590 /******************************************************************************/
591 /* CLIENT_SEND state. */
592 /******************************************************************************/
593 case NN_WS_HANDSHAKE_STATE_CLIENT_SEND:
594 switch (src) {
595
596 case NN_WS_HANDSHAKE_SRC_USOCK:
597 switch (type) {
598 case NN_USOCK_SENT:
599 handshaker->state = NN_WS_HANDSHAKE_STATE_CLIENT_RECV;
600 nn_usock_recv (handshaker->usock, handshaker->response,
601 handshaker->recv_len, NULL);
602 return;
603 case NN_USOCK_SHUTDOWN:
604 /* Ignore it and wait for ERROR event. */
605 return;
606 case NN_USOCK_ERROR:
607 nn_timer_stop (&handshaker->timer);
608 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
609 return;
610 default:
611 nn_fsm_bad_action (handshaker->state, src, type);
612 }
613
614 case NN_WS_HANDSHAKE_SRC_TIMER:
615 switch (type) {
616 case NN_TIMER_TIMEOUT:
617 nn_timer_stop (&handshaker->timer);
618 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
619 return;
620 default:
621 nn_fsm_bad_action (handshaker->state, src, type);
622 }
623
624 default:
625 nn_fsm_bad_source (handshaker->state, src, type);
626 }
627
628 /******************************************************************************/
629 /* CLIENT_RECV state. */
630 /******************************************************************************/
631 case NN_WS_HANDSHAKE_STATE_CLIENT_RECV:
632 switch (src) {
633
634 case NN_WS_HANDSHAKE_SRC_USOCK:
635 switch (type) {
636 case NN_USOCK_RECEIVED:
637 /* Parse bytes received thus far. */
638 switch (nn_ws_handshake_parse_server_response (handshaker)) {
639 case NN_WS_HANDSHAKE_INVALID:
640 /* Opening handshake parsed successfully but does not
641 contain valid values. Fail connection. */
642 nn_timer_stop (&handshaker->timer);
643 handshaker->state =
644 NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
645 return;
646 case NN_WS_HANDSHAKE_VALID:
647 /* As per RFC 6455 4.2.2, the handshake is now complete
648 and the connection is immediately ready for send/recv. */
649 nn_timer_stop (&handshaker->timer);
650 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_DONE;
651 return;
652 case NN_WS_HANDSHAKE_RECV_MORE:
653 /* Not enough bytes have been received to determine
654 validity; remain in the receive state, and retrieve
655 more bytes from client. */
656 handshaker->recv_pos += handshaker->recv_len;
657
658 /* Validate the previous recv operation. */
659 nn_assert (handshaker->recv_pos <
660 sizeof (handshaker->response));
661
662 /* Ensure we can back-track at least the length of the
663 termination sequence to determine how many bytes to
664 receive on the next retry. This is an assertion, not
665 a conditional, since under no condition is it
666 necessary to initially receive so few bytes. */
667 nn_assert (handshaker->recv_pos >=
668 (int) NN_WS_HANDSHAKE_TERMSEQ_LEN);
669
670 /* If i goes to 0, it no need to compare. */
671 for (i = NN_WS_HANDSHAKE_TERMSEQ_LEN; i > 0; i--) {
672 if (memcmp (NN_WS_HANDSHAKE_TERMSEQ,
673 handshaker->response + handshaker->recv_pos - i,
674 i) == 0) {
675 break;
676 }
677 }
678
679 nn_assert (i < NN_WS_HANDSHAKE_TERMSEQ_LEN);
680
681 handshaker->recv_len = NN_WS_HANDSHAKE_TERMSEQ_LEN - i;
682
683 /* In the unlikely case the client would overflow what we
684 assumed was a sufficiently-large buffer to receive the
685 handshake, we fail the connection. */
686 if (handshaker->recv_len + handshaker->recv_pos >
687 sizeof (handshaker->response)) {
688 nn_timer_stop (&handshaker->timer);
689 handshaker->state =
690 NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
691 }
692 else {
693 handshaker->retries++;
694 nn_usock_recv (handshaker->usock,
695 handshaker->response + handshaker->recv_pos,
696 handshaker->recv_len, NULL);
697 }
698 return;
699 default:
700 nn_fsm_error ("Unexpected handshake result",
701 handshaker->state, src, type);
702 }
703 return;
704 case NN_USOCK_SHUTDOWN:
705 /* Ignore it and wait for ERROR event. */
706 return;
707 case NN_USOCK_ERROR:
708 nn_timer_stop (&handshaker->timer);
709 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
710 return;
711 default:
712 nn_fsm_bad_action (handshaker->state, src, type);
713 }
714
715 case NN_WS_HANDSHAKE_SRC_TIMER:
716 switch (type) {
717 case NN_TIMER_TIMEOUT:
718 nn_timer_stop (&handshaker->timer);
719 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
720 return;
721 default:
722 nn_fsm_bad_action (handshaker->state, src, type);
723 }
724
725 default:
726 nn_fsm_bad_source (handshaker->state, src, type);
727 }
728
729 /******************************************************************************/
730 /* HANDSHAKE_SENT state. */
731 /******************************************************************************/
732 case NN_WS_HANDSHAKE_STATE_HANDSHAKE_SENT:
733 switch (src) {
734
735 case NN_WS_HANDSHAKE_SRC_USOCK:
736 switch (type) {
737 case NN_USOCK_SENT:
738 /* As per RFC 6455 4.2.2, the handshake is now complete
739 and the connection is immediately ready for send/recv. */
740 nn_timer_stop (&handshaker->timer);
741 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_DONE;
742 return;
743 case NN_USOCK_SHUTDOWN:
744 /* Ignore it and wait for ERROR event. */
745 return;
746 case NN_USOCK_ERROR:
747 nn_timer_stop (&handshaker->timer);
748 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
749 return;
750 default:
751 nn_fsm_bad_action (handshaker->state, src, type);
752 }
753
754 case NN_WS_HANDSHAKE_SRC_TIMER:
755 switch (type) {
756 case NN_TIMER_TIMEOUT:
757 nn_timer_stop (&handshaker->timer);
758 handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR;
759 return;
760 default:
761 nn_fsm_bad_action (handshaker->state, src, type);
762 }
763
764 default:
765 nn_fsm_bad_source (handshaker->state, src, type);
766 }
767
768 /******************************************************************************/
769 /* STOPPING_TIMER_ERROR state. */
770 /******************************************************************************/
771 case NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR:
772 switch (src) {
773
774 case NN_WS_HANDSHAKE_SRC_USOCK:
775 /* Ignore. The only circumstance the client would send bytes is
776 to notify the server it is closing the connection. Wait for the
777 socket to eventually error. */
778 return;
779
780 case NN_WS_HANDSHAKE_SRC_TIMER:
781 switch (type) {
782 case NN_TIMER_STOPPED:
783 nn_ws_handshake_leave (handshaker, NN_WS_HANDSHAKE_ERROR);
784 return;
785 default:
786 nn_fsm_bad_action (handshaker->state, src, type);
787 }
788
789 default:
790 nn_fsm_bad_source (handshaker->state, src, type);
791 }
792
793 /******************************************************************************/
794 /* STOPPING_TIMER_DONE state. */
795 /******************************************************************************/
796 case NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_DONE:
797 switch (src) {
798
799 case NN_WS_HANDSHAKE_SRC_USOCK:
800 /* Ignore. The only circumstance the client would send bytes is
801 to notify the server it is closing the connection. Wait for the
802 socket to eventually error. */
803 return;
804
805 case NN_WS_HANDSHAKE_SRC_TIMER:
806 switch (type) {
807 case NN_TIMER_STOPPED:
808 nn_ws_handshake_leave (handshaker, NN_WS_HANDSHAKE_OK);
809 return;
810 default:
811 nn_fsm_bad_action (handshaker->state, src, type);
812 }
813
814 default:
815 nn_fsm_bad_source (handshaker->state, src, type);
816 }
817
818 /******************************************************************************/
819 /* DONE state. */
820 /* The header exchange was either done successfully of failed. There's */
821 /* nothing that can be done in this state except stopping the object. */
822 /******************************************************************************/
823 case NN_WS_HANDSHAKE_STATE_DONE:
824 nn_fsm_bad_source (handshaker->state, src, type);
825
826 /******************************************************************************/
827 /* Invalid state. */
828 /******************************************************************************/
829 default:
830 nn_fsm_bad_state (handshaker->state, src, type);
831 }
832 }
833
834 /******************************************************************************/
835 /* State machine actions. */
836 /******************************************************************************/
837
nn_ws_handshake_leave(struct nn_ws_handshake * self,int rc)838 static void nn_ws_handshake_leave (struct nn_ws_handshake *self, int rc)
839 {
840 nn_usock_swap_owner (self->usock, &self->usock_owner);
841 self->usock = NULL;
842 self->usock_owner.src = -1;
843 self->usock_owner.fsm = NULL;
844 self->state = NN_WS_HANDSHAKE_STATE_DONE;
845 nn_fsm_raise (&self->fsm, &self->done, rc);
846 }
847
nn_ws_handshake_parse_client_opening(struct nn_ws_handshake * self)848 static int nn_ws_handshake_parse_client_opening (struct nn_ws_handshake *self)
849 {
850 /* As per RFC 6455 section 1.7, this parser is not intended to be a
851 general-purpose parser for arbitrary HTTP headers. As with the design
852 philosophy of nanomsg, application-specific exchanges are better
853 reserved for accepted connections, not as fields within these
854 headers. */
855
856 int rc;
857 const char *pos;
858 unsigned i;
859
860 /* Guarantee that a NULL terminator exists to enable treating this
861 recv buffer like a string. */
862 nn_assert (memchr (self->opening_hs, '\0', sizeof (self->opening_hs)));
863
864 /* Having found the NULL terminator, from this point forward string
865 functions may be used. */
866 nn_assert (strlen (self->opening_hs) < sizeof (self->opening_hs));
867
868 pos = self->opening_hs;
869
870 /* Is the opening handshake from the client fully received? */
871 if (!strstr (pos, NN_WS_HANDSHAKE_TERMSEQ))
872 return NN_WS_HANDSHAKE_RECV_MORE;
873
874 self->host = NULL;
875 self->origin = NULL;
876 self->key = NULL;
877 self->upgrade = NULL;
878 self->conn = NULL;
879 self->version = NULL;
880 self->protocol = NULL;
881 self->uri = NULL;
882
883 self->host_len = 0;
884 self->origin_len = 0;
885 self->key_len = 0;
886 self->upgrade_len = 0;
887 self->conn_len = 0;
888 self->version_len = 0;
889 self->protocol_len = 0;
890 self->uri_len = 0;
891
892 /* NB: If we got here, we already have a fully received set of
893 HTTP headers. So there is no point in asking for more if the
894 headers lack what we need. */
895
896 /* This function, if generating a return value that triggers
897 a response to the client, should replace this sentinel value
898 with a proper response code. */
899 self->response_code = NN_WS_HANDSHAKE_RESPONSE_NULL;
900
901 /* RFC 7230 3.1.1 Request Line: HTTP Method
902 Note requirement of one space and case sensitivity. */
903 if (!nn_ws_match_token ("GET\x20", &pos, 0, 0))
904 return NN_WS_HANDSHAKE_INVALID;
905
906 /* RFC 7230 3.1.1 Request Line: Requested Resource. */
907 if (!nn_ws_match_value ("\x20", &pos, 0, 0, &self->uri, &self->uri_len))
908 return NN_WS_HANDSHAKE_INVALID;
909
910 /* RFC 7230 3.1.1 Request Line: HTTP version. Note case sensitivity. */
911 if (!nn_ws_match_token ("HTTP/1.1", &pos, 0, 0))
912 return NN_WS_HANDSHAKE_INVALID;
913
914 if (!nn_ws_match_token (CRLF, &pos, 0, 0))
915 return NN_WS_HANDSHAKE_INVALID;
916
917 /* It's expected the current position is now at the first
918 header field. Match them one by one. */
919 while (strlen (pos))
920 {
921 const char *conn = NULL;
922 size_t conn_len = 0;
923 if (nn_ws_match_token ("Host:", &pos, 1, 0)) {
924 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
925 &self->host, &self->host_len);
926 }
927 else if (nn_ws_match_token ("Origin:",
928 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
929 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
930 &self->origin, &self->origin_len);
931 }
932 else if (nn_ws_match_token ("Sec-WebSocket-Key:",
933 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
934 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
935 &self->key, &self->key_len);
936 }
937 else if (nn_ws_match_token ("Upgrade:",
938 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
939 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
940 &self->upgrade, &self->upgrade_len);
941 }
942 else if (nn_ws_match_token ("Connection:",
943 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
944
945 rc = nn_ws_match_value (CRLF, &pos, 1, 1, &conn, &conn_len);
946
947 /* The values here can be comma delimited, or they can be
948 listed as separate Connection headers. We only care about
949 the presence of the Upgrade header, and we're willing to
950 assume well-formedness. This crummy parse may let clients
951 send us a malformed header that we ought to reject, but
952 we'll just cite Postel's law here if anyone asks. */
953 self->conn = nn_strcasestr (conn, "upgrade");
954 if (self->conn != NULL) {
955 self->conn_len = strlen ("upgrade");
956 }
957 }
958 else if (nn_ws_match_token ("Sec-WebSocket-Version:",
959 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
960 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
961 &self->version, &self->version_len);
962 }
963 else if (nn_ws_match_token ("Sec-WebSocket-Protocol:",
964 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
965 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
966 &self->protocol, &self->protocol_len);
967 }
968 else if (nn_ws_match_token ("Sec-WebSocket-Extensions:",
969 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
970 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
971 &self->extensions, &self->extensions_len);
972 }
973 else if (nn_ws_match_token (CRLF,
974 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
975 /* Exit loop since all headers are parsed. */
976 break;
977 }
978 else {
979 /* Skip unknown headers. */
980 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
981 NULL, NULL);
982 }
983
984 if (rc != NN_WS_HANDSHAKE_MATCH)
985 return NN_WS_HANDSHAKE_INVALID;
986 }
987
988 /* Validate the opening handshake is now fully parsed. Additionally,
989 as per RFC 6455 section 4.1, the client should not send additional data
990 after the opening handshake, so this assertion validates upstream recv
991 logic prevented this case. */
992 nn_assert (strlen (pos) == 0);
993
994 /* TODO: protocol expectations below this point are hard-coded here as
995 an initial design decision. Perhaps in the future these values should
996 be settable via compile time (or run-time socket) options? */
997
998 /* These header fields are required as per RFC 6455 section 4.1. */
999 if (!self->host || !self->upgrade || !self->conn ||
1000 !self->key || !self->version) {
1001 self->response_code = NN_WS_HANDSHAKE_RESPONSE_WSPROTO;
1002 return NN_WS_HANDSHAKE_INVALID;
1003 }
1004
1005 /* RFC 6455 section 4.2.1.6 (version December 2011). */
1006 if (nn_ws_validate_value ("13", self->version,
1007 self->version_len, 1) != NN_WS_HANDSHAKE_MATCH) {
1008 self->response_code = NN_WS_HANDSHAKE_RESPONSE_WSVERSION;
1009 return NN_WS_HANDSHAKE_INVALID;
1010 }
1011
1012 /* RFC 6455 section 4.2.1.3 (version December 2011). */
1013 if (nn_ws_validate_value ("websocket", self->upgrade,
1014 self->upgrade_len, 1) != NN_WS_HANDSHAKE_MATCH) {
1015 self->response_code = NN_WS_HANDSHAKE_RESPONSE_WSPROTO;
1016 return NN_WS_HANDSHAKE_INVALID;
1017 }
1018
1019 /* RFC 6455 section 4.2.1.4 (version December 2011). */
1020 if (nn_ws_validate_value ("Upgrade", self->conn,
1021 self->conn_len, 1) != NN_WS_HANDSHAKE_MATCH) {
1022 self->response_code = NN_WS_HANDSHAKE_RESPONSE_WSPROTO;
1023 return NN_WS_HANDSHAKE_INVALID;
1024 }
1025
1026 /* At this point, client meets RFC 6455 compliance for opening handshake.
1027 Now it's time to check nanomsg-imposed required handshake values. */
1028 if (self->protocol) {
1029 /* Ensure the client SP is a compatible socket type. */
1030 for (i = 0; i < NN_WS_HANDSHAKE_SP_MAP_LEN; i++) {
1031 if (nn_ws_validate_value (NN_WS_HANDSHAKE_SP_MAP [i].ws_sp,
1032 self->protocol, self->protocol_len, 1)) {
1033
1034 if (self->pipebase->sock->socktype->protocol ==
1035 NN_WS_HANDSHAKE_SP_MAP [i].server) {
1036 self->response_code = NN_WS_HANDSHAKE_RESPONSE_OK;
1037 return NN_WS_HANDSHAKE_VALID;
1038 }
1039 else {
1040 self->response_code = NN_WS_HANDSHAKE_RESPONSE_NOTPEER;
1041 return NN_WS_HANDSHAKE_INVALID;
1042 }
1043 break;
1044 }
1045 }
1046
1047 self->response_code = NN_WS_HANDSHAKE_RESPONSE_UNKNOWNTYPE;
1048 return NN_WS_HANDSHAKE_INVALID;
1049 }
1050 else {
1051 /* Be permissive and generous here, assuming that if a protocol is
1052 not explicitly declared, PAIR is presumed. This enables
1053 interoperability with non-nanomsg remote peers, nominally by
1054 making the local socket PAIR type. For any other local
1055 socket type, we expect connection to be rejected as
1056 incompatible if the header is not specified. */
1057
1058 if (nn_pipebase_ispeer (self->pipebase, NN_PAIR)) {
1059 self->response_code = NN_WS_HANDSHAKE_RESPONSE_OK;
1060 return NN_WS_HANDSHAKE_VALID;
1061 }
1062 else {
1063 self->response_code = NN_WS_HANDSHAKE_RESPONSE_NOTPEER;
1064 return NN_WS_HANDSHAKE_INVALID;
1065 }
1066 }
1067 }
1068
nn_ws_handshake_parse_server_response(struct nn_ws_handshake * self)1069 static int nn_ws_handshake_parse_server_response (struct nn_ws_handshake *self)
1070 {
1071 /* As per RFC 6455 section 1.7, this parser is not intended to be a
1072 general-purpose parser for arbitrary HTTP headers. As with the design
1073 philosophy of nanomsg, application-specific exchanges are better
1074 reserved for accepted connections, not as fields within these
1075 headers. */
1076
1077 int rc;
1078 const char *pos;
1079
1080 /* Guarantee that a NULL terminator exists to enable treating this
1081 recv buffer like a string. The lack of such would indicate a failure
1082 upstream to catch a buffer overflow. */
1083 nn_assert (memchr (self->response, '\0', sizeof (self->response)));
1084
1085 /* Having found the NULL terminator, from this point forward string
1086 functions may be used. */
1087 nn_assert (strlen (self->response) < sizeof (self->response));
1088
1089 pos = self->response;
1090
1091 /* Is the response from the server fully received? */
1092 if (!strstr (pos, NN_WS_HANDSHAKE_TERMSEQ))
1093 return NN_WS_HANDSHAKE_RECV_MORE;
1094
1095 self->status_code = NULL;
1096 self->reason_phrase = NULL;
1097 self->server = NULL;
1098 self->accept_key = NULL;
1099 self->upgrade = NULL;
1100 self->conn = NULL;
1101 self->version = NULL;
1102 self->protocol = NULL;
1103
1104 self->status_code_len = 0;
1105 self->reason_phrase_len = 0;
1106 self->server_len = 0;
1107 self->accept_key_len = 0;
1108 self->upgrade_len = 0;
1109 self->conn_len = 0;
1110 self->version_len = 0;
1111 self->protocol_len = 0;
1112
1113 /* RFC 7230 3.1.2 Status Line: HTTP Version. */
1114 if (!nn_ws_match_token ("HTTP/1.1\x20", &pos, 0, 0))
1115 return NN_WS_HANDSHAKE_RECV_MORE;
1116
1117 /* RFC 7230 3.1.2 Status Line: Status Code. */
1118 if (!nn_ws_match_value ("\x20", &pos, 0, 0, &self->status_code,
1119 &self->status_code_len))
1120 return NN_WS_HANDSHAKE_RECV_MORE;
1121
1122 /* RFC 7230 3.1.2 Status Line: Reason Phrase. */
1123 if (!nn_ws_match_value (CRLF, &pos, 0, 0,
1124 &self->reason_phrase, &self->reason_phrase_len))
1125 return NN_WS_HANDSHAKE_RECV_MORE;
1126
1127 /* It's expected the current position is now at the first
1128 header field. Match them one by one. */
1129 while (strlen (pos))
1130 {
1131 if (nn_ws_match_token ("Server:", &pos, 1, 0)) {
1132 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
1133 &self->server, &self->server_len);
1134 }
1135 else if (nn_ws_match_token ("Sec-WebSocket-Accept:",
1136 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
1137 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
1138 &self->accept_key, &self->accept_key_len);
1139 }
1140 else if (nn_ws_match_token ("Upgrade:",
1141 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
1142 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
1143 &self->upgrade, &self->upgrade_len);
1144 }
1145 else if (nn_ws_match_token ("Connection:",
1146 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
1147 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
1148 &self->conn, &self->conn_len);
1149 }
1150 else if (nn_ws_match_token ("Sec-WebSocket-Version-Server:",
1151 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
1152 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
1153 &self->version, &self->version_len);
1154 }
1155 else if (nn_ws_match_token ("Sec-WebSocket-Protocol-Server:",
1156 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
1157 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
1158 &self->protocol, &self->protocol_len);
1159 }
1160 else if (nn_ws_match_token ("Sec-WebSocket-Extensions:",
1161 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
1162 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
1163 &self->extensions, &self->extensions_len);
1164 }
1165 else if (nn_ws_match_token (CRLF,
1166 &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) {
1167 /* Exit loop since all headers are parsed. */
1168 break;
1169 }
1170 else {
1171 /* Skip unknown headers. */
1172 rc = nn_ws_match_value (CRLF, &pos, 1, 1,
1173 NULL, NULL);
1174 }
1175
1176 if (rc != NN_WS_HANDSHAKE_MATCH)
1177 return NN_WS_HANDSHAKE_RECV_MORE;
1178 }
1179
1180 /* Validate the opening handshake is now fully parsed. Additionally,
1181 as per RFC 6455 section 4.1, the client should not send additional data
1182 after the opening handshake, so this assertion validates upstream recv
1183 logic prevented this case. */
1184 nn_assert (strlen (pos) == 0);
1185
1186 /* TODO: protocol expectations below this point are hard-coded here as
1187 an initial design decision. Perhaps in the future these values should
1188 be settable via compile time (or run-time socket) options? */
1189
1190 /* These header fields are required as per RFC 6455 4.2.2. */
1191 if (!self->status_code || !self->upgrade || !self->conn ||
1192 !self->accept_key)
1193 return NN_WS_HANDSHAKE_INVALID;
1194
1195 /* TODO: Currently, we only handle a successful connection upgrade.
1196 Anything else is treated as a failed connection.
1197 Consider handling other scenarios like 3xx redirects. */
1198 if (nn_ws_validate_value ("101", self->status_code,
1199 self->status_code_len, 1) != NN_WS_HANDSHAKE_MATCH)
1200 return NN_WS_HANDSHAKE_INVALID;
1201
1202 /* RFC 6455 section 4.2.2.5.2 (version December 2011). */
1203 if (nn_ws_validate_value ("websocket", self->upgrade,
1204 self->upgrade_len, 1) != NN_WS_HANDSHAKE_MATCH)
1205 return NN_WS_HANDSHAKE_INVALID;
1206
1207 /* RFC 6455 section 4.2.2.5.3 (version December 2011). */
1208 if (nn_ws_validate_value ("Upgrade", self->conn,
1209 self->conn_len, 1) != NN_WS_HANDSHAKE_MATCH)
1210 return NN_WS_HANDSHAKE_INVALID;
1211
1212 /* RFC 6455 section 4.2.2.5.4 (version December 2011). */
1213 if (nn_ws_validate_value (self->expected_accept_key, self->accept_key,
1214 self->accept_key_len, 1) != NN_WS_HANDSHAKE_MATCH)
1215 return NN_WS_HANDSHAKE_INVALID;
1216
1217 /* Server response meets RFC 6455 compliance for opening handshake. */
1218 return NN_WS_HANDSHAKE_VALID;
1219 }
1220
nn_ws_handshake_client_request(struct nn_ws_handshake * self)1221 static void nn_ws_handshake_client_request (struct nn_ws_handshake *self)
1222 {
1223 struct nn_iovec open_request;
1224 size_t encoded_key_len;
1225 int rc;
1226 unsigned i;
1227
1228 /* Generate random 16-byte key as per RFC 6455 4.1 */
1229 uint8_t rand_key [16];
1230
1231 /* Known length required to base64 encode above random key plus
1232 string NULL terminator. */
1233 char encoded_key [24 + 1];
1234
1235 nn_random_generate (rand_key, sizeof (rand_key));
1236
1237 rc = nn_base64_encode (rand_key, sizeof (rand_key),
1238 encoded_key, sizeof (encoded_key));
1239 nn_assert (rc >=0);
1240
1241 encoded_key_len = strlen (encoded_key);
1242
1243 nn_assert (encoded_key_len == sizeof (encoded_key) - 1);
1244
1245 /* Pre-calculated expected Accept Key value as per
1246 RFC 6455 section 4.2.2.5.4 (version December 2011). */
1247 rc = nn_ws_handshake_hash_key (encoded_key, encoded_key_len,
1248 self->expected_accept_key, sizeof (self->expected_accept_key));
1249
1250 nn_assert (rc == NN_WS_HANDSHAKE_ACCEPT_KEY_LEN);
1251
1252 /* Lookup SP header value. */
1253 for (i = 0; i < NN_WS_HANDSHAKE_SP_MAP_LEN; i++) {
1254 if (NN_WS_HANDSHAKE_SP_MAP [i].client ==
1255 self->pipebase->sock->socktype->protocol) {
1256 break;
1257 }
1258 }
1259
1260 /* Guarantee that the socket type was found in the map. */
1261 nn_assert (i < NN_WS_HANDSHAKE_SP_MAP_LEN);
1262
1263 sprintf (self->opening_hs,
1264 "GET %s HTTP/1.1\r\n"
1265 "Host: %s\r\n"
1266 "Upgrade: websocket\r\n"
1267 "Connection: Upgrade\r\n"
1268 "Sec-WebSocket-Key: %s\r\n"
1269 "Sec-WebSocket-Version: 13\r\n"
1270 "Sec-WebSocket-Protocol: %s\r\n\r\n",
1271 self->resource, self->remote_host, encoded_key,
1272 NN_WS_HANDSHAKE_SP_MAP[i].ws_sp);
1273
1274 open_request.iov_len = strlen (self->opening_hs);
1275 open_request.iov_base = self->opening_hs;
1276
1277 nn_usock_send (self->usock, &open_request, 1);
1278 }
1279
nn_ws_handshake_server_reply(struct nn_ws_handshake * self)1280 static void nn_ws_handshake_server_reply (struct nn_ws_handshake *self)
1281 {
1282 struct nn_iovec response;
1283 char *code;
1284 char *version;
1285 char *protocol;
1286 int rc;
1287
1288 /* Allow room for NULL terminator. */
1289 char accept_key [NN_WS_HANDSHAKE_ACCEPT_KEY_LEN + 1];
1290
1291 memset (self->response, 0, sizeof (self->response));
1292
1293 if (self->response_code == NN_WS_HANDSHAKE_RESPONSE_OK) {
1294 /* Upgrade connection as per RFC 6455 section 4.2.2. */
1295
1296 rc = nn_ws_handshake_hash_key (self->key, self->key_len,
1297 accept_key, sizeof (accept_key));
1298 nn_assert (rc >= 0);
1299
1300 nn_assert (strlen (accept_key) == NN_WS_HANDSHAKE_ACCEPT_KEY_LEN);
1301
1302 protocol = nn_alloc (self->protocol_len + 1, "WebSocket protocol");
1303 alloc_assert (protocol);
1304 strncpy (protocol, self->protocol, self->protocol_len);
1305 protocol [self->protocol_len] = '\0';
1306
1307 sprintf (self->response,
1308 "HTTP/1.1 101 Switching Protocols\r\n"
1309 "Upgrade: websocket\r\n"
1310 "Connection: Upgrade\r\n"
1311 "Sec-WebSocket-Accept: %s\r\n"
1312 "Sec-WebSocket-Protocol: %s\r\n\r\n",
1313 accept_key, protocol);
1314
1315 nn_free (protocol);
1316 }
1317 else {
1318 /* Fail the connection with a helpful hint. */
1319 switch (self->response_code) {
1320 case NN_WS_HANDSHAKE_RESPONSE_TOO_BIG:
1321 code = "400 Opening Handshake Too Long";
1322 break;
1323 case NN_WS_HANDSHAKE_RESPONSE_WSPROTO:
1324 code = "400 Cannot Have Body";
1325 break;
1326 case NN_WS_HANDSHAKE_RESPONSE_WSVERSION:
1327 code = "400 Unsupported WebSocket Version";
1328 break;
1329 case NN_WS_HANDSHAKE_RESPONSE_NNPROTO:
1330 code = "400 Missing nanomsg Required Headers";
1331 break;
1332 case NN_WS_HANDSHAKE_RESPONSE_NOTPEER:
1333 code = "400 Incompatible Socket Type";
1334 break;
1335 case NN_WS_HANDSHAKE_RESPONSE_UNKNOWNTYPE:
1336 code = "400 Unrecognized Socket Type";
1337 break;
1338 default:
1339 /* Unexpected failure response. */
1340 nn_assert (0);
1341 break;
1342 }
1343
1344 version = nn_alloc (self->version_len + 1, "WebSocket version");
1345 alloc_assert (version);
1346 strncpy (version, self->version, self->version_len);
1347 version [self->version_len] = '\0';
1348
1349 /* Fail connection as per RFC 6455 4.4. */
1350 sprintf (self->response,
1351 "HTTP/1.1 %s\r\n"
1352 "Sec-WebSocket-Version: %s\r\n",
1353 code, version);
1354
1355 nn_free (version);
1356 }
1357
1358 response.iov_len = strlen (self->response);
1359 response.iov_base = &self->response;
1360
1361 nn_usock_send (self->usock, &response, 1);
1362
1363 return;
1364 }
1365
nn_ws_handshake_hash_key(const char * key,size_t key_len,char * hashed,size_t hashed_len)1366 static int nn_ws_handshake_hash_key (const char *key, size_t key_len,
1367 char *hashed, size_t hashed_len)
1368 {
1369 int rc;
1370 unsigned i;
1371 struct nn_sha1 hash;
1372
1373 nn_sha1_init (&hash);
1374
1375 for (i = 0; i < key_len; i++)
1376 nn_sha1_hashbyte (&hash, key [i]);
1377
1378 for (i = 0; i < strlen (NN_WS_HANDSHAKE_MAGIC_GUID); i++)
1379 nn_sha1_hashbyte (&hash, NN_WS_HANDSHAKE_MAGIC_GUID [i]);
1380
1381 rc = nn_base64_encode (nn_sha1_result (&hash),
1382 sizeof (hash.state), hashed, hashed_len);
1383
1384 return rc;
1385 }
1386
1387