1 // 2 // detail/socket_option.hpp 3 // ~~~~~~~~~~~~~~~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2021 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. multicast_enable_loopback()52 multicast_enable_loopback() 53 : ipv4_value_(0), 54 ipv6_value_(0) 55 { 56 } 57 58 // Construct with a specific option value. multicast_enable_loopback(bool v)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. operator =(bool v)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. value() const74 bool value() const 75 { 76 return !!ipv4_value_; 77 } 78 79 // Convert to bool. operator bool() const80 operator bool() const 81 { 82 return !!ipv4_value_; 83 } 84 85 // Test for false. operator !() const86 bool operator!() const 87 { 88 return !ipv4_value_; 89 } 90 91 // Get the level of the socket option. 92 template <typename Protocol> level(const Protocol & protocol) const93 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> name(const Protocol & protocol) const102 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> data(const Protocol & 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> data(const Protocol & protocol) const120 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> size(const Protocol & protocol) const129 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> resize(const Protocol & protocol,std::size_t s)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. unicast_hops()171 unicast_hops() 172 : value_(0) 173 { 174 } 175 176 // Construct with a specific option value. unicast_hops(int v)177 explicit unicast_hops(int v) 178 : value_(v) 179 { 180 } 181 182 // Set the value of the option. operator =(int v)183 unicast_hops& operator=(int v) 184 { 185 value_ = v; 186 return *this; 187 } 188 189 // Get the current value of the option. value() const190 int value() const 191 { 192 return value_; 193 } 194 195 // Get the level of the socket option. 196 template <typename Protocol> level(const Protocol & protocol) const197 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> name(const Protocol & protocol) const206 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> data(const Protocol &)215 int* data(const Protocol&) 216 { 217 return &value_; 218 } 219 220 // Get the address of the data. 221 template <typename Protocol> data(const Protocol &) const222 const int* data(const Protocol&) const 223 { 224 return &value_; 225 } 226 227 // Get the size of the data. 228 template <typename Protocol> size(const Protocol &) const229 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> resize(const Protocol &,std::size_t s)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. multicast_hops()266 multicast_hops() 267 : ipv4_value_(0), 268 ipv6_value_(0) 269 { 270 } 271 272 // Construct with a specific option value. multicast_hops(int v)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. operator =(int v)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. value() const298 int value() const 299 { 300 return ipv6_value_; 301 } 302 303 // Get the level of the socket option. 304 template <typename Protocol> level(const Protocol & protocol) const305 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> name(const Protocol & protocol) const314 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> data(const Protocol & 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> data(const Protocol & protocol) const332 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> size(const Protocol & protocol) const341 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> resize(const Protocol & protocol,std::size_t s)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. multicast_request()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. multicast_request(const address & multicast_address)395 explicit multicast_request(const 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 address_v6 ipv6_address = multicast_address.to_v6(); 403 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_uint()); 412 ipv4_value_.imr_interface.s_addr = 413 boost::asio::detail::socket_ops::host_to_network_long( 414 address_v4::any().to_uint()); 415 } 416 } 417 418 // Construct with multicast address and IPv4 address specifying an interface. multicast_request(const address_v4 & multicast_address,const address_v4 & network_interface=address_v4::any ())419 explicit multicast_request(const address_v4& multicast_address, 420 const address_v4& network_interface = address_v4::any()) 421 : ipv6_value_() // Zero-initialisation gives the "any" address. 422 { 423 ipv4_value_.imr_multiaddr.s_addr = 424 boost::asio::detail::socket_ops::host_to_network_long( 425 multicast_address.to_uint()); 426 ipv4_value_.imr_interface.s_addr = 427 boost::asio::detail::socket_ops::host_to_network_long( 428 network_interface.to_uint()); 429 } 430 431 // Construct with multicast address and IPv6 network interface index. multicast_request(const address_v6 & multicast_address,unsigned long network_interface=0)432 explicit multicast_request( 433 const address_v6& multicast_address, 434 unsigned long network_interface = 0) 435 : ipv4_value_() // Zero-initialisation gives the "any" address. 436 { 437 using namespace std; // For memcpy. 438 address_v6::bytes_type bytes = multicast_address.to_bytes(); 439 memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.data(), 16); 440 if (network_interface) 441 ipv6_value_.ipv6mr_interface = network_interface; 442 else 443 ipv6_value_.ipv6mr_interface = multicast_address.scope_id(); 444 } 445 446 // Get the level of the socket option. 447 template <typename Protocol> level(const Protocol & protocol) const448 int level(const Protocol& protocol) const 449 { 450 if (protocol.family() == PF_INET6) 451 return IPv6_Level; 452 return IPv4_Level; 453 } 454 455 // Get the name of the socket option. 456 template <typename Protocol> name(const Protocol & protocol) const457 int name(const Protocol& protocol) const 458 { 459 if (protocol.family() == PF_INET6) 460 return IPv6_Name; 461 return IPv4_Name; 462 } 463 464 // Get the address of the option data. 465 template <typename Protocol> data(const Protocol & protocol) const466 const void* data(const Protocol& protocol) const 467 { 468 if (protocol.family() == PF_INET6) 469 return &ipv6_value_; 470 return &ipv4_value_; 471 } 472 473 // Get the size of the option data. 474 template <typename Protocol> size(const Protocol & protocol) const475 std::size_t size(const Protocol& protocol) const 476 { 477 if (protocol.family() == PF_INET6) 478 return sizeof(ipv6_value_); 479 return sizeof(ipv4_value_); 480 } 481 482 private: 483 boost::asio::detail::in4_mreq_type ipv4_value_; 484 boost::asio::detail::in6_mreq_type ipv6_value_; 485 }; 486 487 // Helper template for implementing options that specify a network interface. 488 template <int IPv4_Level, int IPv4_Name, int IPv6_Level, int IPv6_Name> 489 class network_interface 490 { 491 public: 492 // Default constructor. network_interface()493 network_interface() 494 { 495 ipv4_value_.s_addr = 496 boost::asio::detail::socket_ops::host_to_network_long( 497 address_v4::any().to_uint()); 498 ipv6_value_ = 0; 499 } 500 501 // Construct with IPv4 interface. network_interface(const address_v4 & ipv4_interface)502 explicit network_interface(const address_v4& ipv4_interface) 503 { 504 ipv4_value_.s_addr = 505 boost::asio::detail::socket_ops::host_to_network_long( 506 ipv4_interface.to_uint()); 507 ipv6_value_ = 0; 508 } 509 510 // Construct with IPv6 interface. network_interface(unsigned int ipv6_interface)511 explicit network_interface(unsigned int ipv6_interface) 512 { 513 ipv4_value_.s_addr = 514 boost::asio::detail::socket_ops::host_to_network_long( 515 address_v4::any().to_uint()); 516 ipv6_value_ = ipv6_interface; 517 } 518 519 // Get the level of the socket option. 520 template <typename Protocol> level(const Protocol & protocol) const521 int level(const Protocol& protocol) const 522 { 523 if (protocol.family() == PF_INET6) 524 return IPv6_Level; 525 return IPv4_Level; 526 } 527 528 // Get the name of the socket option. 529 template <typename Protocol> name(const Protocol & protocol) const530 int name(const Protocol& protocol) const 531 { 532 if (protocol.family() == PF_INET6) 533 return IPv6_Name; 534 return IPv4_Name; 535 } 536 537 // Get the address of the option data. 538 template <typename Protocol> data(const Protocol & protocol) const539 const void* data(const Protocol& protocol) const 540 { 541 if (protocol.family() == PF_INET6) 542 return &ipv6_value_; 543 return &ipv4_value_; 544 } 545 546 // Get the size of the option data. 547 template <typename Protocol> size(const Protocol & protocol) const548 std::size_t size(const Protocol& protocol) const 549 { 550 if (protocol.family() == PF_INET6) 551 return sizeof(ipv6_value_); 552 return sizeof(ipv4_value_); 553 } 554 555 private: 556 boost::asio::detail::in4_addr_type ipv4_value_; 557 unsigned int ipv6_value_; 558 }; 559 560 } // namespace socket_option 561 } // namespace detail 562 } // namespace ip 563 } // namespace asio 564 } // namespace boost 565 566 #include <boost/asio/detail/pop_options.hpp> 567 568 #endif // BOOST_ASIO_IP_DETAIL_SOCKET_OPTION_HPP 569