1 | // |
2 | // detail/socket_option.hpp |
3 | // ~~~~~~~~~~~~~~~~~~~~~~~~ |
4 | // |
5 | // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
6 | // |
7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying |
8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
9 | // |
10 | |
11 | #ifndef BOOST_ASIO_IP_DETAIL_SOCKET_OPTION_HPP |
12 | #define BOOST_ASIO_IP_DETAIL_SOCKET_OPTION_HPP |
13 | |
14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) |
15 | # pragma once |
16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
17 | |
18 | #include <boost/asio/detail/config.hpp> |
19 | #include <cstddef> |
20 | #include <cstring> |
21 | #include <stdexcept> |
22 | #include <boost/asio/detail/socket_ops.hpp> |
23 | #include <boost/asio/detail/socket_types.hpp> |
24 | #include <boost/asio/detail/throw_exception.hpp> |
25 | #include <boost/asio/ip/address.hpp> |
26 | |
27 | #include <boost/asio/detail/push_options.hpp> |
28 | |
29 | namespace boost { |
30 | namespace asio { |
31 | namespace ip { |
32 | namespace detail { |
33 | namespace socket_option { |
34 | |
35 | // Helper template for implementing multicast enable loopback options. |
36 | template <int IPv4_Level, int IPv4_Name, int IPv6_Level, int IPv6_Name> |
37 | class multicast_enable_loopback |
38 | { |
39 | public: |
40 | #if defined(__sun) || defined(__osf__) |
41 | typedef unsigned char ipv4_value_type; |
42 | typedef unsigned char ipv6_value_type; |
43 | #elif defined(_AIX) || defined(__hpux) || defined(__QNXNTO__) |
44 | typedef unsigned char ipv4_value_type; |
45 | typedef unsigned int ipv6_value_type; |
46 | #else |
47 | typedef int ipv4_value_type; |
48 | typedef int ipv6_value_type; |
49 | #endif |
50 | |
51 | // Default constructor. |
52 | multicast_enable_loopback() |
53 | : ipv4_value_(0), |
54 | ipv6_value_(0) |
55 | { |
56 | } |
57 | |
58 | // Construct with a specific option value. |
59 | explicit multicast_enable_loopback(bool v) |
60 | : ipv4_value_(v ? 1 : 0), |
61 | ipv6_value_(v ? 1 : 0) |
62 | { |
63 | } |
64 | |
65 | // Set the value of the boolean. |
66 | multicast_enable_loopback& operator=(bool v) |
67 | { |
68 | ipv4_value_ = v ? 1 : 0; |
69 | ipv6_value_ = v ? 1 : 0; |
70 | return *this; |
71 | } |
72 | |
73 | // Get the current value of the boolean. |
74 | bool value() const |
75 | { |
76 | return !!ipv4_value_; |
77 | } |
78 | |
79 | // Convert to bool. |
80 | operator bool() const |
81 | { |
82 | return !!ipv4_value_; |
83 | } |
84 | |
85 | // Test for false. |
86 | bool operator!() const |
87 | { |
88 | return !ipv4_value_; |
89 | } |
90 | |
91 | // Get the level of the socket option. |
92 | template <typename Protocol> |
93 | int level(const Protocol& protocol) const |
94 | { |
95 | if (protocol.family() == PF_INET6) |
96 | return IPv6_Level; |
97 | return IPv4_Level; |
98 | } |
99 | |
100 | // Get the name of the socket option. |
101 | template <typename Protocol> |
102 | int name(const Protocol& protocol) const |
103 | { |
104 | if (protocol.family() == PF_INET6) |
105 | return IPv6_Name; |
106 | return IPv4_Name; |
107 | } |
108 | |
109 | // Get the address of the boolean data. |
110 | template <typename Protocol> |
111 | void* data(const Protocol& protocol) |
112 | { |
113 | if (protocol.family() == PF_INET6) |
114 | return &ipv6_value_; |
115 | return &ipv4_value_; |
116 | } |
117 | |
118 | // Get the address of the boolean data. |
119 | template <typename Protocol> |
120 | const void* data(const Protocol& protocol) const |
121 | { |
122 | if (protocol.family() == PF_INET6) |
123 | return &ipv6_value_; |
124 | return &ipv4_value_; |
125 | } |
126 | |
127 | // Get the size of the boolean data. |
128 | template <typename Protocol> |
129 | std::size_t size(const Protocol& protocol) const |
130 | { |
131 | if (protocol.family() == PF_INET6) |
132 | return sizeof(ipv6_value_); |
133 | return sizeof(ipv4_value_); |
134 | } |
135 | |
136 | // Set the size of the boolean data. |
137 | template <typename Protocol> |
138 | void resize(const Protocol& protocol, std::size_t s) |
139 | { |
140 | if (protocol.family() == PF_INET6) |
141 | { |
142 | if (s != sizeof(ipv6_value_)) |
143 | { |
144 | std::length_error ex("multicast_enable_loopback socket option resize" ); |
145 | boost::asio::detail::throw_exception(ex); |
146 | } |
147 | ipv4_value_ = ipv6_value_ ? 1 : 0; |
148 | } |
149 | else |
150 | { |
151 | if (s != sizeof(ipv4_value_)) |
152 | { |
153 | std::length_error ex("multicast_enable_loopback socket option resize" ); |
154 | boost::asio::detail::throw_exception(ex); |
155 | } |
156 | ipv6_value_ = ipv4_value_ ? 1 : 0; |
157 | } |
158 | } |
159 | |
160 | private: |
161 | ipv4_value_type ipv4_value_; |
162 | ipv6_value_type ipv6_value_; |
163 | }; |
164 | |
165 | // Helper template for implementing unicast hops options. |
166 | template <int IPv4_Level, int IPv4_Name, int IPv6_Level, int IPv6_Name> |
167 | class unicast_hops |
168 | { |
169 | public: |
170 | // Default constructor. |
171 | unicast_hops() |
172 | : value_(0) |
173 | { |
174 | } |
175 | |
176 | // Construct with a specific option value. |
177 | explicit unicast_hops(int v) |
178 | : value_(v) |
179 | { |
180 | } |
181 | |
182 | // Set the value of the option. |
183 | unicast_hops& operator=(int v) |
184 | { |
185 | value_ = v; |
186 | return *this; |
187 | } |
188 | |
189 | // Get the current value of the option. |
190 | int value() const |
191 | { |
192 | return value_; |
193 | } |
194 | |
195 | // Get the level of the socket option. |
196 | template <typename Protocol> |
197 | int level(const Protocol& protocol) const |
198 | { |
199 | if (protocol.family() == PF_INET6) |
200 | return IPv6_Level; |
201 | return IPv4_Level; |
202 | } |
203 | |
204 | // Get the name of the socket option. |
205 | template <typename Protocol> |
206 | int name(const Protocol& protocol) const |
207 | { |
208 | if (protocol.family() == PF_INET6) |
209 | return IPv6_Name; |
210 | return IPv4_Name; |
211 | } |
212 | |
213 | // Get the address of the data. |
214 | template <typename Protocol> |
215 | int* data(const Protocol&) |
216 | { |
217 | return &value_; |
218 | } |
219 | |
220 | // Get the address of the data. |
221 | template <typename Protocol> |
222 | const int* data(const Protocol&) const |
223 | { |
224 | return &value_; |
225 | } |
226 | |
227 | // Get the size of the data. |
228 | template <typename Protocol> |
229 | std::size_t size(const Protocol&) const |
230 | { |
231 | return sizeof(value_); |
232 | } |
233 | |
234 | // Set the size of the data. |
235 | template <typename Protocol> |
236 | void resize(const Protocol&, std::size_t s) |
237 | { |
238 | if (s != sizeof(value_)) |
239 | { |
240 | std::length_error ex("unicast hops socket option resize" ); |
241 | boost::asio::detail::throw_exception(ex); |
242 | } |
243 | #if defined(__hpux) |
244 | if (value_ < 0) |
245 | value_ = value_ & 0xFF; |
246 | #endif |
247 | } |
248 | |
249 | private: |
250 | int value_; |
251 | }; |
252 | |
253 | // Helper template for implementing multicast hops options. |
254 | template <int IPv4_Level, int IPv4_Name, int IPv6_Level, int IPv6_Name> |
255 | class multicast_hops |
256 | { |
257 | public: |
258 | #if defined(BOOST_ASIO_WINDOWS) && defined(UNDER_CE) |
259 | typedef int ipv4_value_type; |
260 | #else |
261 | typedef unsigned char ipv4_value_type; |
262 | #endif |
263 | typedef int ipv6_value_type; |
264 | |
265 | // Default constructor. |
266 | multicast_hops() |
267 | : ipv4_value_(0), |
268 | ipv6_value_(0) |
269 | { |
270 | } |
271 | |
272 | // Construct with a specific option value. |
273 | explicit multicast_hops(int v) |
274 | { |
275 | if (v < 0 || v > 255) |
276 | { |
277 | std::out_of_range ex("multicast hops value out of range" ); |
278 | boost::asio::detail::throw_exception(ex); |
279 | } |
280 | ipv4_value_ = (ipv4_value_type)v; |
281 | ipv6_value_ = v; |
282 | } |
283 | |
284 | // Set the value of the option. |
285 | multicast_hops& operator=(int v) |
286 | { |
287 | if (v < 0 || v > 255) |
288 | { |
289 | std::out_of_range ex("multicast hops value out of range" ); |
290 | boost::asio::detail::throw_exception(ex); |
291 | } |
292 | ipv4_value_ = (ipv4_value_type)v; |
293 | ipv6_value_ = v; |
294 | return *this; |
295 | } |
296 | |
297 | // Get the current value of the option. |
298 | int value() const |
299 | { |
300 | return ipv6_value_; |
301 | } |
302 | |
303 | // Get the level of the socket option. |
304 | template <typename Protocol> |
305 | int level(const Protocol& protocol) const |
306 | { |
307 | if (protocol.family() == PF_INET6) |
308 | return IPv6_Level; |
309 | return IPv4_Level; |
310 | } |
311 | |
312 | // Get the name of the socket option. |
313 | template <typename Protocol> |
314 | int name(const Protocol& protocol) const |
315 | { |
316 | if (protocol.family() == PF_INET6) |
317 | return IPv6_Name; |
318 | return IPv4_Name; |
319 | } |
320 | |
321 | // Get the address of the data. |
322 | template <typename Protocol> |
323 | void* data(const Protocol& protocol) |
324 | { |
325 | if (protocol.family() == PF_INET6) |
326 | return &ipv6_value_; |
327 | return &ipv4_value_; |
328 | } |
329 | |
330 | // Get the address of the data. |
331 | template <typename Protocol> |
332 | const void* data(const Protocol& protocol) const |
333 | { |
334 | if (protocol.family() == PF_INET6) |
335 | return &ipv6_value_; |
336 | return &ipv4_value_; |
337 | } |
338 | |
339 | // Get the size of the data. |
340 | template <typename Protocol> |
341 | std::size_t size(const Protocol& protocol) const |
342 | { |
343 | if (protocol.family() == PF_INET6) |
344 | return sizeof(ipv6_value_); |
345 | return sizeof(ipv4_value_); |
346 | } |
347 | |
348 | // Set the size of the data. |
349 | template <typename Protocol> |
350 | void resize(const Protocol& protocol, std::size_t s) |
351 | { |
352 | if (protocol.family() == PF_INET6) |
353 | { |
354 | if (s != sizeof(ipv6_value_)) |
355 | { |
356 | std::length_error ex("multicast hops socket option resize" ); |
357 | boost::asio::detail::throw_exception(ex); |
358 | } |
359 | if (ipv6_value_ < 0) |
360 | ipv4_value_ = 0; |
361 | else if (ipv6_value_ > 255) |
362 | ipv4_value_ = 255; |
363 | else |
364 | ipv4_value_ = (ipv4_value_type)ipv6_value_; |
365 | } |
366 | else |
367 | { |
368 | if (s != sizeof(ipv4_value_)) |
369 | { |
370 | std::length_error ex("multicast hops socket option resize" ); |
371 | boost::asio::detail::throw_exception(ex); |
372 | } |
373 | ipv6_value_ = ipv4_value_; |
374 | } |
375 | } |
376 | |
377 | private: |
378 | ipv4_value_type ipv4_value_; |
379 | ipv6_value_type ipv6_value_; |
380 | }; |
381 | |
382 | // Helper template for implementing ip_mreq-based options. |
383 | template <int IPv4_Level, int IPv4_Name, int IPv6_Level, int IPv6_Name> |
384 | class multicast_request |
385 | { |
386 | public: |
387 | // Default constructor. |
388 | multicast_request() |
389 | : ipv4_value_(), // Zero-initialisation gives the "any" address. |
390 | ipv6_value_() // Zero-initialisation gives the "any" address. |
391 | { |
392 | } |
393 | |
394 | // Construct with multicast address only. |
395 | explicit multicast_request(const boost::asio::ip::address& multicast_address) |
396 | : ipv4_value_(), // Zero-initialisation gives the "any" address. |
397 | ipv6_value_() // Zero-initialisation gives the "any" address. |
398 | { |
399 | if (multicast_address.is_v6()) |
400 | { |
401 | using namespace std; // For memcpy. |
402 | boost::asio::ip::address_v6 ipv6_address = multicast_address.to_v6(); |
403 | boost::asio::ip::address_v6::bytes_type bytes = ipv6_address.to_bytes(); |
404 | memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.data(), 16); |
405 | ipv6_value_.ipv6mr_interface = ipv6_address.scope_id(); |
406 | } |
407 | else |
408 | { |
409 | ipv4_value_.imr_multiaddr.s_addr = |
410 | boost::asio::detail::socket_ops::host_to_network_long( |
411 | multicast_address.to_v4().to_ulong()); |
412 | ipv4_value_.imr_interface.s_addr = |
413 | boost::asio::detail::socket_ops::host_to_network_long( |
414 | boost::asio::ip::address_v4::any().to_ulong()); |
415 | } |
416 | } |
417 | |
418 | // Construct with multicast address and IPv4 address specifying an interface. |
419 | explicit multicast_request( |
420 | const boost::asio::ip::address_v4& multicast_address, |
421 | const boost::asio::ip::address_v4& network_interface |
422 | = boost::asio::ip::address_v4::any()) |
423 | : ipv6_value_() // Zero-initialisation gives the "any" address. |
424 | { |
425 | ipv4_value_.imr_multiaddr.s_addr = |
426 | boost::asio::detail::socket_ops::host_to_network_long( |
427 | multicast_address.to_ulong()); |
428 | ipv4_value_.imr_interface.s_addr = |
429 | boost::asio::detail::socket_ops::host_to_network_long( |
430 | network_interface.to_ulong()); |
431 | } |
432 | |
433 | // Construct with multicast address and IPv6 network interface index. |
434 | explicit multicast_request( |
435 | const boost::asio::ip::address_v6& multicast_address, |
436 | unsigned long network_interface = 0) |
437 | : ipv4_value_() // Zero-initialisation gives the "any" address. |
438 | { |
439 | using namespace std; // For memcpy. |
440 | boost::asio::ip::address_v6::bytes_type bytes = |
441 | multicast_address.to_bytes(); |
442 | memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.data(), 16); |
443 | if (network_interface) |
444 | ipv6_value_.ipv6mr_interface = network_interface; |
445 | else |
446 | ipv6_value_.ipv6mr_interface = multicast_address.scope_id(); |
447 | } |
448 | |
449 | // Get the level of the socket option. |
450 | template <typename Protocol> |
451 | int level(const Protocol& protocol) const |
452 | { |
453 | if (protocol.family() == PF_INET6) |
454 | return IPv6_Level; |
455 | return IPv4_Level; |
456 | } |
457 | |
458 | // Get the name of the socket option. |
459 | template <typename Protocol> |
460 | int name(const Protocol& protocol) const |
461 | { |
462 | if (protocol.family() == PF_INET6) |
463 | return IPv6_Name; |
464 | return IPv4_Name; |
465 | } |
466 | |
467 | // Get the address of the option data. |
468 | template <typename Protocol> |
469 | const void* data(const Protocol& protocol) const |
470 | { |
471 | if (protocol.family() == PF_INET6) |
472 | return &ipv6_value_; |
473 | return &ipv4_value_; |
474 | } |
475 | |
476 | // Get the size of the option data. |
477 | template <typename Protocol> |
478 | std::size_t size(const Protocol& protocol) const |
479 | { |
480 | if (protocol.family() == PF_INET6) |
481 | return sizeof(ipv6_value_); |
482 | return sizeof(ipv4_value_); |
483 | } |
484 | |
485 | private: |
486 | boost::asio::detail::in4_mreq_type ipv4_value_; |
487 | boost::asio::detail::in6_mreq_type ipv6_value_; |
488 | }; |
489 | |
490 | // Helper template for implementing options that specify a network interface. |
491 | template <int IPv4_Level, int IPv4_Name, int IPv6_Level, int IPv6_Name> |
492 | class network_interface |
493 | { |
494 | public: |
495 | // Default constructor. |
496 | network_interface() |
497 | { |
498 | ipv4_value_.s_addr = |
499 | boost::asio::detail::socket_ops::host_to_network_long( |
500 | boost::asio::ip::address_v4::any().to_ulong()); |
501 | ipv6_value_ = 0; |
502 | } |
503 | |
504 | // Construct with IPv4 interface. |
505 | explicit network_interface(const boost::asio::ip::address_v4& ipv4_interface) |
506 | { |
507 | ipv4_value_.s_addr = |
508 | boost::asio::detail::socket_ops::host_to_network_long( |
509 | ipv4_interface.to_ulong()); |
510 | ipv6_value_ = 0; |
511 | } |
512 | |
513 | // Construct with IPv6 interface. |
514 | explicit network_interface(unsigned int ipv6_interface) |
515 | { |
516 | ipv4_value_.s_addr = |
517 | boost::asio::detail::socket_ops::host_to_network_long( |
518 | boost::asio::ip::address_v4::any().to_ulong()); |
519 | ipv6_value_ = ipv6_interface; |
520 | } |
521 | |
522 | // Get the level of the socket option. |
523 | template <typename Protocol> |
524 | int level(const Protocol& protocol) const |
525 | { |
526 | if (protocol.family() == PF_INET6) |
527 | return IPv6_Level; |
528 | return IPv4_Level; |
529 | } |
530 | |
531 | // Get the name of the socket option. |
532 | template <typename Protocol> |
533 | int name(const Protocol& protocol) const |
534 | { |
535 | if (protocol.family() == PF_INET6) |
536 | return IPv6_Name; |
537 | return IPv4_Name; |
538 | } |
539 | |
540 | // Get the address of the option data. |
541 | template <typename Protocol> |
542 | const void* data(const Protocol& protocol) const |
543 | { |
544 | if (protocol.family() == PF_INET6) |
545 | return &ipv6_value_; |
546 | return &ipv4_value_; |
547 | } |
548 | |
549 | // Get the size of the option data. |
550 | template <typename Protocol> |
551 | std::size_t size(const Protocol& protocol) const |
552 | { |
553 | if (protocol.family() == PF_INET6) |
554 | return sizeof(ipv6_value_); |
555 | return sizeof(ipv4_value_); |
556 | } |
557 | |
558 | private: |
559 | boost::asio::detail::in4_addr_type ipv4_value_; |
560 | unsigned int ipv6_value_; |
561 | }; |
562 | |
563 | } // namespace socket_option |
564 | } // namespace detail |
565 | } // namespace ip |
566 | } // namespace asio |
567 | } // namespace boost |
568 | |
569 | #include <boost/asio/detail/pop_options.hpp> |
570 | |
571 | #endif // BOOST_ASIO_IP_DETAIL_SOCKET_OPTION_HPP |
572 | |