ws_engine.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MPL-2.0 */
2 
3 #include "precompiled.hpp"
4 
5 #ifdef ZMQ_USE_NSS
6 #include <secoid.h>
7 #include <sechash.h>
8 #define SHA_DIGEST_LENGTH 20
9 #elif defined ZMQ_USE_BUILTIN_SHA1
10 #include "../external/sha1/sha1.h"
11 #elif defined ZMQ_USE_GNUTLS
12 #define SHA_DIGEST_LENGTH 20
13 #include <gnutls/gnutls.h>
14 #include <gnutls/crypto.h>
15 #endif
16 
17 #if !defined ZMQ_HAVE_WINDOWS
18 #include <sys/types.h>
19 #include <unistd.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #ifdef ZMQ_HAVE_VXWORKS
24 #include <sockLib.h>
25 #endif
26 #endif
27 
28 #include <cstring>
29 
30 #include "compat.hpp"
31 #include "tcp.hpp"
32 #include "ws_engine.hpp"
33 #include "session_base.hpp"
34 #include "err.hpp"
35 #include "ip.hpp"
36 #include "random.hpp"
37 #include "ws_decoder.hpp"
38 #include "ws_encoder.hpp"
39 #include "null_mechanism.hpp"
40 #include "plain_server.hpp"
41 #include "plain_client.hpp"
42 
43 #ifdef ZMQ_HAVE_CURVE
44 #include "curve_client.hpp"
45 #include "curve_server.hpp"
46 #endif
47 
48 // OSX uses a different name for this socket option
49 #ifndef IPV6_ADD_MEMBERSHIP
50 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
51 #endif
52 
53 #ifdef __APPLE__
54 #include <TargetConditionals.h>
55 #endif
56 
57 static int
58 encode_base64 (const unsigned char *in_, int in_len_, char *out_, int out_len_);
59 
60 static void compute_accept_key (char *key_,
61  unsigned char hash_[SHA_DIGEST_LENGTH]);
62 
64  const options_t &options_,
65  const endpoint_uri_pair_t &endpoint_uri_pair_,
66  const ws_address_t &address_,
67  bool client_) :
68  stream_engine_base_t (fd_, options_, endpoint_uri_pair_, true),
69  _client (client_),
70  _address (address_),
71  _client_handshake_state (client_handshake_initial),
72  _server_handshake_state (handshake_initial),
73  _header_name_position (0),
74  _header_value_position (0),
75  _header_upgrade_websocket (false),
76  _header_connection_upgrade (false),
77  _heartbeat_timeout (0)
78 {
79  memset (_websocket_key, 0, MAX_HEADER_VALUE_LENGTH + 1);
81  memset (_websocket_protocol, 0, 256);
82 
85  _close_msg.init ();
86 
87  if (_options.heartbeat_interval > 0) {
89  if (_heartbeat_timeout == -1)
91  }
92 }
93 
95 {
96  _close_msg.close ();
97 }
98 
100 {
101  if (_client) {
102  const char *protocol;
103  if (_options.mechanism == ZMQ_NULL)
104  protocol = "ZWS2.0/NULL,ZWS2.0";
105  else if (_options.mechanism == ZMQ_PLAIN)
106  protocol = "ZWS2.0/PLAIN";
107 #ifdef ZMQ_HAVE_CURVE
108  else if (_options.mechanism == ZMQ_CURVE)
109  protocol = "ZWS2.0/CURVE";
110 #endif
111  else {
112  // Avoid uninitialized variable error breaking UWP build
113  protocol = "";
114  assert (false);
115  }
116 
117  unsigned char nonce[16];
118  int *p = reinterpret_cast<int *> (nonce);
119 
120  // The nonce doesn't have to be secure one, it is just use to avoid proxy cache
121  *p = zmq::generate_random ();
122  *(p + 1) = zmq::generate_random ();
123  *(p + 2) = zmq::generate_random ();
124  *(p + 3) = zmq::generate_random ();
125 
126  int size =
127  encode_base64 (nonce, 16, _websocket_key, MAX_HEADER_VALUE_LENGTH);
128  assert (size > 0);
129 
130  size = snprintf (
131  reinterpret_cast<char *> (_write_buffer), WS_BUFFER_SIZE,
132  "GET %s HTTP/1.1\r\n"
133  "Host: %s\r\n"
134  "Upgrade: websocket\r\n"
135  "Connection: Upgrade\r\n"
136  "Sec-WebSocket-Key: %s\r\n"
137  "Sec-WebSocket-Protocol: %s\r\n"
138  "Sec-WebSocket-Version: 13\r\n\r\n",
139  _address.path (), _address.host (), _websocket_key, protocol);
140  assert (size > 0 && size < WS_BUFFER_SIZE);
141  _outpos = _write_buffer;
142  _outsize = size;
143  set_pollout ();
144  }
145 }
146 
148 {
149  start_ws_handshake ();
150  set_pollin ();
151  in_event ();
152 }
153 
155 {
156  const int rc = msg_->init_size (_options.routing_id_size);
157  errno_assert (rc == 0);
158  if (_options.routing_id_size > 0)
159  memcpy (msg_->data (), _options.routing_id, _options.routing_id_size);
161 
162  return 0;
163 }
164 
166 {
167  if (_options.recv_routing_id) {
169  const int rc = session ()->push_msg (msg_);
170  errno_assert (rc == 0);
171  } else {
172  int rc = msg_->close ();
173  errno_assert (rc == 0);
174  rc = msg_->init ();
175  errno_assert (rc == 0);
176  }
177 
178  _process_msg = &ws_engine_t::push_msg_to_session;
179 
180  return 0;
181 }
182 
183 bool zmq::ws_engine_t::select_protocol (const char *protocol_)
184 {
185  if (_options.mechanism == ZMQ_NULL && (strcmp ("ZWS2.0", protocol_) == 0)) {
186  _next_msg = static_cast<int (stream_engine_base_t::*) (msg_t *)> (
188  _process_msg = static_cast<int (stream_engine_base_t::*) (msg_t *)> (
190 
191  // No mechanism in place, enabling heartbeat
192  if (_options.heartbeat_interval > 0 && !_has_heartbeat_timer) {
193  add_timer (_options.heartbeat_interval, heartbeat_ivl_timer_id);
194  _has_heartbeat_timer = true;
195  }
196 
197  return true;
198  }
199  if (_options.mechanism == ZMQ_NULL
200  && strcmp ("ZWS2.0/NULL", protocol_) == 0) {
201  _mechanism = new (std::nothrow)
202  null_mechanism_t (session (), _peer_address, _options);
203  alloc_assert (_mechanism);
204  return true;
205  } else if (_options.mechanism == ZMQ_PLAIN
206  && strcmp ("ZWS2.0/PLAIN", protocol_) == 0) {
207  if (_options.as_server)
208  _mechanism = new (std::nothrow)
209  plain_server_t (session (), _peer_address, _options);
210  else
211  _mechanism =
212  new (std::nothrow) plain_client_t (session (), _options);
213  alloc_assert (_mechanism);
214  return true;
215  }
216 #ifdef ZMQ_HAVE_CURVE
217  else if (_options.mechanism == ZMQ_CURVE
218  && strcmp ("ZWS2.0/CURVE", protocol_) == 0) {
219  if (_options.as_server)
220  _mechanism = new (std::nothrow)
221  curve_server_t (session (), _peer_address, _options, false);
222  else
223  _mechanism =
224  new (std::nothrow) curve_client_t (session (), _options, false);
225  alloc_assert (_mechanism);
226  return true;
227  }
228 #endif
229 
230  return false;
231 }
232 
234 {
235  bool complete;
236 
237  if (_client)
238  complete = client_handshake ();
239  else
240  complete = server_handshake ();
241 
242  if (complete) {
243  _encoder =
244  new (std::nothrow) ws_encoder_t (_options.out_batch_size, _client);
245  alloc_assert (_encoder);
246 
247  _decoder = new (std::nothrow)
248  ws_decoder_t (_options.in_batch_size, _options.maxmsgsize,
249  _options.zero_copy, !_client);
250  alloc_assert (_decoder);
251 
252  socket ()->event_handshake_succeeded (_endpoint_uri_pair, 0);
253 
254  set_pollout ();
255  }
256 
257  return complete;
258 }
259 
261 {
262  const int nbytes = read (_read_buffer, WS_BUFFER_SIZE);
263  if (nbytes == -1) {
264  if (errno != EAGAIN)
266  return false;
267  }
268 
269  _inpos = _read_buffer;
270  _insize = nbytes;
271 
272  while (_insize > 0) {
273  const char c = static_cast<char> (*_inpos);
274 
275  switch (_server_handshake_state) {
276  case handshake_initial:
277  if (c == 'G')
278  _server_handshake_state = request_line_G;
279  else
280  _server_handshake_state = handshake_error;
281  break;
282  case request_line_G:
283  if (c == 'E')
284  _server_handshake_state = request_line_GE;
285  else
286  _server_handshake_state = handshake_error;
287  break;
288  case request_line_GE:
289  if (c == 'T')
290  _server_handshake_state = request_line_GET;
291  else
292  _server_handshake_state = handshake_error;
293  break;
294  case request_line_GET:
295  if (c == ' ')
296  _server_handshake_state = request_line_GET_space;
297  else
298  _server_handshake_state = handshake_error;
299  break;
301  if (c == '\r' || c == '\n')
302  _server_handshake_state = handshake_error;
303  // TODO: instead of check what is not allowed check what is allowed
304  if (c != ' ')
305  _server_handshake_state = request_line_resource;
306  else
307  _server_handshake_state = request_line_GET_space;
308  break;
310  if (c == '\r' || c == '\n')
311  _server_handshake_state = handshake_error;
312  else if (c == ' ')
313  _server_handshake_state = request_line_resource_space;
314  else
315  _server_handshake_state = request_line_resource;
316  break;
318  if (c == 'H')
319  _server_handshake_state = request_line_H;
320  else
321  _server_handshake_state = handshake_error;
322  break;
323  case request_line_H:
324  if (c == 'T')
325  _server_handshake_state = request_line_HT;
326  else
327  _server_handshake_state = handshake_error;
328  break;
329  case request_line_HT:
330  if (c == 'T')
331  _server_handshake_state = request_line_HTT;
332  else
333  _server_handshake_state = handshake_error;
334  break;
335  case request_line_HTT:
336  if (c == 'P')
337  _server_handshake_state = request_line_HTTP;
338  else
339  _server_handshake_state = handshake_error;
340  break;
341  case request_line_HTTP:
342  if (c == '/')
343  _server_handshake_state = request_line_HTTP_slash;
344  else
345  _server_handshake_state = handshake_error;
346  break;
348  if (c == '1')
349  _server_handshake_state = request_line_HTTP_slash_1;
350  else
351  _server_handshake_state = handshake_error;
352  break;
354  if (c == '.')
355  _server_handshake_state = request_line_HTTP_slash_1_dot;
356  else
357  _server_handshake_state = handshake_error;
358  break;
360  if (c == '1')
361  _server_handshake_state = request_line_HTTP_slash_1_dot_1;
362  else
363  _server_handshake_state = handshake_error;
364  break;
366  if (c == '\r')
367  _server_handshake_state = request_line_cr;
368  else
369  _server_handshake_state = handshake_error;
370  break;
371  case request_line_cr:
372  if (c == '\n')
373  _server_handshake_state = header_field_begin_name;
374  else
375  _server_handshake_state = handshake_error;
376  break;
378  switch (c) {
379  case '\r':
380  _server_handshake_state = handshake_end_line_cr;
381  break;
382  case '\n':
383  _server_handshake_state = handshake_error;
384  break;
385  default:
386  _header_name[0] = c;
387  _header_name_position = 1;
388  _server_handshake_state = header_field_name;
389  break;
390  }
391  break;
392  case header_field_name:
393  if (c == '\r' || c == '\n')
394  _server_handshake_state = handshake_error;
395  else if (c == ':') {
396  _header_name[_header_name_position] = '\0';
397  _server_handshake_state = header_field_colon;
398  } else if (_header_name_position + 1 > MAX_HEADER_NAME_LENGTH)
399  _server_handshake_state = handshake_error;
400  else {
401  _header_name[_header_name_position] = c;
402  _header_name_position++;
403  _server_handshake_state = header_field_name;
404  }
405  break;
406  case header_field_colon:
408  if (c == '\n')
409  _server_handshake_state = handshake_error;
410  else if (c == '\r')
411  _server_handshake_state = header_field_cr;
412  else if (c == ' ')
413  _server_handshake_state = header_field_value_trailing_space;
414  else {
415  _header_value[0] = c;
416  _header_value_position = 1;
417  _server_handshake_state = header_field_value;
418  }
419  break;
420  case header_field_value:
421  if (c == '\n')
422  _server_handshake_state = handshake_error;
423  else if (c == '\r') {
424  _header_value[_header_value_position] = '\0';
425 
426  if (strcasecmp ("upgrade", _header_name) == 0)
427  _header_upgrade_websocket =
428  strcasecmp ("websocket", _header_value) == 0;
429  else if (strcasecmp ("connection", _header_name) == 0) {
430  char *rest = NULL;
431  char *element = strtok_r (_header_value, ",", &rest);
432  while (element != NULL) {
433  while (*element == ' ')
434  element++;
435  if (strcasecmp ("upgrade", element) == 0) {
436  _header_connection_upgrade = true;
437  break;
438  }
439  element = strtok_r (NULL, ",", &rest);
440  }
441  } else if (strcasecmp ("Sec-WebSocket-Key", _header_name)
442  == 0)
443  strcpy_s (_websocket_key, _header_value);
444  else if (strcasecmp ("Sec-WebSocket-Protocol", _header_name)
445  == 0) {
446  // Currently only the ZWS2.0 is supported
447  // Sec-WebSocket-Protocol can appear multiple times or be a comma separated list
448  // if _websocket_protocol is already set we skip the check
449  if (_websocket_protocol[0] == '\0') {
450  char *rest = NULL;
451  char *p = strtok_r (_header_value, ",", &rest);
452  while (p != NULL) {
453  if (*p == ' ')
454  p++;
455 
456  if (select_protocol (p)) {
457  strcpy_s (_websocket_protocol, p);
458  break;
459  }
460 
461  p = strtok_r (NULL, ",", &rest);
462  }
463  }
464  }
465 
466  _server_handshake_state = header_field_cr;
467  } else if (_header_value_position + 1 > MAX_HEADER_VALUE_LENGTH)
468  _server_handshake_state = handshake_error;
469  else {
470  _header_value[_header_value_position] = c;
471  _header_value_position++;
472  _server_handshake_state = header_field_value;
473  }
474  break;
475  case header_field_cr:
476  if (c == '\n')
477  _server_handshake_state = header_field_begin_name;
478  else
479  _server_handshake_state = handshake_error;
480  break;
482  if (c == '\n') {
483  if (_header_connection_upgrade && _header_upgrade_websocket
484  && _websocket_protocol[0] != '\0'
485  && _websocket_key[0] != '\0') {
486  _server_handshake_state = handshake_complete;
487 
488  unsigned char hash[SHA_DIGEST_LENGTH];
489  compute_accept_key (_websocket_key, hash);
490 
491  const int accept_key_len = encode_base64 (
492  hash, SHA_DIGEST_LENGTH, _websocket_accept,
494  assert (accept_key_len > 0);
495  _websocket_accept[accept_key_len] = '\0';
496 
497  const int written =
498  snprintf (reinterpret_cast<char *> (_write_buffer),
500  "HTTP/1.1 101 Switching Protocols\r\n"
501  "Upgrade: websocket\r\n"
502  "Connection: Upgrade\r\n"
503  "Sec-WebSocket-Accept: %s\r\n"
504  "Sec-WebSocket-Protocol: %s\r\n"
505  "\r\n",
506  _websocket_accept, _websocket_protocol);
507  assert (written >= 0 && written < WS_BUFFER_SIZE);
508  _outpos = _write_buffer;
509  _outsize = written;
510 
511  _inpos++;
512  _insize--;
513 
514  return true;
515  }
516  _server_handshake_state = handshake_error;
517  } else
518  _server_handshake_state = handshake_error;
519  break;
520  default:
521  assert (false);
522  }
523 
524  _inpos++;
525  _insize--;
526 
527  if (_server_handshake_state == handshake_error) {
528  // TODO: send bad request
529 
530  socket ()->event_handshake_failed_protocol (
531  _endpoint_uri_pair, ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED);
532 
534  return false;
535  }
536  }
537  return false;
538 }
539 
541 {
542  const int nbytes = read (_read_buffer, WS_BUFFER_SIZE);
543  if (nbytes == -1) {
544  if (errno != EAGAIN)
546  return false;
547  }
548 
549  _inpos = _read_buffer;
550  _insize = nbytes;
551 
552  while (_insize > 0) {
553  const char c = static_cast<char> (*_inpos);
554 
555  switch (_client_handshake_state) {
557  if (c == 'H')
558  _client_handshake_state = response_line_H;
559  else
560  _client_handshake_state = client_handshake_error;
561  break;
562  case response_line_H:
563  if (c == 'T')
564  _client_handshake_state = response_line_HT;
565  else
566  _client_handshake_state = client_handshake_error;
567  break;
568  case response_line_HT:
569  if (c == 'T')
570  _client_handshake_state = response_line_HTT;
571  else
572  _client_handshake_state = client_handshake_error;
573  break;
574  case response_line_HTT:
575  if (c == 'P')
576  _client_handshake_state = response_line_HTTP;
577  else
578  _client_handshake_state = client_handshake_error;
579  break;
580  case response_line_HTTP:
581  if (c == '/')
582  _client_handshake_state = response_line_HTTP_slash;
583  else
584  _client_handshake_state = client_handshake_error;
585  break;
587  if (c == '1')
588  _client_handshake_state = response_line_HTTP_slash_1;
589  else
590  _client_handshake_state = client_handshake_error;
591  break;
593  if (c == '.')
594  _client_handshake_state = response_line_HTTP_slash_1_dot;
595  else
596  _client_handshake_state = client_handshake_error;
597  break;
599  if (c == '1')
600  _client_handshake_state = response_line_HTTP_slash_1_dot_1;
601  else
602  _client_handshake_state = client_handshake_error;
603  break;
605  if (c == ' ')
606  _client_handshake_state =
608  else
609  _client_handshake_state = client_handshake_error;
610  break;
612  if (c == ' ')
613  _client_handshake_state =
615  else if (c == '1')
616  _client_handshake_state = response_line_status_1;
617  else
618  _client_handshake_state = client_handshake_error;
619  break;
621  if (c == '0')
622  _client_handshake_state = response_line_status_10;
623  else
624  _client_handshake_state = client_handshake_error;
625  break;
627  if (c == '1')
628  _client_handshake_state = response_line_status_101;
629  else
630  _client_handshake_state = client_handshake_error;
631  break;
633  if (c == ' ')
634  _client_handshake_state = response_line_status_101_space;
635  else
636  _client_handshake_state = client_handshake_error;
637  break;
639  if (c == ' ')
640  _client_handshake_state = response_line_status_101_space;
641  else if (c == 'S')
642  _client_handshake_state = response_line_s;
643  else
644  _client_handshake_state = client_handshake_error;
645  break;
646  case response_line_s:
647  if (c == 'w')
648  _client_handshake_state = response_line_sw;
649  else
650  _client_handshake_state = client_handshake_error;
651  break;
652  case response_line_sw:
653  if (c == 'i')
654  _client_handshake_state = response_line_swi;
655  else
656  _client_handshake_state = client_handshake_error;
657  break;
658  case response_line_swi:
659  if (c == 't')
660  _client_handshake_state = response_line_swit;
661  else
662  _client_handshake_state = client_handshake_error;
663  break;
664  case response_line_swit:
665  if (c == 'c')
666  _client_handshake_state = response_line_switc;
667  else
668  _client_handshake_state = client_handshake_error;
669  break;
670  case response_line_switc:
671  if (c == 'h')
672  _client_handshake_state = response_line_switch;
673  else
674  _client_handshake_state = client_handshake_error;
675  break;
677  if (c == 'i')
678  _client_handshake_state = response_line_switchi;
679  else
680  _client_handshake_state = client_handshake_error;
681  break;
683  if (c == 'n')
684  _client_handshake_state = response_line_switchin;
685  else
686  _client_handshake_state = client_handshake_error;
687  break;
689  if (c == 'g')
690  _client_handshake_state = response_line_switching;
691  else
692  _client_handshake_state = client_handshake_error;
693  break;
695  if (c == ' ')
696  _client_handshake_state = response_line_switching_space;
697  else
698  _client_handshake_state = client_handshake_error;
699  break;
701  if (c == 'P')
702  _client_handshake_state = response_line_p;
703  else
704  _client_handshake_state = client_handshake_error;
705  break;
706  case response_line_p:
707  if (c == 'r')
708  _client_handshake_state = response_line_pr;
709  else
710  _client_handshake_state = client_handshake_error;
711  break;
712  case response_line_pr:
713  if (c == 'o')
714  _client_handshake_state = response_line_pro;
715  else
716  _client_handshake_state = client_handshake_error;
717  break;
718  case response_line_pro:
719  if (c == 't')
720  _client_handshake_state = response_line_prot;
721  else
722  _client_handshake_state = client_handshake_error;
723  break;
724  case response_line_prot:
725  if (c == 'o')
726  _client_handshake_state = response_line_proto;
727  else
728  _client_handshake_state = client_handshake_error;
729  break;
730  case response_line_proto:
731  if (c == 'c')
732  _client_handshake_state = response_line_protoc;
733  else
734  _client_handshake_state = client_handshake_error;
735  break;
737  if (c == 'o')
738  _client_handshake_state = response_line_protoco;
739  else
740  _client_handshake_state = client_handshake_error;
741  break;
743  if (c == 'l')
744  _client_handshake_state = response_line_protocol;
745  else
746  _client_handshake_state = client_handshake_error;
747  break;
749  if (c == 's')
750  _client_handshake_state = response_line_protocols;
751  else
752  _client_handshake_state = client_handshake_error;
753  break;
755  if (c == '\r')
756  _client_handshake_state = response_line_cr;
757  else
758  _client_handshake_state = client_handshake_error;
759  break;
760  case response_line_cr:
761  if (c == '\n')
762  _client_handshake_state = client_header_field_begin_name;
763  else
764  _client_handshake_state = client_handshake_error;
765  break;
767  switch (c) {
768  case '\r':
769  _client_handshake_state = client_handshake_end_line_cr;
770  break;
771  case '\n':
772  _client_handshake_state = client_handshake_error;
773  break;
774  default:
775  _header_name[0] = c;
776  _header_name_position = 1;
777  _client_handshake_state = client_header_field_name;
778  break;
779  }
780  break;
782  if (c == '\r' || c == '\n')
783  _client_handshake_state = client_handshake_error;
784  else if (c == ':') {
785  _header_name[_header_name_position] = '\0';
786  _client_handshake_state = client_header_field_colon;
787  } else if (_header_name_position + 1 > MAX_HEADER_NAME_LENGTH)
788  _client_handshake_state = client_handshake_error;
789  else {
790  _header_name[_header_name_position] = c;
791  _header_name_position++;
792  _client_handshake_state = client_header_field_name;
793  }
794  break;
797  if (c == '\n')
798  _client_handshake_state = client_handshake_error;
799  else if (c == '\r')
800  _client_handshake_state = client_header_field_cr;
801  else if (c == ' ')
802  _client_handshake_state =
804  else {
805  _header_value[0] = c;
806  _header_value_position = 1;
807  _client_handshake_state = client_header_field_value;
808  }
809  break;
811  if (c == '\n')
812  _client_handshake_state = client_handshake_error;
813  else if (c == '\r') {
814  _header_value[_header_value_position] = '\0';
815 
816  if (strcasecmp ("upgrade", _header_name) == 0)
817  _header_upgrade_websocket =
818  strcasecmp ("websocket", _header_value) == 0;
819  else if (strcasecmp ("connection", _header_name) == 0)
820  _header_connection_upgrade =
821  strcasecmp ("upgrade", _header_value) == 0;
822  else if (strcasecmp ("Sec-WebSocket-Accept", _header_name)
823  == 0)
824  strcpy_s (_websocket_accept, _header_value);
825  else if (strcasecmp ("Sec-WebSocket-Protocol", _header_name)
826  == 0) {
827  if (_mechanism) {
828  _client_handshake_state = client_handshake_error;
829  break;
830  }
831  if (select_protocol (_header_value))
832  strcpy_s (_websocket_protocol, _header_value);
833  }
834  _client_handshake_state = client_header_field_cr;
835  } else if (_header_value_position + 1 > MAX_HEADER_VALUE_LENGTH)
836  _client_handshake_state = client_handshake_error;
837  else {
838  _header_value[_header_value_position] = c;
839  _header_value_position++;
840  _client_handshake_state = client_header_field_value;
841  }
842  break;
844  if (c == '\n')
845  _client_handshake_state = client_header_field_begin_name;
846  else
847  _client_handshake_state = client_handshake_error;
848  break;
850  if (c == '\n') {
851  if (_header_connection_upgrade && _header_upgrade_websocket
852  && _websocket_protocol[0] != '\0'
853  && _websocket_accept[0] != '\0') {
854  _client_handshake_state = client_handshake_complete;
855 
856  // TODO: validate accept key
857 
858  _inpos++;
859  _insize--;
860 
861  return true;
862  }
863  _client_handshake_state = client_handshake_error;
864  } else
865  _client_handshake_state = client_handshake_error;
866  break;
867  default:
868  assert (false);
869  }
870 
871  _inpos++;
872  _insize--;
873 
874  if (_client_handshake_state == client_handshake_error) {
875  socket ()->event_handshake_failed_protocol (
876  _endpoint_uri_pair, ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED);
877 
879  return false;
880  }
881  }
882 
883  return false;
884 }
885 
887 {
888  zmq_assert (_mechanism != NULL);
889 
890  // with WS engine, ping and pong commands are control messages and should not go through any mechanism
891  if (msg_->is_ping () || msg_->is_pong () || msg_->is_close_cmd ()) {
892  if (process_command_message (msg_) == -1)
893  return -1;
894  } else if (_mechanism->decode (msg_) == -1)
895  return -1;
896 
897  if (_has_timeout_timer) {
898  _has_timeout_timer = false;
899  cancel_timer (heartbeat_timeout_timer_id);
900  }
901 
902  if (msg_->flags () & msg_t::command && !msg_->is_ping ()
903  && !msg_->is_pong () && !msg_->is_close_cmd ())
904  process_command_message (msg_);
905 
906  if (_metadata)
907  msg_->set_metadata (_metadata);
908  if (session ()->push_msg (msg_) == -1) {
909  if (errno == EAGAIN)
911  return -1;
912  }
913  return 0;
914 }
915 
917 {
918  int rc = msg_->move (_close_msg);
919  errno_assert (rc == 0);
920 
921  _next_msg = static_cast<int (stream_engine_base_t::*) (msg_t *)> (
923 
924  return rc;
925 }
926 
928 {
929  LIBZMQ_UNUSED (msg_);
930  _next_msg = static_cast<int (stream_engine_base_t::*) (msg_t *)> (
932 
933  errno = EAGAIN;
934  return -1;
935 }
936 
938 {
939  LIBZMQ_UNUSED (msg_);
940  error (connection_error);
941  errno = ECONNRESET;
942  return -1;
943 }
944 
946 {
947  int rc = msg_->init ();
948  errno_assert (rc == 0);
950 
951  _next_msg = &ws_engine_t::pull_and_encode;
952  if (!_has_timeout_timer && _heartbeat_timeout > 0) {
953  add_timer (_heartbeat_timeout, heartbeat_timeout_timer_id);
954  _has_timeout_timer = true;
955  }
956 
957  return rc;
958 }
959 
960 
962 {
963  int rc = msg_->init ();
964  errno_assert (rc == 0);
966 
967  _next_msg = &ws_engine_t::pull_and_encode;
968  return rc;
969 }
970 
971 
973 {
974  if (msg_->is_ping ()) {
975  _next_msg = static_cast<int (stream_engine_base_t::*) (msg_t *)> (
977  out_event ();
978  } else if (msg_->is_close_cmd ()) {
979  int rc = _close_msg.copy (*msg_);
980  errno_assert (rc == 0);
981  _next_msg = static_cast<int (stream_engine_base_t::*) (msg_t *)> (
983  out_event ();
984  }
985 
986  return 0;
987 }
988 
989 static int
990 encode_base64 (const unsigned char *in_, int in_len_, char *out_, int out_len_)
991 {
992  static const unsigned char base64enc_tab[65] =
993  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
994 
995  int io = 0;
996  uint32_t v = 0;
997  int rem = 0;
998 
999  for (int ii = 0; ii < in_len_; ii++) {
1000  unsigned char ch;
1001  ch = in_[ii];
1002  v = (v << 8) | ch;
1003  rem += 8;
1004  while (rem >= 6) {
1005  rem -= 6;
1006  if (io >= out_len_)
1007  return -1; /* truncation is failure */
1008  out_[io++] = base64enc_tab[(v >> rem) & 63];
1009  }
1010  }
1011  if (rem) {
1012  v <<= (6 - rem);
1013  if (io >= out_len_)
1014  return -1; /* truncation is failure */
1015  out_[io++] = base64enc_tab[v & 63];
1016  }
1017  while (io & 3) {
1018  if (io >= out_len_)
1019  return -1; /* truncation is failure */
1020  out_[io++] = '=';
1021  }
1022  if (io >= out_len_)
1023  return -1; /* no room for null terminator */
1024  out_[io] = 0;
1025  return io;
1026 }
1027 
1028 static void compute_accept_key (char *key_, unsigned char *hash_)
1029 {
1030  const char *magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
1031 #ifdef ZMQ_USE_NSS
1032  unsigned int len;
1033  HASH_HashType type = HASH_GetHashTypeByOidTag (SEC_OID_SHA1);
1034  HASHContext *ctx = HASH_Create (type);
1035  assert (ctx);
1036 
1037  HASH_Begin (ctx);
1038  HASH_Update (ctx, (unsigned char *) key_, (unsigned int) strlen (key_));
1039  HASH_Update (ctx, (unsigned char *) magic_string,
1040  (unsigned int) strlen (magic_string));
1041  HASH_End (ctx, hash_, &len, SHA_DIGEST_LENGTH);
1042  HASH_Destroy (ctx);
1043 #elif defined ZMQ_USE_BUILTIN_SHA1
1044  sha1_ctxt ctx;
1045  SHA1_Init (&ctx);
1046  SHA1_Update (&ctx, (unsigned char *) key_, strlen (key_));
1047  SHA1_Update (&ctx, (unsigned char *) magic_string, strlen (magic_string));
1048 
1049  SHA1_Final (hash_, &ctx);
1050 #elif defined ZMQ_USE_GNUTLS
1051  gnutls_hash_hd_t hd;
1052  gnutls_hash_init (&hd, GNUTLS_DIG_SHA1);
1053  gnutls_hash (hd, key_, strlen (key_));
1054  gnutls_hash (hd, magic_string, strlen (magic_string));
1055  gnutls_hash_deinit (hd, hash_);
1056 #else
1057 #error "No sha1 implementation set"
1058 #endif
1059 }
zmq::ws_engine_t::handshake
bool handshake()
Definition: ws_engine.cpp:233
zmq::stream_engine_base_t::push_msg_to_session
int push_msg_to_session(msg_t *msg_)
Definition: stream_engine_base.cpp:662
zmq::msg_t::command
@ command
Definition: msg.hpp:56
zmq::response_line_p
@ response_line_p
Definition: ws_engine.hpp:78
zmq::ws_engine_t::_websocket_protocol
char _websocket_protocol[256]
Definition: ws_engine.hpp:146
zmq::ws_engine_t::process_command_message
int process_command_message(msg_t *msg_)
Definition: ws_engine.cpp:972
ip.hpp
zmq::ws_engine_t::routing_id_msg
int routing_id_msg(msg_t *msg_)
Definition: ws_engine.cpp:154
encode_base64
static int encode_base64(const unsigned char *in_, int in_len_, char *out_, int out_len_)
Definition: ws_engine.cpp:990
zmq::client_handshake_end_line_cr
@ client_handshake_end_line_cr
Definition: ws_engine.hpp:94
zmq::response_line_status_101
@ response_line_status_101
Definition: ws_engine.hpp:66
zmq::ws_engine_t::close_connection_after_close
int close_connection_after_close(msg_t *msg_)
Definition: ws_engine.cpp:937
ZMQ_CURVE
#define ZMQ_CURVE
Definition: zmq.h:364
zmq::handshake_end_line_cr
@ handshake_end_line_cr
Definition: ws_engine.hpp:45
NULL
NULL
Definition: test_security_zap.cpp:405
zmq::options_t
Definition: options.hpp:34
zmq::request_line_HTTP_slash_1
@ request_line_HTTP_slash_1
Definition: ws_engine.hpp:35
zmq::client_header_field_value
@ client_header_field_value
Definition: ws_engine.hpp:92
zmq::response_line_HTTP_slash_1
@ response_line_HTTP_slash_1
Definition: ws_engine.hpp:60
zmq::ws_engine_t::client_handshake
bool client_handshake()
Definition: ws_engine.cpp:540
zmq::client_handshake_initial
@ client_handshake_initial
Definition: ws_engine.hpp:54
zmq::client_header_field_colon
@ client_header_field_colon
Definition: ws_engine.hpp:90
zmq::response_line_protocols
@ response_line_protocols
Definition: ws_engine.hpp:86
EAGAIN
#define EAGAIN
Definition: errno.hpp:14
zmq::request_line_HTTP_slash_1_dot
@ request_line_HTTP_slash_1_dot
Definition: ws_engine.hpp:36
zmq::response_line_sw
@ response_line_sw
Definition: ws_engine.hpp:69
zmq::ws_engine_t::produce_no_msg_after_close
int produce_no_msg_after_close(msg_t *msg_)
Definition: ws_engine.cpp:927
precompiled.hpp
zmq_assert
#define zmq_assert(x)
Definition: err.hpp:102
zmq::request_line_GET_space
@ request_line_GET_space
Definition: ws_engine.hpp:27
random.hpp
zmq::options_t::heartbeat_interval
int heartbeat_interval
Definition: options.hpp:219
null_mechanism.hpp
zmq::header_field_value_trailing_space
@ header_field_value_trailing_space
Definition: ws_engine.hpp:42
errno
int errno
zmq::stream_engine_base_t::next_handshake_command
int next_handshake_command(msg_t *msg_)
Definition: stream_engine_base.cpp:453
zmq::ws_engine_t::_close_msg
msg_t _close_msg
Definition: ws_engine.hpp:151
zmq::response_line_swi
@ response_line_swi
Definition: ws_engine.hpp:70
error
Definition: cJSON.c:88
zmq::request_line_GET
@ request_line_GET
Definition: ws_engine.hpp:26
zmq::response_line_cr
@ response_line_cr
Definition: ws_engine.hpp:87
zmq::ws_engine_t::produce_close_message
int produce_close_message(msg_t *msg_)
Definition: ws_engine.cpp:916
zmq::ws_engine_t::select_protocol
bool select_protocol(const char *protocol)
Definition: ws_engine.cpp:183
zmq::msg_t::move
int move(msg_t &src_)
Definition: msg.cpp:305
zmq::request_line_HT
@ request_line_HT
Definition: ws_engine.hpp:31
zmq::request_line_HTTP_slash_1_dot_1
@ request_line_HTTP_slash_1_dot_1
Definition: ws_engine.hpp:37
zmq::request_line_HTTP
@ request_line_HTTP
Definition: ws_engine.hpp:33
zmq::ws_engine_t::~ws_engine_t
~ws_engine_t()
Definition: ws_engine.cpp:94
zmq::fd_t
int fd_t
Definition: zmq.hpp:287
zmq::msg_t::init_size
int init_size(size_t size_)
Definition: msg.cpp:62
zmq::request_line_HTT
@ request_line_HTT
Definition: ws_engine.hpp:32
zmq::options_t::heartbeat_timeout
int heartbeat_timeout
Definition: options.hpp:221
zmq::stream_engine_base_t::pull_msg_from_session
int pull_msg_from_session(msg_t *msg_)
Definition: stream_engine_base.cpp:657
zmq::response_line_s
@ response_line_s
Definition: ws_engine.hpp:68
ZMQ_PLAIN
#define ZMQ_PLAIN
Definition: zmq.h:363
zmq::ws_engine_t::process_routing_id_msg
int process_routing_id_msg(msg_t *msg_)
Definition: ws_engine.cpp:165
snprintf
int snprintf(char *str, size_t size, const char *format,...)
Definition: port.cc:64
alloc_assert
#define alloc_assert(x)
Definition: err.hpp:146
errno_assert
#define errno_assert(x)
Definition: err.hpp:113
zmq::response_line_HTT
@ response_line_HTT
Definition: ws_engine.hpp:57
zmq::client_header_field_cr
@ client_header_field_cr
Definition: ws_engine.hpp:93
zmq::handshake_complete
@ handshake_complete
Definition: ws_engine.hpp:46
zmq::ws_engine_t::plug_internal
void plug_internal()
Definition: ws_engine.cpp:147
compat.hpp
WS_BUFFER_SIZE
#define WS_BUFFER_SIZE
Definition: ws_engine.hpp:12
zmq::ws_engine_t::_websocket_key
char _websocket_key[MAX_HEADER_VALUE_LENGTH+1]
Definition: ws_engine.hpp:147
zmq::endpoint_uri_pair_t
Definition: endpoint.hpp:17
zmq::ws_engine_t::server_handshake
bool server_handshake()
Definition: ws_engine.cpp:260
zmq::msg_t::flags
unsigned char flags
Definition: msg.hpp:232
zmq::client_header_field_value_trailing_space
@ client_header_field_value_trailing_space
Definition: ws_engine.hpp:91
zmq::response_line_protoc
@ response_line_protoc
Definition: ws_engine.hpp:83
LIBZMQ_UNUSED
#define LIBZMQ_UNUSED(object)
Definition: macros.hpp:6
zmq::msg_t::close
int close()
Definition: msg.cpp:242
SHA_DIGEST_LENGTH
#define SHA_DIGEST_LENGTH
Definition: sha1.h:74
zmq::response_line_switchin
@ response_line_switchin
Definition: ws_engine.hpp:75
zmq::response_line_swit
@ response_line_swit
Definition: ws_engine.hpp:71
zmq::ws_engine_t::produce_ping_message
int produce_ping_message(msg_t *msg_)
Definition: ws_engine.cpp:945
zmq::header_field_colon
@ header_field_colon
Definition: ws_engine.hpp:41
zmq::response_line_switching
@ response_line_switching
Definition: ws_engine.hpp:76
zmq::response_line_HTTP
@ response_line_HTTP
Definition: ws_engine.hpp:58
zmq::i_engine::connection_error
@ connection_error
Definition: i_engine.hpp:20
strcpy_s
static int strcpy_s(char(&dest_)[size], const char *const src_)
Definition: compat.hpp:29
p
const char * p
Definition: gmock-matchers_test.cc:3863
ws_engine.hpp
zmq::response_line_HTTP_slash
@ response_line_HTTP_slash
Definition: ws_engine.hpp:59
SHA1_Update
#define SHA1_Update(x, y, z)
Definition: sha1.h:78
zmq::msg_t::set_metadata
void set_metadata(metadata_t *metadata_)
Definition: msg.cpp:448
zmq::response_line_pr
@ response_line_pr
Definition: ws_engine.hpp:79
size
#define size
Definition: glcorearb.h:2944
zmq::stream_engine_base_t::pull_and_encode
int pull_and_encode(msg_t *msg_)
Definition: stream_engine_base.cpp:607
curve_server.hpp
ws_encoder.hpp
zmq::ws_engine_t::_websocket_accept
char _websocket_accept[MAX_HEADER_VALUE_LENGTH+1]
Definition: ws_engine.hpp:148
MAX_HEADER_NAME_LENGTH
#define MAX_HEADER_NAME_LENGTH
Definition: ws_engine.hpp:13
zmq::stream_engine_base_t::_process_msg
int(stream_engine_base_t::* _process_msg)(msg_t *msg_)
Definition: stream_engine_base.hpp:117
zmq::response_line_HTTP_slash_1_dot_1_space
@ response_line_HTTP_slash_1_dot_1_space
Definition: ws_engine.hpp:63
MAX_HEADER_VALUE_LENGTH
#define MAX_HEADER_VALUE_LENGTH
Definition: ws_engine.hpp:14
zmq::header_field_value
@ header_field_value
Definition: ws_engine.hpp:43
zmq::client_handshake_error
@ client_handshake_error
Definition: ws_engine.hpp:97
ws_decoder.hpp
zmq::response_line_switching_space
@ response_line_switching_space
Definition: ws_engine.hpp:77
zmq::stream_engine_base_t::_next_msg
int(stream_engine_base_t::* _next_msg)(msg_t *msg_)
Definition: stream_engine_base.hpp:116
zmq::i_engine::protocol_error
@ protocol_error
Definition: i_engine.hpp:19
zmq::stream_engine_base_t
Definition: stream_engine_base.hpp:28
zmq::ws_address_t
Definition: ws_address.hpp:15
zmq::response_line_HTTP_slash_1_dot
@ response_line_HTTP_slash_1_dot
Definition: ws_engine.hpp:61
zmq::stream_engine_base_t::_options
const options_t _options
Definition: stream_engine_base.hpp:104
zmq::msg_t::init
int init()
Definition: msg.cpp:50
ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED
#define ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED
Definition: zmq.h:445
plain_server.hpp
zmq::response_line_protocol
@ response_line_protocol
Definition: ws_engine.hpp:85
plain_client.hpp
zmq::response_line_proto
@ response_line_proto
Definition: ws_engine.hpp:82
type
GLenum type
Definition: glcorearb.h:2695
zmq::response_line_HT
@ response_line_HT
Definition: ws_engine.hpp:56
zmq::header_field_cr
@ header_field_cr
Definition: ws_engine.hpp:44
zmq::ws_engine_t::_heartbeat_timeout
int _heartbeat_timeout
Definition: ws_engine.hpp:150
zmq::msg_t::routing_id
uint32_t routing_id
Definition: msg.hpp:233
len
int len
Definition: php/ext/google/protobuf/map.c:206
zmq::request_line_HTTP_slash
@ request_line_HTTP_slash
Definition: ws_engine.hpp:34
v
const GLdouble * v
Definition: glcorearb.h:3106
zmq::response_line_status_10
@ response_line_status_10
Definition: ws_engine.hpp:65
zmq::ws_engine_t::produce_pong_message
int produce_pong_message(msg_t *msg_)
Definition: ws_engine.cpp:961
tcp.hpp
zmq::handshake_error
@ handshake_error
Definition: ws_engine.hpp:48
ch
char ch
Definition: gmock-matchers_test.cc:3871
zmq::response_line_switchi
@ response_line_switchi
Definition: ws_engine.hpp:74
zmq::response_line_H
@ response_line_H
Definition: ws_engine.hpp:55
zmq::request_line_cr
@ request_line_cr
Definition: ws_engine.hpp:38
size
GLsizeiptr size
Definition: glcorearb.h:2943
zmq::header_field_name
@ header_field_name
Definition: ws_engine.hpp:40
zmq::response_line_status_1
@ response_line_status_1
Definition: ws_engine.hpp:64
zmq::ws_engine_t::start_ws_handshake
void start_ws_handshake()
Definition: ws_engine.cpp:99
zmq::response_line_switc
@ response_line_switc
Definition: ws_engine.hpp:72
zmq::request_line_G
@ request_line_G
Definition: ws_engine.hpp:24
ECONNRESET
#define ECONNRESET
Definition: zmq.h:143
sha1_ctxt
Definition: sha1.h:48
zmq::generate_random
uint32_t generate_random()
Definition: random.cpp:30
err.hpp
ZMQ_NULL
#define ZMQ_NULL
Definition: zmq.h:362
zmq::ws_engine_t::decode_and_push
int decode_and_push(msg_t *msg_)
Definition: ws_engine.cpp:886
zmq::stream_engine_base_t::push_one_then_decode_and_push
int push_one_then_decode_and_push(msg_t *msg_)
Definition: stream_engine_base.cpp:649
zmq::handshake_initial
@ handshake_initial
Definition: ws_engine.hpp:23
zmq::request_line_GE
@ request_line_GE
Definition: ws_engine.hpp:25
zmq::response_line_status_101_space
@ response_line_status_101_space
Definition: ws_engine.hpp:67
zmq::response_line_pro
@ response_line_pro
Definition: ws_engine.hpp:80
zmq::msg_t::is_ping
bool is_ping() const
Definition: msg.cpp:511
zmq::client_header_field_name
@ client_header_field_name
Definition: ws_engine.hpp:89
zmq::request_line_resource
@ request_line_resource
Definition: ws_engine.hpp:28
zmq::msg_t::is_pong
bool is_pong() const
Definition: msg.cpp:516
true
#define true
Definition: cJSON.c:65
zmq::client_header_field_begin_name
@ client_header_field_begin_name
Definition: ws_engine.hpp:88
zmq::response_line_HTTP_slash_1_dot_1
@ response_line_HTTP_slash_1_dot_1
Definition: ws_engine.hpp:62
zmq::response_line_switch
@ response_line_switch
Definition: ws_engine.hpp:73
zmq::header_field_begin_name
@ header_field_begin_name
Definition: ws_engine.hpp:39
zmq::msg_t::pong
@ pong
Definition: msg.hpp:60
zmq::msg_t::ping
@ ping
Definition: msg.hpp:59
zmq::request_line_resource_space
@ request_line_resource_space
Definition: ws_engine.hpp:29
SHA1_Init
#define SHA1_Init(x)
Definition: sha1.h:77
zmq::stream_engine_base_t::process_handshake_command
int process_handshake_command(msg_t *msg_)
Definition: stream_engine_base.cpp:473
session_base.hpp
zmq::msg_t::data
unsigned char data[max_vsm_size]
Definition: msg.hpp:239
false
#define false
Definition: cJSON.c:70
zmq::ws_engine_t::ws_engine_t
ws_engine_t(fd_t fd_, const options_t &options_, const endpoint_uri_pair_t &endpoint_uri_pair_, const ws_address_t &address_, bool client_)
Definition: ws_engine.cpp:63
SHA1_Final
#define SHA1_Final(x, y)
Definition: sha1.h:79
zmq::msg_t::set_flags
void set_flags(unsigned char flags_)
Definition: msg.cpp:433
zmq::msg_t::is_close_cmd
bool is_close_cmd() const
Definition: msg.cpp:521
zmq::request_line_H
@ request_line_H
Definition: ws_engine.hpp:30
curve_client.hpp
zmq::response_line_protoco
@ response_line_protoco
Definition: ws_engine.hpp:84
zmq::response_line_prot
@ response_line_prot
Definition: ws_engine.hpp:81
zmq::msg_t
Definition: msg.hpp:33
compute_accept_key
static void compute_accept_key(char *key_, unsigned char hash_[SHA_DIGEST_LENGTH])
zmq::client_handshake_complete
@ client_handshake_complete
Definition: ws_engine.hpp:95
options_
DebugStringOptions options_
Definition: src/google/protobuf/descriptor.cc:2410


libaditof
Author(s):
autogenerated on Wed May 21 2025 02:07:02