1 /*
2     Copyright (c) 2007-2017 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 "testutil.hpp"
31 #include "testutil_unity.hpp"
32 
33 #include <string.h>
34 
35 SETUP_TEARDOWN_TESTCONTEXT
36 
test_sockopt_router_notify()37 void test_sockopt_router_notify ()
38 {
39     void *router = test_context_socket (ZMQ_ROUTER);
40     int opt_notify;
41 
42     int opt_notify_read;
43     size_t opt_notify_read_size = sizeof (opt_notify_read);
44 
45 
46     // default value is off when socket is constructed
47     TEST_ASSERT_SUCCESS_ERRNO (zmq_getsockopt (
48       router, ZMQ_ROUTER_NOTIFY, &opt_notify_read, &opt_notify_read_size));
49 
50     TEST_ASSERT_EQUAL (0, opt_notify_read);
51 
52 
53     // valid value - Connect
54     opt_notify = ZMQ_NOTIFY_CONNECT;
55     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
56       router, ZMQ_ROUTER_NOTIFY, &opt_notify, sizeof (opt_notify)));
57 
58     TEST_ASSERT_SUCCESS_ERRNO (zmq_getsockopt (
59       router, ZMQ_ROUTER_NOTIFY, &opt_notify_read, &opt_notify_read_size));
60 
61     TEST_ASSERT_EQUAL (opt_notify, opt_notify_read);
62 
63 
64     // valid value - Disconnect
65     opt_notify = ZMQ_NOTIFY_DISCONNECT;
66     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
67       router, ZMQ_ROUTER_NOTIFY, &opt_notify, sizeof (opt_notify)));
68 
69     TEST_ASSERT_SUCCESS_ERRNO (zmq_getsockopt (
70       router, ZMQ_ROUTER_NOTIFY, &opt_notify_read, &opt_notify_read_size));
71 
72     TEST_ASSERT_EQUAL (opt_notify, opt_notify_read);
73 
74 
75     // valid value - Off
76     opt_notify = 0;
77     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
78       router, ZMQ_ROUTER_NOTIFY, &opt_notify, sizeof (opt_notify)));
79 
80     TEST_ASSERT_SUCCESS_ERRNO (zmq_getsockopt (
81       router, ZMQ_ROUTER_NOTIFY, &opt_notify_read, &opt_notify_read_size));
82 
83     TEST_ASSERT_EQUAL (opt_notify, opt_notify_read);
84 
85 
86     // valid value - Both
87     opt_notify = ZMQ_NOTIFY_CONNECT | ZMQ_NOTIFY_DISCONNECT;
88     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
89       router, ZMQ_ROUTER_NOTIFY, &opt_notify, sizeof (opt_notify)));
90 
91     TEST_ASSERT_SUCCESS_ERRNO (zmq_getsockopt (
92       router, ZMQ_ROUTER_NOTIFY, &opt_notify_read, &opt_notify_read_size));
93 
94     TEST_ASSERT_EQUAL (opt_notify, opt_notify_read);
95 
96 
97     // value boundary
98     opt_notify = -1;
99     TEST_ASSERT_FAILURE_ERRNO (
100       EINVAL, zmq_setsockopt (router, ZMQ_ROUTER_NOTIFY, &opt_notify,
101                               sizeof (opt_notify)));
102 
103     opt_notify = (ZMQ_NOTIFY_CONNECT | ZMQ_NOTIFY_DISCONNECT) + 1;
104     TEST_ASSERT_FAILURE_ERRNO (
105       EINVAL, zmq_setsockopt (router, ZMQ_ROUTER_NOTIFY, &opt_notify,
106                               sizeof (opt_notify)));
107 
108     // failures don't update the value
109     TEST_ASSERT_SUCCESS_ERRNO (zmq_getsockopt (
110       router, ZMQ_ROUTER_NOTIFY, &opt_notify_read, &opt_notify_read_size));
111 
112     TEST_ASSERT_EQUAL (ZMQ_NOTIFY_CONNECT | ZMQ_NOTIFY_DISCONNECT,
113                        opt_notify_read);
114 
115 
116     test_context_socket_close (router);
117 
118 
119     // check a non-router socket type
120     void *dealer = test_context_socket (ZMQ_DEALER);
121 
122     // setsockopt fails for non-router sockets
123     opt_notify = ZMQ_NOTIFY_CONNECT;
124     TEST_ASSERT_FAILURE_ERRNO (
125       EINVAL, zmq_setsockopt (dealer, ZMQ_ROUTER_NOTIFY, &opt_notify,
126                               sizeof (opt_notify)));
127 
128     // getsockopts returns off for any non-router socket
129     TEST_ASSERT_SUCCESS_ERRNO (zmq_getsockopt (
130       dealer, ZMQ_ROUTER_NOTIFY, &opt_notify_read, &opt_notify_read_size));
131 
132     TEST_ASSERT_EQUAL (0, opt_notify_read);
133 
134 
135     test_context_socket_close (dealer);
136 }
137 
138 
test_router_notify_helper(int opt_notify_)139 void test_router_notify_helper (int opt_notify_)
140 {
141     void *router = test_context_socket (ZMQ_ROUTER);
142     int opt_more;
143     size_t opt_more_length = sizeof (opt_more);
144     int opt_events;
145     size_t opt_events_length = sizeof (opt_events);
146     char connect_address[MAX_SOCKET_STRING];
147 
148 
149     // valid values
150     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
151       router, ZMQ_ROUTER_NOTIFY, &opt_notify_, sizeof (opt_notify_)));
152 
153     bind_loopback_ipv4 (router, connect_address, sizeof connect_address);
154 
155     void *dealer = test_context_socket (ZMQ_DEALER);
156     const char *dealer_routing_id = "X";
157 
158     TEST_ASSERT_SUCCESS_ERRNO (
159       zmq_setsockopt (dealer, ZMQ_ROUTING_ID, dealer_routing_id, 1));
160 
161     // dealer connects
162     TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (dealer, connect_address));
163 
164     // connection notification msg
165     if (opt_notify_ & ZMQ_NOTIFY_CONNECT) {
166         // routing-id only message of the connect
167         recv_string_expect_success (router, dealer_routing_id,
168                                     0);             // 1st part: routing-id
169         recv_string_expect_success (router, "", 0); // 2nd part: empty
170         TEST_ASSERT_SUCCESS_ERRNO (
171           zmq_getsockopt (router, ZMQ_RCVMORE, &opt_more, &opt_more_length));
172         TEST_ASSERT_EQUAL (0, opt_more);
173     }
174 
175     // test message from the dealer
176     send_string_expect_success (dealer, "Hello", 0);
177     recv_string_expect_success (router, dealer_routing_id, 0);
178     recv_string_expect_success (router, "Hello", 0);
179     TEST_ASSERT_SUCCESS_ERRNO (
180       zmq_getsockopt (router, ZMQ_RCVMORE, &opt_more, &opt_more_length));
181     TEST_ASSERT_EQUAL (0, opt_more);
182 
183     // dealer disconnects
184     TEST_ASSERT_SUCCESS_ERRNO (zmq_disconnect (dealer, connect_address));
185 
186     // need one more process_commands() (???)
187     msleep (SETTLE_TIME);
188     zmq_getsockopt (dealer, ZMQ_EVENTS, &opt_events, &opt_events_length);
189 
190     // connection notification msg
191     if (opt_notify_ & ZMQ_NOTIFY_DISCONNECT) {
192         // routing-id only message of the connect
193         recv_string_expect_success (router, dealer_routing_id,
194                                     0);             // 1st part: routing-id
195         recv_string_expect_success (router, "", 0); // 2nd part: empty
196         TEST_ASSERT_SUCCESS_ERRNO (
197           zmq_getsockopt (router, ZMQ_RCVMORE, &opt_more, &opt_more_length));
198         TEST_ASSERT_EQUAL (0, opt_more);
199     }
200 
201     test_context_socket_close (dealer);
202     test_context_socket_close (router);
203 }
204 
205 
test_router_notify_connect()206 void test_router_notify_connect ()
207 {
208     test_router_notify_helper (ZMQ_NOTIFY_CONNECT);
209 }
210 
211 
test_router_notify_disconnect()212 void test_router_notify_disconnect ()
213 {
214     test_router_notify_helper (ZMQ_NOTIFY_DISCONNECT);
215 }
216 
217 
test_router_notify_both()218 void test_router_notify_both ()
219 {
220     test_router_notify_helper (ZMQ_NOTIFY_CONNECT | ZMQ_NOTIFY_DISCONNECT);
221 }
222 
223 
test_handshake_fail()224 void test_handshake_fail ()
225 {
226     // setup router socket
227     void *router = test_context_socket (ZMQ_ROUTER);
228     int opt_timeout = 200;
229     int opt_notify = ZMQ_NOTIFY_CONNECT | ZMQ_NOTIFY_DISCONNECT;
230     char connect_address[MAX_SOCKET_STRING];
231 
232     // valid values
233     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
234       router, ZMQ_ROUTER_NOTIFY, &opt_notify, sizeof (opt_notify)));
235 
236     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
237       router, ZMQ_RCVTIMEO, &opt_timeout, sizeof (opt_timeout)));
238 
239     bind_loopback_ipv4 (router, connect_address, sizeof connect_address);
240 
241     // send something on raw tcp
242     void *stream = test_context_socket (ZMQ_STREAM);
243     TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (stream, connect_address));
244 
245     send_string_expect_success (stream, "not-a-handshake", 0);
246 
247     TEST_ASSERT_SUCCESS_ERRNO (zmq_disconnect (stream, connect_address));
248     test_context_socket_close (stream);
249 
250     // no notification delivered
251     char buffer[255];
252     TEST_ASSERT_FAILURE_ERRNO (EAGAIN,
253                                zmq_recv (router, buffer, sizeof (buffer), 0));
254 
255     test_context_socket_close (router);
256 }
257 
258 
test_error_during_multipart()259 void test_error_during_multipart ()
260 {
261     /*
262      * If the disconnect occurs in the middle of the multipart
263      * message, the socket should not add the notification at the
264      * end of the incomplete message. It must discard the incomplete
265      * message, and delivert the notification as a new message.
266      */
267 
268     char connect_address[MAX_SOCKET_STRING];
269     char long_str[128] = {0};
270     memset (long_str, '*', sizeof (long_str) - 1);
271 
272     // setup router
273     void *router = test_context_socket (ZMQ_ROUTER);
274 
275     int opt_notify = ZMQ_NOTIFY_DISCONNECT;
276     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
277       router, ZMQ_ROUTER_NOTIFY, &opt_notify, sizeof (opt_notify)));
278 
279     int64_t opt_maxmsgsize = 64; // the handshake fails if this is too small
280     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
281       router, ZMQ_MAXMSGSIZE, &opt_maxmsgsize, sizeof (opt_maxmsgsize)));
282 
283     bind_loopback_ipv4 (router, connect_address, sizeof connect_address);
284 
285     // setup dealer
286     void *dealer = test_context_socket (ZMQ_DEALER);
287     const char *dealer_routing_id = "X";
288 
289     TEST_ASSERT_SUCCESS_ERRNO (
290       zmq_setsockopt (dealer, ZMQ_ROUTING_ID, dealer_routing_id, 1));
291 
292     TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (dealer, connect_address));
293 
294 
295     // send multipart message, the 2nd part causes a disconnect.
296     send_string_expect_success (dealer, "Hello2", ZMQ_SNDMORE);
297     send_string_expect_success (dealer, long_str, 0);
298 
299     // disconnect notification
300     recv_string_expect_success (router, dealer_routing_id, 0);
301     recv_string_expect_success (router, "", 0);
302 
303 
304     test_context_socket_close (dealer);
305     test_context_socket_close (router);
306 }
307 
308 
main(void)309 int main (void)
310 {
311     setup_test_environment ();
312 
313     UNITY_BEGIN ();
314     RUN_TEST (test_sockopt_router_notify);
315     RUN_TEST (test_router_notify_connect);
316     RUN_TEST (test_router_notify_disconnect);
317     RUN_TEST (test_router_notify_both);
318     RUN_TEST (test_handshake_fail);
319     RUN_TEST (test_error_during_multipart);
320 
321     return UNITY_END ();
322 }
323