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#include "macros.hpp"
32
33#include <string>
34#include <limits.h>
35
36#include "msg.hpp"
37#include "err.hpp"
38#include "plain_client.hpp"
39#include "session_base.hpp"
40#include "plain_common.hpp"
41
42zmq::plain_client_t::plain_client_t (session_base_t *const session_,
43 const options_t &options_) :
44 mechanism_base_t (session_, options_),
45 _state (sending_hello)
46{
47}
48
49zmq::plain_client_t::~plain_client_t ()
50{
51}
52
53int zmq::plain_client_t::next_handshake_command (msg_t *msg_)
54{
55 int rc = 0;
56
57 switch (_state) {
58 case sending_hello:
59 produce_hello (msg_);
60 _state = waiting_for_welcome;
61 break;
62 case sending_initiate:
63 produce_initiate (msg_);
64 _state = waiting_for_ready;
65 break;
66 default:
67 errno = EAGAIN;
68 rc = -1;
69 }
70 return rc;
71}
72
73int zmq::plain_client_t::process_handshake_command (msg_t *msg_)
74{
75 const unsigned char *cmd_data =
76 static_cast<unsigned char *> (msg_->data ());
77 const size_t data_size = msg_->size ();
78
79 int rc = 0;
80 if (data_size >= welcome_prefix_len
81 && !memcmp (cmd_data, welcome_prefix, welcome_prefix_len))
82 rc = process_welcome (cmd_data, data_size);
83 else if (data_size >= ready_prefix_len
84 && !memcmp (cmd_data, ready_prefix, ready_prefix_len))
85 rc = process_ready (cmd_data, data_size);
86 else if (data_size >= error_prefix_len
87 && !memcmp (cmd_data, error_prefix, error_prefix_len))
88 rc = process_error (cmd_data, data_size);
89 else {
90 session->get_socket ()->event_handshake_failed_protocol (
91 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
92 errno = EPROTO;
93 rc = -1;
94 }
95
96 if (rc == 0) {
97 rc = msg_->close ();
98 errno_assert (rc == 0);
99 rc = msg_->init ();
100 errno_assert (rc == 0);
101 }
102
103 return rc;
104}
105
106zmq::mechanism_t::status_t zmq::plain_client_t::status () const
107{
108 switch (_state) {
109 case ready:
110 return mechanism_t::ready;
111 case error_command_received:
112 return mechanism_t::error;
113 default:
114 return mechanism_t::handshaking;
115 }
116}
117
118void zmq::plain_client_t::produce_hello (msg_t *msg_) const
119{
120 const std::string username = options.plain_username;
121 zmq_assert (username.length () <= UCHAR_MAX);
122
123 const std::string password = options.plain_password;
124 zmq_assert (password.length () <= UCHAR_MAX);
125
126 const size_t command_size = hello_prefix_len + brief_len_size
127 + username.length () + brief_len_size
128 + password.length ();
129
130 const int rc = msg_->init_size (command_size);
131 errno_assert (rc == 0);
132
133 unsigned char *ptr = static_cast<unsigned char *> (msg_->data ());
134 memcpy (ptr, hello_prefix, hello_prefix_len);
135 ptr += hello_prefix_len;
136
137 *ptr++ = static_cast<unsigned char> (username.length ());
138 memcpy (ptr, username.c_str (), username.length ());
139 ptr += username.length ();
140
141 *ptr++ = static_cast<unsigned char> (password.length ());
142 memcpy (ptr, password.c_str (), password.length ());
143}
144
145int zmq::plain_client_t::process_welcome (const unsigned char *cmd_data_,
146 size_t data_size_)
147{
148 LIBZMQ_UNUSED (cmd_data_);
149
150 if (_state != waiting_for_welcome) {
151 session->get_socket ()->event_handshake_failed_protocol (
152 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
153 errno = EPROTO;
154 return -1;
155 }
156 if (data_size_ != welcome_prefix_len) {
157 session->get_socket ()->event_handshake_failed_protocol (
158 session->get_endpoint (),
159 ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME);
160 errno = EPROTO;
161 return -1;
162 }
163 _state = sending_initiate;
164 return 0;
165}
166
167void zmq::plain_client_t::produce_initiate (msg_t *msg_) const
168{
169 make_command_with_basic_properties (msg_, initiate_prefix,
170 initiate_prefix_len);
171}
172
173int zmq::plain_client_t::process_ready (const unsigned char *cmd_data_,
174 size_t data_size_)
175{
176 if (_state != waiting_for_ready) {
177 session->get_socket ()->event_handshake_failed_protocol (
178 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
179 errno = EPROTO;
180 return -1;
181 }
182 const int rc = parse_metadata (cmd_data_ + ready_prefix_len,
183 data_size_ - ready_prefix_len);
184 if (rc == 0)
185 _state = ready;
186 else
187 session->get_socket ()->event_handshake_failed_protocol (
188 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA);
189
190 return rc;
191}
192
193int zmq::plain_client_t::process_error (const unsigned char *cmd_data_,
194 size_t data_size_)
195{
196 if (_state != waiting_for_welcome && _state != waiting_for_ready) {
197 session->get_socket ()->event_handshake_failed_protocol (
198 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
199 errno = EPROTO;
200 return -1;
201 }
202 const size_t start_of_error_reason = error_prefix_len + brief_len_size;
203 if (data_size_ < start_of_error_reason) {
204 session->get_socket ()->event_handshake_failed_protocol (
205 session->get_endpoint (),
206 ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);
207 errno = EPROTO;
208 return -1;
209 }
210 const size_t error_reason_len =
211 static_cast<size_t> (cmd_data_[error_prefix_len]);
212 if (error_reason_len > data_size_ - start_of_error_reason) {
213 session->get_socket ()->event_handshake_failed_protocol (
214 session->get_endpoint (),
215 ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);
216 errno = EPROTO;
217 return -1;
218 }
219 const char *error_reason =
220 reinterpret_cast<const char *> (cmd_data_) + start_of_error_reason;
221 handle_error_reason (error_reason, error_reason_len);
222 _state = error_command_received;
223 return 0;
224}
225