1/*
2 Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
3
4 This file is part of libzmq, the ZeroMQ core engine in C++.
5
6 libzmq is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License (LGPL) as published
8 by the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 As a special exception, the Contributors give you permission to link
12 this library with independent modules to produce an executable,
13 regardless of the license terms of these independent modules, and to
14 copy and distribute the resulting executable under terms of your choice,
15 provided that you also meet, for each linked independent module, the
16 terms and conditions of the license of that module. An independent
17 module is a module which is not derived from or based on this library.
18 If you modify this library, you must extend this exception to your
19 version of the library.
20
21 libzmq is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24 License for more details.
25
26 You should have received a copy of the GNU Lesser General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
28*/
29
30#include "precompiled.hpp"
31
32#include <stddef.h>
33#include <string.h>
34#include <stdlib.h>
35
36#include "err.hpp"
37#include "msg.hpp"
38#include "session_base.hpp"
39#include "null_mechanism.hpp"
40
41const char error_command_name[] = "\5ERROR";
42const size_t error_command_name_len = sizeof (error_command_name) - 1;
43const size_t error_reason_len_size = 1;
44
45const char ready_command_name[] = "\5READY";
46const size_t ready_command_name_len = sizeof (ready_command_name) - 1;
47
48zmq::null_mechanism_t::null_mechanism_t (session_base_t *session_,
49 const std::string &peer_address_,
50 const options_t &options_) :
51 mechanism_base_t (session_, options_),
52 zap_client_t (session_, peer_address_, options_),
53 _ready_command_sent (false),
54 _error_command_sent (false),
55 _ready_command_received (false),
56 _error_command_received (false),
57 _zap_request_sent (false),
58 _zap_reply_received (false)
59{
60}
61
62zmq::null_mechanism_t::~null_mechanism_t ()
63{
64}
65
66int zmq::null_mechanism_t::next_handshake_command (msg_t *msg_)
67{
68 if (_ready_command_sent || _error_command_sent) {
69 errno = EAGAIN;
70 return -1;
71 }
72
73 if (zap_required () && !_zap_reply_received) {
74 if (_zap_request_sent) {
75 errno = EAGAIN;
76 return -1;
77 }
78 // Given this is a backward-incompatible change, it's behind a socket
79 // option disabled by default.
80 int rc = session->zap_connect ();
81 if (rc == -1 && options.zap_enforce_domain) {
82 session->get_socket ()->event_handshake_failed_no_detail (
83 session->get_endpoint (), EFAULT);
84 return -1;
85 }
86 if (rc == 0) {
87 send_zap_request ();
88 _zap_request_sent = true;
89
90 // TODO actually, it is quite unlikely that we can read the ZAP
91 // reply already, but removing this has some strange side-effect
92 // (probably because the pipe's in_active flag is true until a read
93 // is attempted)
94 rc = receive_and_process_zap_reply ();
95 if (rc != 0)
96 return -1;
97
98 _zap_reply_received = true;
99 }
100 }
101
102 if (_zap_reply_received && status_code != "200") {
103 _error_command_sent = true;
104 if (status_code != "300") {
105 const size_t status_code_len = 3;
106 const int rc = msg_->init_size (
107 error_command_name_len + error_reason_len_size + status_code_len);
108 zmq_assert (rc == 0);
109 unsigned char *msg_data =
110 static_cast<unsigned char *> (msg_->data ());
111 memcpy (msg_data, error_command_name, error_command_name_len);
112 msg_data += error_command_name_len;
113 *msg_data = status_code_len;
114 msg_data += error_reason_len_size;
115 memcpy (msg_data, status_code.c_str (), status_code_len);
116 return 0;
117 }
118 errno = EAGAIN;
119 return -1;
120 }
121
122 make_command_with_basic_properties (msg_, ready_command_name,
123 ready_command_name_len);
124
125 _ready_command_sent = true;
126
127 return 0;
128}
129
130int zmq::null_mechanism_t::process_handshake_command (msg_t *msg_)
131{
132 if (_ready_command_received || _error_command_received) {
133 session->get_socket ()->event_handshake_failed_protocol (
134 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
135 errno = EPROTO;
136 return -1;
137 }
138
139 const unsigned char *cmd_data =
140 static_cast<unsigned char *> (msg_->data ());
141 const size_t data_size = msg_->size ();
142
143 int rc = 0;
144 if (data_size >= ready_command_name_len
145 && !memcmp (cmd_data, ready_command_name, ready_command_name_len))
146 rc = process_ready_command (cmd_data, data_size);
147 else if (data_size >= error_command_name_len
148 && !memcmp (cmd_data, error_command_name, error_command_name_len))
149 rc = process_error_command (cmd_data, data_size);
150 else {
151 session->get_socket ()->event_handshake_failed_protocol (
152 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
153 errno = EPROTO;
154 rc = -1;
155 }
156
157 if (rc == 0) {
158 rc = msg_->close ();
159 errno_assert (rc == 0);
160 rc = msg_->init ();
161 errno_assert (rc == 0);
162 }
163 return rc;
164}
165
166int zmq::null_mechanism_t::process_ready_command (
167 const unsigned char *cmd_data_, size_t data_size_)
168{
169 _ready_command_received = true;
170 return parse_metadata (cmd_data_ + ready_command_name_len,
171 data_size_ - ready_command_name_len);
172}
173
174int zmq::null_mechanism_t::process_error_command (
175 const unsigned char *cmd_data_, size_t data_size_)
176{
177 const size_t fixed_prefix_size =
178 error_command_name_len + error_reason_len_size;
179 if (data_size_ < fixed_prefix_size) {
180 session->get_socket ()->event_handshake_failed_protocol (
181 session->get_endpoint (),
182 ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);
183
184 errno = EPROTO;
185 return -1;
186 }
187 const size_t error_reason_len =
188 static_cast<size_t> (cmd_data_[error_command_name_len]);
189 if (error_reason_len > data_size_ - fixed_prefix_size) {
190 session->get_socket ()->event_handshake_failed_protocol (
191 session->get_endpoint (),
192 ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);
193
194 errno = EPROTO;
195 return -1;
196 }
197 const char *error_reason =
198 reinterpret_cast<const char *> (cmd_data_) + fixed_prefix_size;
199 handle_error_reason (error_reason, error_reason_len);
200 _error_command_received = true;
201 return 0;
202}
203
204int zmq::null_mechanism_t::zap_msg_available ()
205{
206 if (_zap_reply_received) {
207 errno = EFSM;
208 return -1;
209 }
210 const int rc = receive_and_process_zap_reply ();
211 if (rc == 0)
212 _zap_reply_received = true;
213 return rc == -1 ? -1 : 0;
214}
215
216zmq::mechanism_t::status_t zmq::null_mechanism_t::status () const
217{
218 if (_ready_command_sent && _ready_command_received)
219 return ready;
220
221 const bool command_sent = _ready_command_sent || _error_command_sent;
222 const bool command_received =
223 _ready_command_received || _error_command_received;
224 return command_sent && command_received ? error : handshaking;
225}
226
227void zmq::null_mechanism_t::send_zap_request ()
228{
229 zap_client_t::send_zap_request ("NULL", 4, NULL, NULL, 0);
230}
231