null_mechanism.cpp 6.8 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 41
#include "wire.hpp"
#include "null_mechanism.hpp"

42 43 44
zmq::null_mechanism_t::null_mechanism_t (session_base_t *session_,
                                         const std::string &peer_address_,
                                         const options_t &options_) :
45
    mechanism_base_t (session_, options_),
46
    zap_client_t (session_, peer_address_, options_),
47
    ready_command_sent (false),
48
    error_command_sent (false),
49
    ready_command_received (false),
50
    error_command_received (false),
51 52
    zap_request_sent (false),
    zap_reply_received (false)
53 54 55 56 57 58 59
{
}

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

60
int zmq::null_mechanism_t::next_handshake_command (msg_t *msg_)
61
{
62
    if (ready_command_sent || error_command_sent) {
63 64 65
        errno = EAGAIN;
        return -1;
    }
66

67
    if (zap_required () && !zap_reply_received) {
68 69 70 71
        if (zap_request_sent) {
            errno = EAGAIN;
            return -1;
        }
72 73 74 75
        int rc = session->zap_connect ();
        if (rc == -1) {
            session->get_socket ()->event_handshake_failed_no_detail (
              session->get_endpoint (), EFAULT);
76 77
            return -1;
        }
78
        send_zap_request ();
79
        zap_request_sent = true;
80

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

89 90
        zap_reply_received = true;
    }
91

92
    if (zap_reply_received && status_code != "200") {
93
        error_command_sent = true;
94 95 96 97 98 99 100 101 102 103 104 105 106 107
        if (status_code != "300") {
            const size_t status_code_len = 3;
            const int rc = msg_->init_size (6 + 1 + status_code_len);
            zmq_assert (rc == 0);
            unsigned char *msg_data =
              static_cast<unsigned char *> (msg_->data ());
            memcpy (msg_data, "\5ERROR", 6);
            msg_data[6] = status_code_len;
            memcpy (msg_data + 7, status_code.c_str (), status_code_len);
            return 0;
        } else {
            errno = EAGAIN;
            return -1;
        }
108 109
    }

110
    make_command_with_basic_properties (msg_, "\5READY", 6);
111 112 113 114 115 116

    ready_command_sent = true;

    return 0;
}

117
int zmq::null_mechanism_t::process_handshake_command (msg_t *msg_)
118
{
119
    if (ready_command_received || error_command_received) {
120
        session->get_socket ()->event_handshake_failed_protocol (
121
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
122 123 124 125
        errno = EPROTO;
        return -1;
    }

126
    const unsigned char *cmd_data =
127
      static_cast<unsigned char *> (msg_->data ());
128
    const size_t data_size = msg_->size ();
129

130 131 132
    int rc = 0;
    if (data_size >= 6 && !memcmp (cmd_data, "\5READY", 6))
        rc = process_ready_command (cmd_data, data_size);
133
    else if (data_size >= 6 && !memcmp (cmd_data, "\5ERROR", 6))
134 135
        rc = process_error_command (cmd_data, data_size);
    else {
136
        session->get_socket ()->event_handshake_failed_protocol (
137
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
138
        errno = EPROTO;
139
        rc = -1;
140 141
    }

142
    if (rc == 0) {
143
        rc = msg_->close ();
144 145 146
        errno_assert (rc == 0);
        rc = msg_->init ();
        errno_assert (rc == 0);
147
    }
148 149
    return rc;
}
150

151 152
int zmq::null_mechanism_t::process_ready_command (const unsigned char *cmd_data,
                                                  size_t data_size)
153
{
154
    ready_command_received = true;
155 156
    return parse_metadata (cmd_data + 6, data_size - 6);
}
157

158 159
int zmq::null_mechanism_t::process_error_command (const unsigned char *cmd_data,
                                                  size_t data_size)
160
{
161
    if (data_size < 7) {
162 163 164 165
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);

166 167 168
        errno = EPROTO;
        return -1;
    }
169
    const size_t error_reason_len = static_cast<size_t> (cmd_data[6]);
170
    if (error_reason_len > data_size - 7) {
171 172 173 174
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);

175 176 177
        errno = EPROTO;
        return -1;
    }
178 179
    const char *error_reason = reinterpret_cast<const char *> (cmd_data) + 7;
    handle_error_reason (error_reason, error_reason_len);
180
    error_command_received = true;
181
    return 0;
182 183
}

184 185 186 187 188 189 190 191 192
int zmq::null_mechanism_t::zap_msg_available ()
{
    if (zap_reply_received) {
        errno = EFSM;
        return -1;
    }
    const int rc = receive_and_process_zap_reply ();
    if (rc == 0)
        zap_reply_received = true;
193
    return rc == -1 ? -1 : 0;
194 195
}

196
zmq::mechanism_t::status_t zmq::null_mechanism_t::status () const
197
{
198
    const bool command_sent = ready_command_sent || error_command_sent;
199
    const bool command_received =
200
      ready_command_received || error_command_received;
201 202

    if (ready_command_sent && ready_command_received)
203
        return mechanism_t::ready;
204
    else if (command_sent && command_received)
205 206 207
        return error;
    else
        return handshaking;
208
}
209

210
void zmq::null_mechanism_t::send_zap_request ()
211
{
212
    zap_client_t::send_zap_request ("NULL", 4, NULL, NULL, 0);
213
}