1 //
2 // detail/socket_option.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2019 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