null_mechanism.cpp 7.73 KB
Newer Older
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 7 8
    libzmq is free software; you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License (LGPL) as published
    by the Free Software Foundation; either version 3 of the License, or
9 10
    (at your option) any later version.

11 12 13 14 15 16 17 18 19 20 21 22 23 24
    As a special exception, the Contributors give you permission to link
    this library with independent modules to produce an executable,
    regardless of the license terms of these independent modules, and to
    copy and distribute the resulting executable under terms of your choice,
    provided that you also meet, for each linked independent module, the
    terms and conditions of the license of that module. An independent
    module is a module which is not derived from or based on this library.
    If you modify this library, you must extend this exception to your
    version of the library.

    libzmq is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    License for more details.
25 26 27 28 29

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

30
#include "precompiled.hpp"
31 32 33 34 35 36 37

#include <stddef.h>
#include <string.h>
#include <stdlib.h>

#include "err.hpp"
#include "msg.hpp"
38
#include "session_base.hpp"
39 40
#include "null_mechanism.hpp"

41 42 43 44 45 46 47
const char error_command_name[] = "\5ERROR";
const size_t error_command_name_len = sizeof (error_command_name) - 1;
const size_t error_reason_len_size = 1;

const char ready_command_name[] = "\5READY";
const size_t ready_command_name_len = sizeof (ready_command_name) - 1;

48 49 50
zmq::null_mechanism_t::null_mechanism_t (session_base_t *session_,
                                         const std::string &peer_address_,
                                         const options_t &options_) :
51
    mechanism_base_t (session_, options_),
52
    zap_client_t (session_, peer_address_, options_),
53 54 55 56 57 58
    _ready_command_sent (false),
    _error_command_sent (false),
    _ready_command_received (false),
    _error_command_received (false),
    _zap_request_sent (false),
    _zap_reply_received (false)
59 60 61 62 63 64 65
{
}

zmq::null_mechanism_t::~null_mechanism_t ()
{
}

66
int zmq::null_mechanism_t::next_handshake_command (msg_t *msg_)
67
{
68
    if (_ready_command_sent || _error_command_sent) {
69 70 71
        errno = EAGAIN;
        return -1;
    }
72

73 74
    if (zap_required () && !_zap_reply_received) {
        if (_zap_request_sent) {
75 76 77
            errno = EAGAIN;
            return -1;
        }
78 79
        //  Given this is a backward-incompatible change, it's behind a socket
        //  option disabled by default.
80
        int rc = session->zap_connect ();
81
        if (rc == -1 && options.zap_enforce_domain) {
82 83
            session->get_socket ()->event_handshake_failed_no_detail (
              session->get_endpoint (), EFAULT);
84
            return -1;
85 86
        }
        if (rc == 0) {
87
            send_zap_request ();
88
            _zap_request_sent = true;
89 90 91 92 93 94 95 96 97

            //  TODO actually, it is quite unlikely that we can read the ZAP
            //  reply already, but removing this has some strange side-effect
            //  (probably because the pipe's in_active flag is true until a read
            //  is attempted)
            rc = receive_and_process_zap_reply ();
            if (rc != 0)
                return -1;

98
            _zap_reply_received = true;
99
        }
100
    }
101

102 103
    if (_zap_reply_received && status_code != "200") {
        _error_command_sent = true;
104 105
        if (status_code != "300") {
            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 109 110
            zmq_assert (rc == 0);
            unsigned char *msg_data =
              static_cast<unsigned char *> (msg_->data ());
111 112
            memcpy (msg_data, error_command_name, error_command_name_len);
            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 117
            return 0;
        }
118 119
        errno = EAGAIN;
        return -1;
120 121
    }

122 123
    make_command_with_basic_properties (msg_, ready_command_name,
                                        ready_command_name_len);
124

125
    _ready_command_sent = true;
126 127 128 129

    return 0;
}

130
int 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 136 137 138
        errno = EPROTO;
        return -1;
    }

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 145
    if (data_size >= ready_command_name_len
        && !memcmp (cmd_data, ready_command_name, ready_command_name_len))
146
        rc = process_ready_command (cmd_data, data_size);
147 148
    else if (data_size >= error_command_name_len
             && !memcmp (cmd_data, error_command_name, error_command_name_len))
149 150
        rc = process_error_command (cmd_data, data_size);
    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 160 161
        errno_assert (rc == 0);
        rc = msg_->init ();
        errno_assert (rc == 0);
162
    }
163 164
    return rc;
}
165

166 167
int zmq::null_mechanism_t::process_ready_command (
  const unsigned char *cmd_data_, size_t data_size_)
168
{
169
    _ready_command_received = true;
170 171
    return parse_metadata (cmd_data_ + ready_command_name_len,
                           data_size_ - ready_command_name_len);
172
}
173

174 175
int zmq::null_mechanism_t::process_error_command (
  const unsigned char *cmd_data_, size_t data_size_)
176
{
177 178 179
    const size_t fixed_prefix_size =
      error_command_name_len + error_reason_len_size;
    if (data_size_ < fixed_prefix_size) {
180 181 182 183
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);

184 185 186
        errno = EPROTO;
        return -1;
    }
187 188 189
    const size_t error_reason_len =
      static_cast<size_t> (cmd_data_[error_command_name_len]);
    if (error_reason_len > data_size_ - fixed_prefix_size) {
190 191 192 193
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);

194 195 196
        errno = EPROTO;
        return -1;
    }
197 198
    const char *error_reason =
      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
}

204 205
int zmq::null_mechanism_t::zap_msg_available ()
{
206
    if (_zap_reply_received) {
207 208 209 210 211
        errno = EFSM;
        return -1;
    }
    const int rc = receive_and_process_zap_reply ();
    if (rc == 0)
212
        _zap_reply_received = true;
213
    return rc == -1 ? -1 : 0;
214 215
}

216
zmq::mechanism_t::status_t zmq::null_mechanism_t::status () const
217
{
218 219 220
    if (_ready_command_sent && _ready_command_received)
        return ready;

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

227
void zmq::null_mechanism_t::send_zap_request ()
228
{
229
    zap_client_t::send_zap_request ("NULL", 4, NULL, NULL, 0);
230
}