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 
plain_client_t(session_base_t * const session_,const options_t & options_)42 zmq::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 
~plain_client_t()49 zmq::plain_client_t::~plain_client_t ()
50 {
51 }
52 
next_handshake_command(msg_t * msg_)53 int 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 
process_handshake_command(msg_t * msg_)73 int 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 
status() const106 zmq::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 
produce_hello(msg_t * msg_) const118 void 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 
process_welcome(const unsigned char * cmd_data_,size_t data_size_)145 int 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 
produce_initiate(msg_t * msg_) const167 void 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 
process_ready(const unsigned char * cmd_data_,size_t data_size_)173 int 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 
process_error(const unsigned char * cmd_data_,size_t data_size_)193 int 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