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 <string.h> |
32 | #include <limits.h> |
33 | #include <set> |
34 | |
35 | #include "options.hpp" |
36 | #include "err.hpp" |
37 | #include "macros.hpp" |
38 | |
39 | #ifndef ZMQ_HAVE_WINDOWS |
40 | #include <net/if.h> |
41 | #endif |
42 | |
43 | #if defined IFNAMSIZ |
44 | #define BINDDEVSIZ IFNAMSIZ |
45 | #else |
46 | #define BINDDEVSIZ 16 |
47 | #endif |
48 | |
49 | static int sockopt_invalid () |
50 | { |
51 | #if defined(ZMQ_ACT_MILITANT) |
52 | zmq_assert (false); |
53 | #endif |
54 | errno = EINVAL; |
55 | return -1; |
56 | } |
57 | |
58 | int zmq::do_getsockopt (void *const optval_, |
59 | size_t *const optvallen_, |
60 | const std::string &value_) |
61 | { |
62 | return do_getsockopt (optval_, optvallen_, value_.c_str (), |
63 | value_.size () + 1); |
64 | } |
65 | |
66 | int zmq::do_getsockopt (void *const optval_, |
67 | size_t *const optvallen_, |
68 | const void *value_, |
69 | const size_t value_len_) |
70 | { |
71 | // TODO behaviour is inconsistent with options_t::getsockopt; there, an |
72 | // *exact* length match is required except for string-like (but not the |
73 | // CURVE keys!) (and therefore null-ing remaining memory is a no-op, see |
74 | // comment below) |
75 | if (*optvallen_ < value_len_) { |
76 | return sockopt_invalid (); |
77 | } |
78 | memcpy (optval_, value_, value_len_); |
79 | // TODO why is the remaining memory null-ed? |
80 | memset (static_cast<char *> (optval_) + value_len_, 0, |
81 | *optvallen_ - value_len_); |
82 | *optvallen_ = value_len_; |
83 | return 0; |
84 | } |
85 | |
86 | #ifdef ZMQ_HAVE_CURVE |
87 | static int do_getsockopt_curve_key (void *const optval_, |
88 | const size_t *const optvallen_, |
89 | const uint8_t (&curve_key_)[CURVE_KEYSIZE]) |
90 | { |
91 | if (*optvallen_ == CURVE_KEYSIZE) { |
92 | memcpy (optval_, curve_key_, CURVE_KEYSIZE); |
93 | return 0; |
94 | } |
95 | if (*optvallen_ == CURVE_KEYSIZE_Z85 + 1) { |
96 | zmq_z85_encode (static_cast<char *> (optval_), curve_key_, |
97 | CURVE_KEYSIZE); |
98 | return 0; |
99 | } |
100 | return sockopt_invalid (); |
101 | } |
102 | #endif |
103 | |
104 | template <typename T> |
105 | int do_setsockopt (const void *const optval_, |
106 | const size_t optvallen_, |
107 | T *const out_value_) |
108 | { |
109 | if (optvallen_ == sizeof (T)) { |
110 | memcpy (out_value_, optval_, sizeof (T)); |
111 | return 0; |
112 | } |
113 | return sockopt_invalid (); |
114 | } |
115 | |
116 | int zmq::do_setsockopt_int_as_bool_strict (const void *const optval_, |
117 | const size_t optvallen_, |
118 | bool *const out_value_) |
119 | { |
120 | // TODO handling of values other than 0 or 1 is not consistent, |
121 | // here it is disallowed, but for other options such as |
122 | // ZMQ_ROUTER_RAW any positive value is accepted |
123 | int value = -1; |
124 | if (do_setsockopt (optval_, optvallen_, &value) == -1) |
125 | return -1; |
126 | if (value == 0 || value == 1) { |
127 | *out_value_ = (value != 0); |
128 | return 0; |
129 | } |
130 | return sockopt_invalid (); |
131 | } |
132 | |
133 | int zmq::do_setsockopt_int_as_bool_relaxed (const void *const optval_, |
134 | const size_t optvallen_, |
135 | bool *const out_value_) |
136 | { |
137 | int value = -1; |
138 | if (do_setsockopt (optval_, optvallen_, &value) == -1) |
139 | return -1; |
140 | *out_value_ = (value != 0); |
141 | return 0; |
142 | } |
143 | |
144 | static int |
145 | do_setsockopt_string_allow_empty_strict (const void *const optval_, |
146 | const size_t optvallen_, |
147 | std::string *const out_value_, |
148 | const size_t max_len_) |
149 | { |
150 | // TODO why is optval_ != NULL not allowed in case of optvallen_== 0? |
151 | // TODO why are empty strings allowed for some socket options, but not for others? |
152 | if (optval_ == NULL && optvallen_ == 0) { |
153 | out_value_->clear (); |
154 | return 0; |
155 | } |
156 | if (optval_ != NULL && optvallen_ > 0 && optvallen_ <= max_len_) { |
157 | out_value_->assign (static_cast<const char *> (optval_), optvallen_); |
158 | return 0; |
159 | } |
160 | return sockopt_invalid (); |
161 | } |
162 | |
163 | static int |
164 | do_setsockopt_string_allow_empty_relaxed (const void *const optval_, |
165 | const size_t optvallen_, |
166 | std::string *const out_value_, |
167 | const size_t max_len_) |
168 | { |
169 | // TODO use either do_setsockopt_string_allow_empty_relaxed or |
170 | // do_setsockopt_string_allow_empty_strict everywhere |
171 | if (optvallen_ > 0 && optvallen_ <= max_len_) { |
172 | out_value_->assign (static_cast<const char *> (optval_), optvallen_); |
173 | return 0; |
174 | } |
175 | return sockopt_invalid (); |
176 | } |
177 | |
178 | template <typename T> |
179 | int do_setsockopt_set (const void *const optval_, |
180 | const size_t optvallen_, |
181 | std::set<T> *const set_) |
182 | { |
183 | if (optvallen_ == 0 && optval_ == NULL) { |
184 | set_->clear (); |
185 | return 0; |
186 | } |
187 | if (optvallen_ == sizeof (T) && optval_ != NULL) { |
188 | set_->insert (*(static_cast<const T *> (optval_))); |
189 | return 0; |
190 | } |
191 | return sockopt_invalid (); |
192 | } |
193 | |
194 | // TODO why is 1000 a sensible default? |
195 | const int default_hwm = 1000; |
196 | |
197 | zmq::options_t::options_t () : |
198 | sndhwm (default_hwm), |
199 | rcvhwm (default_hwm), |
200 | affinity (0), |
201 | routing_id_size (0), |
202 | rate (100), |
203 | recovery_ivl (10000), |
204 | multicast_hops (1), |
205 | multicast_maxtpdu (1500), |
206 | sndbuf (-1), |
207 | rcvbuf (-1), |
208 | tos (0), |
209 | type (-1), |
210 | linger (-1), |
211 | connect_timeout (0), |
212 | tcp_maxrt (0), |
213 | reconnect_ivl (100), |
214 | reconnect_ivl_max (0), |
215 | backlog (100), |
216 | maxmsgsize (-1), |
217 | rcvtimeo (-1), |
218 | sndtimeo (-1), |
219 | ipv6 (false), |
220 | immediate (0), |
221 | filter (false), |
222 | invert_matching (false), |
223 | recv_routing_id (false), |
224 | raw_socket (false), |
225 | raw_notify (true), |
226 | tcp_keepalive (-1), |
227 | tcp_keepalive_cnt (-1), |
228 | tcp_keepalive_idle (-1), |
229 | tcp_keepalive_intvl (-1), |
230 | mechanism (ZMQ_NULL), |
231 | as_server (0), |
232 | gss_principal_nt (ZMQ_GSSAPI_NT_HOSTBASED), |
233 | gss_service_principal_nt (ZMQ_GSSAPI_NT_HOSTBASED), |
234 | gss_plaintext (false), |
235 | socket_id (0), |
236 | conflate (false), |
237 | handshake_ivl (30000), |
238 | connected (false), |
239 | heartbeat_ttl (0), |
240 | heartbeat_interval (0), |
241 | heartbeat_timeout (-1), |
242 | use_fd (-1), |
243 | zap_enforce_domain (false), |
244 | loopback_fastpath (false), |
245 | multicast_loop (true), |
246 | in_batch_size (8192), |
247 | out_batch_size (8192), |
248 | zero_copy (true), |
249 | router_notify (0), |
250 | monitor_event_version (1), |
251 | wss_trust_system (false) |
252 | { |
253 | memset (curve_public_key, 0, CURVE_KEYSIZE); |
254 | memset (curve_secret_key, 0, CURVE_KEYSIZE); |
255 | memset (curve_server_key, 0, CURVE_KEYSIZE); |
256 | #if defined ZMQ_HAVE_VMCI |
257 | vmci_buffer_size = 0; |
258 | vmci_buffer_min_size = 0; |
259 | vmci_buffer_max_size = 0; |
260 | vmci_connect_timeout = -1; |
261 | #endif |
262 | } |
263 | |
264 | int zmq::options_t::set_curve_key (uint8_t *destination_, |
265 | const void *optval_, |
266 | size_t optvallen_) |
267 | { |
268 | switch (optvallen_) { |
269 | case CURVE_KEYSIZE: |
270 | memcpy (destination_, optval_, optvallen_); |
271 | mechanism = ZMQ_CURVE; |
272 | return 0; |
273 | |
274 | case CURVE_KEYSIZE_Z85 + 1: |
275 | if (zmq_z85_decode (destination_, |
276 | reinterpret_cast<const char *> (optval_))) { |
277 | mechanism = ZMQ_CURVE; |
278 | return 0; |
279 | } |
280 | break; |
281 | |
282 | case CURVE_KEYSIZE_Z85: |
283 | char z85_key[CURVE_KEYSIZE_Z85 + 1]; |
284 | memcpy (z85_key, reinterpret_cast<const char *> (optval_), |
285 | optvallen_); |
286 | z85_key[CURVE_KEYSIZE_Z85] = 0; |
287 | if (zmq_z85_decode (destination_, z85_key)) { |
288 | mechanism = ZMQ_CURVE; |
289 | return 0; |
290 | } |
291 | break; |
292 | |
293 | default: |
294 | break; |
295 | } |
296 | return -1; |
297 | } |
298 | |
299 | const int deciseconds_per_millisecond = 100; |
300 | |
301 | int zmq::options_t::setsockopt (int option_, |
302 | const void *optval_, |
303 | size_t optvallen_) |
304 | { |
305 | bool is_int = (optvallen_ == sizeof (int)); |
306 | int value = 0; |
307 | if (is_int) |
308 | memcpy (&value, optval_, sizeof (int)); |
309 | #if defined(ZMQ_ACT_MILITANT) |
310 | bool malformed = true; // Did caller pass a bad option value? |
311 | #endif |
312 | |
313 | switch (option_) { |
314 | case ZMQ_SNDHWM: |
315 | if (is_int && value >= 0) { |
316 | sndhwm = value; |
317 | return 0; |
318 | } |
319 | break; |
320 | |
321 | case ZMQ_RCVHWM: |
322 | if (is_int && value >= 0) { |
323 | rcvhwm = value; |
324 | return 0; |
325 | } |
326 | break; |
327 | |
328 | case ZMQ_AFFINITY: |
329 | return do_setsockopt (optval_, optvallen_, &affinity); |
330 | |
331 | case ZMQ_ROUTING_ID: |
332 | // Routing id is any binary string from 1 to 255 octets |
333 | if (optvallen_ > 0 && optvallen_ <= UCHAR_MAX) { |
334 | routing_id_size = static_cast<unsigned char> (optvallen_); |
335 | memcpy (routing_id, optval_, routing_id_size); |
336 | return 0; |
337 | } |
338 | break; |
339 | |
340 | case ZMQ_RATE: |
341 | if (is_int && value > 0) { |
342 | rate = value; |
343 | return 0; |
344 | } |
345 | break; |
346 | |
347 | case ZMQ_RECOVERY_IVL: |
348 | if (is_int && value >= 0) { |
349 | recovery_ivl = value; |
350 | return 0; |
351 | } |
352 | break; |
353 | |
354 | case ZMQ_SNDBUF: |
355 | if (is_int && value >= -1) { |
356 | sndbuf = value; |
357 | return 0; |
358 | } |
359 | break; |
360 | |
361 | case ZMQ_RCVBUF: |
362 | if (is_int && value >= -1) { |
363 | rcvbuf = value; |
364 | return 0; |
365 | } |
366 | break; |
367 | |
368 | case ZMQ_TOS: |
369 | if (is_int && value >= 0) { |
370 | tos = value; |
371 | return 0; |
372 | } |
373 | break; |
374 | |
375 | case ZMQ_LINGER: |
376 | if (is_int && value >= -1) { |
377 | linger.store (value); |
378 | return 0; |
379 | } |
380 | break; |
381 | |
382 | case ZMQ_CONNECT_TIMEOUT: |
383 | if (is_int && value >= 0) { |
384 | connect_timeout = value; |
385 | return 0; |
386 | } |
387 | break; |
388 | |
389 | case ZMQ_TCP_MAXRT: |
390 | if (is_int && value >= 0) { |
391 | tcp_maxrt = value; |
392 | return 0; |
393 | } |
394 | break; |
395 | |
396 | case ZMQ_RECONNECT_IVL: |
397 | if (is_int && value >= -1) { |
398 | reconnect_ivl = value; |
399 | return 0; |
400 | } |
401 | break; |
402 | |
403 | case ZMQ_RECONNECT_IVL_MAX: |
404 | if (is_int && value >= 0) { |
405 | reconnect_ivl_max = value; |
406 | return 0; |
407 | } |
408 | break; |
409 | |
410 | case ZMQ_BACKLOG: |
411 | if (is_int && value >= 0) { |
412 | backlog = value; |
413 | return 0; |
414 | } |
415 | break; |
416 | |
417 | case ZMQ_MAXMSGSIZE: |
418 | return do_setsockopt (optval_, optvallen_, &maxmsgsize); |
419 | |
420 | case ZMQ_MULTICAST_HOPS: |
421 | if (is_int && value > 0) { |
422 | multicast_hops = value; |
423 | return 0; |
424 | } |
425 | break; |
426 | |
427 | case ZMQ_MULTICAST_MAXTPDU: |
428 | if (is_int && value > 0) { |
429 | multicast_maxtpdu = value; |
430 | return 0; |
431 | } |
432 | break; |
433 | |
434 | case ZMQ_RCVTIMEO: |
435 | if (is_int && value >= -1) { |
436 | rcvtimeo = value; |
437 | return 0; |
438 | } |
439 | break; |
440 | |
441 | case ZMQ_SNDTIMEO: |
442 | if (is_int && value >= -1) { |
443 | sndtimeo = value; |
444 | return 0; |
445 | } |
446 | break; |
447 | |
448 | /* Deprecated in favor of ZMQ_IPV6 */ |
449 | case ZMQ_IPV4ONLY: { |
450 | bool value; |
451 | int rc = |
452 | do_setsockopt_int_as_bool_strict (optval_, optvallen_, &value); |
453 | if (rc == 0) |
454 | ipv6 = !value; |
455 | return rc; |
456 | } |
457 | |
458 | /* To replace the somewhat surprising IPV4ONLY */ |
459 | case ZMQ_IPV6: |
460 | return do_setsockopt_int_as_bool_strict (optval_, optvallen_, |
461 | &ipv6); |
462 | |
463 | case ZMQ_SOCKS_PROXY: |
464 | return do_setsockopt_string_allow_empty_strict ( |
465 | optval_, optvallen_, &socks_proxy_address, SIZE_MAX); |
466 | |
467 | case ZMQ_SOCKS_USERNAME: |
468 | /* Make empty string or NULL equivalent. */ |
469 | if (optval_ == NULL || optvallen_ == 0) { |
470 | socks_proxy_username.clear (); |
471 | return 0; |
472 | } else { |
473 | return do_setsockopt_string_allow_empty_strict ( |
474 | optval_, optvallen_, &socks_proxy_username, 255); |
475 | } |
476 | case ZMQ_SOCKS_PASSWORD: |
477 | /* Make empty string or NULL equivalent. */ |
478 | if (optval_ == NULL || optvallen_ == 0) { |
479 | socks_proxy_password.clear (); |
480 | return 0; |
481 | } else { |
482 | return do_setsockopt_string_allow_empty_strict ( |
483 | optval_, optvallen_, &socks_proxy_password, 255); |
484 | } |
485 | case ZMQ_TCP_KEEPALIVE: |
486 | if (is_int && (value == -1 || value == 0 || value == 1)) { |
487 | tcp_keepalive = value; |
488 | return 0; |
489 | } |
490 | break; |
491 | |
492 | case ZMQ_TCP_KEEPALIVE_CNT: |
493 | if (is_int && (value == -1 || value >= 0)) { |
494 | tcp_keepalive_cnt = value; |
495 | return 0; |
496 | } |
497 | break; |
498 | |
499 | case ZMQ_TCP_KEEPALIVE_IDLE: |
500 | if (is_int && (value == -1 || value >= 0)) { |
501 | tcp_keepalive_idle = value; |
502 | return 0; |
503 | } |
504 | break; |
505 | |
506 | case ZMQ_TCP_KEEPALIVE_INTVL: |
507 | if (is_int && (value == -1 || value >= 0)) { |
508 | tcp_keepalive_intvl = value; |
509 | return 0; |
510 | } |
511 | break; |
512 | |
513 | case ZMQ_IMMEDIATE: |
514 | // TODO why is immediate not bool (and called non_immediate, as its meaning appears to be reversed) |
515 | if (is_int && (value == 0 || value == 1)) { |
516 | immediate = value; |
517 | return 0; |
518 | } |
519 | break; |
520 | |
521 | case ZMQ_TCP_ACCEPT_FILTER: { |
522 | std::string filter_str; |
523 | int rc = do_setsockopt_string_allow_empty_strict ( |
524 | optval_, optvallen_, &filter_str, UCHAR_MAX); |
525 | if (rc == 0) { |
526 | if (filter_str.empty ()) { |
527 | tcp_accept_filters.clear (); |
528 | } else { |
529 | tcp_address_mask_t mask; |
530 | rc = mask.resolve (filter_str.c_str (), ipv6); |
531 | if (rc == 0) { |
532 | tcp_accept_filters.push_back (mask); |
533 | } |
534 | } |
535 | } |
536 | return rc; |
537 | } |
538 | |
539 | #if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED |
540 | case ZMQ_IPC_FILTER_UID: |
541 | return do_setsockopt_set (optval_, optvallen_, |
542 | &ipc_uid_accept_filters); |
543 | |
544 | |
545 | case ZMQ_IPC_FILTER_GID: |
546 | return do_setsockopt_set (optval_, optvallen_, |
547 | &ipc_gid_accept_filters); |
548 | #endif |
549 | |
550 | #if defined ZMQ_HAVE_SO_PEERCRED |
551 | case ZMQ_IPC_FILTER_PID: |
552 | return do_setsockopt_set (optval_, optvallen_, |
553 | &ipc_pid_accept_filters); |
554 | #endif |
555 | |
556 | case ZMQ_PLAIN_SERVER: |
557 | if (is_int && (value == 0 || value == 1)) { |
558 | as_server = value; |
559 | mechanism = value ? ZMQ_PLAIN : ZMQ_NULL; |
560 | return 0; |
561 | } |
562 | break; |
563 | |
564 | case ZMQ_PLAIN_USERNAME: |
565 | if (optvallen_ == 0 && optval_ == NULL) { |
566 | mechanism = ZMQ_NULL; |
567 | return 0; |
568 | } else if (optvallen_ > 0 && optvallen_ <= UCHAR_MAX |
569 | && optval_ != NULL) { |
570 | plain_username.assign (static_cast<const char *> (optval_), |
571 | optvallen_); |
572 | as_server = 0; |
573 | mechanism = ZMQ_PLAIN; |
574 | return 0; |
575 | } |
576 | break; |
577 | |
578 | case ZMQ_PLAIN_PASSWORD: |
579 | if (optvallen_ == 0 && optval_ == NULL) { |
580 | mechanism = ZMQ_NULL; |
581 | return 0; |
582 | } else if (optvallen_ > 0 && optvallen_ <= UCHAR_MAX |
583 | && optval_ != NULL) { |
584 | plain_password.assign (static_cast<const char *> (optval_), |
585 | optvallen_); |
586 | as_server = 0; |
587 | mechanism = ZMQ_PLAIN; |
588 | return 0; |
589 | } |
590 | break; |
591 | |
592 | case ZMQ_ZAP_DOMAIN: |
593 | return do_setsockopt_string_allow_empty_relaxed ( |
594 | optval_, optvallen_, &zap_domain, UCHAR_MAX); |
595 | break; |
596 | |
597 | // If curve encryption isn't built, these options provoke EINVAL |
598 | #ifdef ZMQ_HAVE_CURVE |
599 | case ZMQ_CURVE_SERVER: |
600 | if (is_int && (value == 0 || value == 1)) { |
601 | as_server = value; |
602 | mechanism = value ? ZMQ_CURVE : ZMQ_NULL; |
603 | return 0; |
604 | } |
605 | break; |
606 | |
607 | case ZMQ_CURVE_PUBLICKEY: |
608 | if (0 == set_curve_key (curve_public_key, optval_, optvallen_)) { |
609 | return 0; |
610 | } |
611 | break; |
612 | |
613 | case ZMQ_CURVE_SECRETKEY: |
614 | if (0 == set_curve_key (curve_secret_key, optval_, optvallen_)) { |
615 | return 0; |
616 | } |
617 | break; |
618 | |
619 | case ZMQ_CURVE_SERVERKEY: |
620 | if (0 == set_curve_key (curve_server_key, optval_, optvallen_)) { |
621 | as_server = 0; |
622 | return 0; |
623 | } |
624 | break; |
625 | #endif |
626 | |
627 | case ZMQ_CONFLATE: |
628 | return do_setsockopt_int_as_bool_strict (optval_, optvallen_, |
629 | &conflate); |
630 | |
631 | // If libgssapi isn't installed, these options provoke EINVAL |
632 | #ifdef HAVE_LIBGSSAPI_KRB5 |
633 | case ZMQ_GSSAPI_SERVER: |
634 | if (is_int && (value == 0 || value == 1)) { |
635 | as_server = value; |
636 | mechanism = ZMQ_GSSAPI; |
637 | return 0; |
638 | } |
639 | break; |
640 | |
641 | case ZMQ_GSSAPI_PRINCIPAL: |
642 | if (optvallen_ > 0 && optvallen_ <= UCHAR_MAX && optval_ != NULL) { |
643 | gss_principal.assign ((const char *) optval_, optvallen_); |
644 | mechanism = ZMQ_GSSAPI; |
645 | return 0; |
646 | } |
647 | break; |
648 | |
649 | case ZMQ_GSSAPI_SERVICE_PRINCIPAL: |
650 | if (optvallen_ > 0 && optvallen_ <= UCHAR_MAX && optval_ != NULL) { |
651 | gss_service_principal.assign ((const char *) optval_, |
652 | optvallen_); |
653 | mechanism = ZMQ_GSSAPI; |
654 | as_server = 0; |
655 | return 0; |
656 | } |
657 | break; |
658 | |
659 | case ZMQ_GSSAPI_PLAINTEXT: |
660 | return do_setsockopt_int_as_bool_strict (optval_, optvallen_, |
661 | &gss_plaintext); |
662 | |
663 | case ZMQ_GSSAPI_PRINCIPAL_NAMETYPE: |
664 | if (is_int |
665 | && (value == ZMQ_GSSAPI_NT_HOSTBASED |
666 | || value == ZMQ_GSSAPI_NT_USER_NAME |
667 | || value == ZMQ_GSSAPI_NT_KRB5_PRINCIPAL)) { |
668 | gss_principal_nt = value; |
669 | return 0; |
670 | } |
671 | break; |
672 | |
673 | case ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE: |
674 | if (is_int |
675 | && (value == ZMQ_GSSAPI_NT_HOSTBASED |
676 | || value == ZMQ_GSSAPI_NT_USER_NAME |
677 | || value == ZMQ_GSSAPI_NT_KRB5_PRINCIPAL)) { |
678 | gss_service_principal_nt = value; |
679 | return 0; |
680 | } |
681 | break; |
682 | #endif |
683 | |
684 | case ZMQ_HANDSHAKE_IVL: |
685 | if (is_int && value >= 0) { |
686 | handshake_ivl = value; |
687 | return 0; |
688 | } |
689 | break; |
690 | |
691 | case ZMQ_INVERT_MATCHING: |
692 | return do_setsockopt_int_as_bool_relaxed (optval_, optvallen_, |
693 | &invert_matching); |
694 | |
695 | case ZMQ_HEARTBEAT_IVL: |
696 | if (is_int && value >= 0) { |
697 | heartbeat_interval = value; |
698 | return 0; |
699 | } |
700 | break; |
701 | |
702 | case ZMQ_HEARTBEAT_TTL: |
703 | // Convert this to deciseconds from milliseconds |
704 | value = value / deciseconds_per_millisecond; |
705 | if (is_int && value >= 0 && value <= UINT16_MAX) { |
706 | heartbeat_ttl = static_cast<uint16_t> (value); |
707 | return 0; |
708 | } |
709 | break; |
710 | |
711 | case ZMQ_HEARTBEAT_TIMEOUT: |
712 | if (is_int && value >= 0) { |
713 | heartbeat_timeout = value; |
714 | return 0; |
715 | } |
716 | break; |
717 | |
718 | #ifdef ZMQ_HAVE_VMCI |
719 | case ZMQ_VMCI_BUFFER_SIZE: |
720 | return do_setsockopt (optval_, optvallen_, &vmci_buffer_size); |
721 | |
722 | case ZMQ_VMCI_BUFFER_MIN_SIZE: |
723 | return do_setsockopt (optval_, optvallen_, &vmci_buffer_min_size); |
724 | |
725 | case ZMQ_VMCI_BUFFER_MAX_SIZE: |
726 | return do_setsockopt (optval_, optvallen_, &vmci_buffer_max_size); |
727 | |
728 | case ZMQ_VMCI_CONNECT_TIMEOUT: |
729 | return do_setsockopt (optval_, optvallen_, &vmci_connect_timeout); |
730 | #endif |
731 | |
732 | case ZMQ_USE_FD: |
733 | if (is_int && value >= -1) { |
734 | use_fd = value; |
735 | return 0; |
736 | } |
737 | break; |
738 | |
739 | case ZMQ_BINDTODEVICE: |
740 | return do_setsockopt_string_allow_empty_strict ( |
741 | optval_, optvallen_, &bound_device, BINDDEVSIZ); |
742 | |
743 | case ZMQ_ZAP_ENFORCE_DOMAIN: |
744 | return do_setsockopt_int_as_bool_relaxed (optval_, optvallen_, |
745 | &zap_enforce_domain); |
746 | |
747 | case ZMQ_LOOPBACK_FASTPATH: |
748 | return do_setsockopt_int_as_bool_relaxed (optval_, optvallen_, |
749 | &loopback_fastpath); |
750 | |
751 | case ZMQ_METADATA: |
752 | if (optvallen_ > 0 && !is_int) { |
753 | const std::string s (static_cast<const char *> (optval_)); |
754 | const size_t pos = s.find (':'); |
755 | if (pos != std::string::npos && pos != 0 |
756 | && pos != s.length () - 1) { |
757 | const std::string key = s.substr (0, pos); |
758 | if (key.compare (0, 2, "X-" ) == 0 |
759 | && key.length () <= UCHAR_MAX) { |
760 | std::string val = s.substr (pos + 1, s.length ()); |
761 | app_metadata.insert ( |
762 | std::pair<std::string, std::string> (key, val)); |
763 | return 0; |
764 | } |
765 | } |
766 | } |
767 | errno = EINVAL; |
768 | return -1; |
769 | |
770 | case ZMQ_MULTICAST_LOOP: |
771 | return do_setsockopt_int_as_bool_relaxed (optval_, optvallen_, |
772 | &multicast_loop); |
773 | |
774 | #ifdef ZMQ_BUILD_DRAFT_API |
775 | case ZMQ_IN_BATCH_SIZE: |
776 | if (is_int && value > 0) { |
777 | in_batch_size = value; |
778 | return 0; |
779 | } |
780 | break; |
781 | |
782 | case ZMQ_OUT_BATCH_SIZE: |
783 | if (is_int && value > 0) { |
784 | out_batch_size = value; |
785 | return 0; |
786 | } |
787 | break; |
788 | |
789 | case ZMQ_WSS_KEY_PEM: |
790 | // TODO: check if valid certificate |
791 | wss_key_pem = std::string ((char *) optval_, optvallen_); |
792 | return 0; |
793 | case ZMQ_WSS_CERT_PEM: |
794 | // TODO: check if valid certificate |
795 | wss_cert_pem = std::string ((char *) optval_, optvallen_); |
796 | return 0; |
797 | case ZMQ_WSS_TRUST_PEM: |
798 | // TODO: check if valid certificate |
799 | wss_trust_pem = std::string ((char *) optval_, optvallen_); |
800 | return 0; |
801 | case ZMQ_WSS_HOSTNAME: |
802 | wss_hostname = std::string ((char *) optval_, optvallen_); |
803 | return 0; |
804 | case ZMQ_WSS_TRUST_SYSTEM: |
805 | return do_setsockopt_int_as_bool_strict (optval_, optvallen_, |
806 | &wss_trust_system); |
807 | |
808 | #endif |
809 | |
810 | default: |
811 | #if defined(ZMQ_ACT_MILITANT) |
812 | // There are valid scenarios for probing with unknown socket option |
813 | // values, e.g. to check if security is enabled or not. This will not |
814 | // provoke a militant assert. However, passing bad values to a valid |
815 | // socket option will, if ZMQ_ACT_MILITANT is defined. |
816 | malformed = false; |
817 | #endif |
818 | break; |
819 | } |
820 | |
821 | // TODO mechanism should either be set explicitly, or determined when |
822 | // connecting. currently, it depends on the order of setsockopt calls |
823 | // if there is some inconsistency, which is confusing. in addition, |
824 | // the assumed or set mechanism should be queryable (as a socket option) |
825 | |
826 | #if defined(ZMQ_ACT_MILITANT) |
827 | // There is no valid use case for passing an error back to the application |
828 | // when it sent malformed arguments to a socket option. Use ./configure |
829 | // --with-militant to enable this checking. |
830 | if (malformed) |
831 | zmq_assert (false); |
832 | #endif |
833 | errno = EINVAL; |
834 | return -1; |
835 | } |
836 | |
837 | int zmq::options_t::getsockopt (int option_, |
838 | void *optval_, |
839 | size_t *optvallen_) const |
840 | { |
841 | const bool is_int = (*optvallen_ == sizeof (int)); |
842 | int *value = static_cast<int *> (optval_); |
843 | #if defined(ZMQ_ACT_MILITANT) |
844 | bool malformed = true; // Did caller pass a bad option value? |
845 | #endif |
846 | |
847 | switch (option_) { |
848 | case ZMQ_SNDHWM: |
849 | if (is_int) { |
850 | *value = sndhwm; |
851 | return 0; |
852 | } |
853 | break; |
854 | |
855 | case ZMQ_RCVHWM: |
856 | if (is_int) { |
857 | *value = rcvhwm; |
858 | return 0; |
859 | } |
860 | break; |
861 | |
862 | case ZMQ_AFFINITY: |
863 | if (*optvallen_ == sizeof (uint64_t)) { |
864 | *(static_cast<uint64_t *> (optval_)) = affinity; |
865 | return 0; |
866 | } |
867 | break; |
868 | |
869 | case ZMQ_ROUTING_ID: |
870 | return do_getsockopt (optval_, optvallen_, routing_id, |
871 | routing_id_size); |
872 | break; |
873 | |
874 | case ZMQ_RATE: |
875 | if (is_int) { |
876 | *value = rate; |
877 | return 0; |
878 | } |
879 | break; |
880 | |
881 | case ZMQ_RECOVERY_IVL: |
882 | if (is_int) { |
883 | *value = recovery_ivl; |
884 | return 0; |
885 | } |
886 | break; |
887 | |
888 | case ZMQ_SNDBUF: |
889 | if (is_int) { |
890 | *value = sndbuf; |
891 | return 0; |
892 | } |
893 | break; |
894 | |
895 | case ZMQ_RCVBUF: |
896 | if (is_int) { |
897 | *value = rcvbuf; |
898 | return 0; |
899 | } |
900 | break; |
901 | |
902 | case ZMQ_TOS: |
903 | if (is_int) { |
904 | *value = tos; |
905 | return 0; |
906 | } |
907 | break; |
908 | |
909 | case ZMQ_TYPE: |
910 | if (is_int) { |
911 | *value = type; |
912 | return 0; |
913 | } |
914 | break; |
915 | |
916 | case ZMQ_LINGER: |
917 | if (is_int) { |
918 | *value = linger.load (); |
919 | return 0; |
920 | } |
921 | break; |
922 | |
923 | case ZMQ_CONNECT_TIMEOUT: |
924 | if (is_int) { |
925 | *value = connect_timeout; |
926 | return 0; |
927 | } |
928 | break; |
929 | |
930 | case ZMQ_TCP_MAXRT: |
931 | if (is_int) { |
932 | *value = tcp_maxrt; |
933 | return 0; |
934 | } |
935 | break; |
936 | |
937 | case ZMQ_RECONNECT_IVL: |
938 | if (is_int) { |
939 | *value = reconnect_ivl; |
940 | return 0; |
941 | } |
942 | break; |
943 | |
944 | case ZMQ_RECONNECT_IVL_MAX: |
945 | if (is_int) { |
946 | *value = reconnect_ivl_max; |
947 | return 0; |
948 | } |
949 | break; |
950 | |
951 | case ZMQ_BACKLOG: |
952 | if (is_int) { |
953 | *value = backlog; |
954 | return 0; |
955 | } |
956 | break; |
957 | |
958 | case ZMQ_MAXMSGSIZE: |
959 | if (*optvallen_ == sizeof (int64_t)) { |
960 | *(static_cast<int64_t *> (optval_)) = maxmsgsize; |
961 | *optvallen_ = sizeof (int64_t); |
962 | return 0; |
963 | } |
964 | break; |
965 | |
966 | case ZMQ_MULTICAST_HOPS: |
967 | if (is_int) { |
968 | *value = multicast_hops; |
969 | return 0; |
970 | } |
971 | break; |
972 | |
973 | case ZMQ_MULTICAST_MAXTPDU: |
974 | if (is_int) { |
975 | *value = multicast_maxtpdu; |
976 | return 0; |
977 | } |
978 | break; |
979 | |
980 | case ZMQ_RCVTIMEO: |
981 | if (is_int) { |
982 | *value = rcvtimeo; |
983 | return 0; |
984 | } |
985 | break; |
986 | |
987 | case ZMQ_SNDTIMEO: |
988 | if (is_int) { |
989 | *value = sndtimeo; |
990 | return 0; |
991 | } |
992 | break; |
993 | |
994 | case ZMQ_IPV4ONLY: |
995 | if (is_int) { |
996 | *value = 1 - ipv6; |
997 | return 0; |
998 | } |
999 | break; |
1000 | |
1001 | case ZMQ_IPV6: |
1002 | if (is_int) { |
1003 | *value = ipv6; |
1004 | return 0; |
1005 | } |
1006 | break; |
1007 | |
1008 | case ZMQ_IMMEDIATE: |
1009 | if (is_int) { |
1010 | *value = immediate; |
1011 | return 0; |
1012 | } |
1013 | break; |
1014 | |
1015 | case ZMQ_SOCKS_PROXY: |
1016 | return do_getsockopt (optval_, optvallen_, socks_proxy_address); |
1017 | break; |
1018 | |
1019 | case ZMQ_SOCKS_USERNAME: |
1020 | return do_getsockopt (optval_, optvallen_, socks_proxy_username); |
1021 | break; |
1022 | |
1023 | case ZMQ_SOCKS_PASSWORD: |
1024 | return do_getsockopt (optval_, optvallen_, socks_proxy_password); |
1025 | break; |
1026 | |
1027 | case ZMQ_TCP_KEEPALIVE: |
1028 | if (is_int) { |
1029 | *value = tcp_keepalive; |
1030 | return 0; |
1031 | } |
1032 | break; |
1033 | |
1034 | case ZMQ_TCP_KEEPALIVE_CNT: |
1035 | if (is_int) { |
1036 | *value = tcp_keepalive_cnt; |
1037 | return 0; |
1038 | } |
1039 | break; |
1040 | |
1041 | case ZMQ_TCP_KEEPALIVE_IDLE: |
1042 | if (is_int) { |
1043 | *value = tcp_keepalive_idle; |
1044 | return 0; |
1045 | } |
1046 | break; |
1047 | |
1048 | case ZMQ_TCP_KEEPALIVE_INTVL: |
1049 | if (is_int) { |
1050 | *value = tcp_keepalive_intvl; |
1051 | return 0; |
1052 | } |
1053 | break; |
1054 | |
1055 | case ZMQ_MECHANISM: |
1056 | if (is_int) { |
1057 | *value = mechanism; |
1058 | return 0; |
1059 | } |
1060 | break; |
1061 | |
1062 | case ZMQ_PLAIN_SERVER: |
1063 | if (is_int) { |
1064 | *value = as_server && mechanism == ZMQ_PLAIN; |
1065 | return 0; |
1066 | } |
1067 | break; |
1068 | |
1069 | case ZMQ_PLAIN_USERNAME: |
1070 | return do_getsockopt (optval_, optvallen_, plain_username); |
1071 | break; |
1072 | |
1073 | case ZMQ_PLAIN_PASSWORD: |
1074 | return do_getsockopt (optval_, optvallen_, plain_password); |
1075 | break; |
1076 | |
1077 | case ZMQ_ZAP_DOMAIN: |
1078 | return do_getsockopt (optval_, optvallen_, zap_domain); |
1079 | break; |
1080 | |
1081 | // If curve encryption isn't built, these options provoke EINVAL |
1082 | #ifdef ZMQ_HAVE_CURVE |
1083 | case ZMQ_CURVE_SERVER: |
1084 | if (is_int) { |
1085 | *value = as_server && mechanism == ZMQ_CURVE; |
1086 | return 0; |
1087 | } |
1088 | break; |
1089 | |
1090 | case ZMQ_CURVE_PUBLICKEY: |
1091 | return do_getsockopt_curve_key (optval_, optvallen_, |
1092 | curve_public_key); |
1093 | break; |
1094 | |
1095 | case ZMQ_CURVE_SECRETKEY: |
1096 | return do_getsockopt_curve_key (optval_, optvallen_, |
1097 | curve_secret_key); |
1098 | break; |
1099 | |
1100 | case ZMQ_CURVE_SERVERKEY: |
1101 | return do_getsockopt_curve_key (optval_, optvallen_, |
1102 | curve_server_key); |
1103 | break; |
1104 | #endif |
1105 | |
1106 | case ZMQ_CONFLATE: |
1107 | if (is_int) { |
1108 | *value = conflate; |
1109 | return 0; |
1110 | } |
1111 | break; |
1112 | |
1113 | // If libgssapi isn't installed, these options provoke EINVAL |
1114 | #ifdef HAVE_LIBGSSAPI_KRB5 |
1115 | case ZMQ_GSSAPI_SERVER: |
1116 | if (is_int) { |
1117 | *value = as_server && mechanism == ZMQ_GSSAPI; |
1118 | return 0; |
1119 | } |
1120 | break; |
1121 | |
1122 | case ZMQ_GSSAPI_PRINCIPAL: |
1123 | return do_getsockopt (optval_, optvallen_, gss_principal); |
1124 | break; |
1125 | |
1126 | case ZMQ_GSSAPI_SERVICE_PRINCIPAL: |
1127 | return do_getsockopt (optval_, optvallen_, gss_service_principal); |
1128 | break; |
1129 | |
1130 | case ZMQ_GSSAPI_PLAINTEXT: |
1131 | if (is_int) { |
1132 | *value = gss_plaintext; |
1133 | return 0; |
1134 | } |
1135 | break; |
1136 | |
1137 | case ZMQ_GSSAPI_PRINCIPAL_NAMETYPE: |
1138 | if (is_int) { |
1139 | *value = gss_principal_nt; |
1140 | return 0; |
1141 | } |
1142 | break; |
1143 | case ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE: |
1144 | if (is_int) { |
1145 | *value = gss_service_principal_nt; |
1146 | return 0; |
1147 | } |
1148 | break; |
1149 | #endif |
1150 | |
1151 | case ZMQ_HANDSHAKE_IVL: |
1152 | if (is_int) { |
1153 | *value = handshake_ivl; |
1154 | return 0; |
1155 | } |
1156 | break; |
1157 | |
1158 | case ZMQ_INVERT_MATCHING: |
1159 | if (is_int) { |
1160 | *value = invert_matching; |
1161 | return 0; |
1162 | } |
1163 | break; |
1164 | |
1165 | case ZMQ_HEARTBEAT_IVL: |
1166 | if (is_int) { |
1167 | *value = heartbeat_interval; |
1168 | return 0; |
1169 | } |
1170 | break; |
1171 | |
1172 | case ZMQ_HEARTBEAT_TTL: |
1173 | if (is_int) { |
1174 | // Convert the internal deciseconds value to milliseconds |
1175 | *value = heartbeat_ttl * 100; |
1176 | return 0; |
1177 | } |
1178 | break; |
1179 | |
1180 | case ZMQ_HEARTBEAT_TIMEOUT: |
1181 | if (is_int) { |
1182 | *value = heartbeat_timeout; |
1183 | return 0; |
1184 | } |
1185 | break; |
1186 | |
1187 | case ZMQ_USE_FD: |
1188 | if (is_int) { |
1189 | *value = use_fd; |
1190 | return 0; |
1191 | } |
1192 | break; |
1193 | |
1194 | case ZMQ_BINDTODEVICE: |
1195 | return do_getsockopt (optval_, optvallen_, bound_device); |
1196 | break; |
1197 | |
1198 | case ZMQ_ZAP_ENFORCE_DOMAIN: |
1199 | if (is_int) { |
1200 | *value = zap_enforce_domain; |
1201 | return 0; |
1202 | } |
1203 | break; |
1204 | |
1205 | case ZMQ_LOOPBACK_FASTPATH: |
1206 | if (is_int) { |
1207 | *value = loopback_fastpath; |
1208 | return 0; |
1209 | } |
1210 | break; |
1211 | |
1212 | case ZMQ_MULTICAST_LOOP: |
1213 | if (is_int) { |
1214 | *value = multicast_loop; |
1215 | return 0; |
1216 | } |
1217 | break; |
1218 | |
1219 | #ifdef ZMQ_BUILD_DRAFT_API |
1220 | case ZMQ_ROUTER_NOTIFY: |
1221 | if (is_int) { |
1222 | *value = router_notify; |
1223 | return 0; |
1224 | } |
1225 | break; |
1226 | case ZMQ_IN_BATCH_SIZE: |
1227 | if (is_int) { |
1228 | *value = in_batch_size; |
1229 | return 0; |
1230 | } |
1231 | break; |
1232 | |
1233 | case ZMQ_OUT_BATCH_SIZE: |
1234 | if (is_int) { |
1235 | *value = out_batch_size; |
1236 | return 0; |
1237 | } |
1238 | break; |
1239 | #endif |
1240 | |
1241 | |
1242 | default: |
1243 | #if defined(ZMQ_ACT_MILITANT) |
1244 | malformed = false; |
1245 | #endif |
1246 | break; |
1247 | } |
1248 | #if defined(ZMQ_ACT_MILITANT) |
1249 | if (malformed) |
1250 | zmq_assert (false); |
1251 | #endif |
1252 | errno = EINVAL; |
1253 | return -1; |
1254 | } |
1255 | |