Also use Objects as part of an operation but as a result don't generate Any operation...
[ACE_TAO.git] / ACE / ace / SOCK_Dgram_Mcast.cpp
blobcfaab1fcba2ca7b1b64ed53f0e1513f8471f8acc
1 #include "ace/SOCK_Dgram_Mcast.h"
3 #include "ace/OS_Memory.h"
4 #include "ace/OS_NS_string.h"
5 #include "ace/OS_NS_errno.h"
6 #include "ace/os_include/net/os_if.h"
7 #include "ace/os_include/arpa/os_inet.h"
8 #if defined (ACE_HAS_ALLOC_HOOKS)
9 # include "ace/Malloc_Base.h"
10 #endif /* ACE_HAS_ALLOC_HOOKS */
12 #if defined (ACE_LINUX) && defined (ACE_HAS_IPV6)
13 #include "ace/OS_NS_sys_socket.h"
14 #endif
16 #if defined (ACE_WIN32)
17 #include /**/ <iphlpapi.h>
18 #endif
20 #if defined (ACE_HAS_GETIFADDRS)
21 # if defined (ACE_VXWORKS)
22 # include /**/ <net/ifaddrs.h>
23 # else
24 # include /**/ <ifaddrs.h>
25 # endif /*ACE_VXWORKS */
26 #endif /* ACE_HAS_GETIFADDRS */
28 #if !defined (__ACE_INLINE__)
29 #include "ace/SOCK_Dgram_Mcast.inl"
30 #endif /* __ACE_INLINE__ */
32 #include "ace/Log_Category.h"
34 // This is a workaround for platforms with non-standard
35 // definitions of the ip_mreq structure
36 #if ! defined (IMR_MULTIADDR)
37 #define IMR_MULTIADDR imr_multiaddr
38 #endif /* ! defined (IMR_MULTIADDR) */
40 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
42 // Helper (inline) functions.
43 class ACE_SDM_helpers
45 public:
46 // Convert ACE_INET_Addr to string, using local formatting rules.
48 static void addr_to_string (const ACE_INET_Addr &ip_addr,
49 ACE_TCHAR *ret_string, // results here.
50 size_t len,
51 int clip_portnum) // clip port# info?
53 if (ip_addr.addr_to_string (ret_string, len, 1) == -1)
54 ACE_OS::strcpy (ret_string, ACE_TEXT ("<?>"));
55 else
57 ACE_TCHAR *pc = ACE_OS::strrchr (ret_string, ACE_TEXT (':'));
58 if (clip_portnum && pc)
59 *pc = ACE_TEXT ('\0'); // clip port# info.
62 // op== for ip_mreq structs.
63 static int is_equal (const ip_mreq &m1, const ip_mreq &m2)
65 return m1.IMR_MULTIADDR.s_addr == m2.IMR_MULTIADDR.s_addr
66 && m1.imr_interface.s_addr == m2.imr_interface.s_addr;
70 ACE_ALLOC_HOOK_DEFINE (ACE_SOCK_Dgram_Mcast)
72 void
73 ACE_SOCK_Dgram_Mcast::dump (void) const
75 #if defined (ACE_HAS_DUMP)
76 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::dump");
78 ACELIB_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this));
80 # if defined (ACE_SOCK_DGRAM_MCAST_DUMPABLE)
81 ACE_TCHAR addr_string[MAXNAMELEN + 1];
83 ACELIB_DEBUG ((LM_DEBUG,
84 ACE_TEXT ("\nOptions: bindaddr=%s, nulliface=%s\n"),
85 ACE_BIT_ENABLED (this->opts_, OPT_BINDADDR_YES) ?
86 ACE_TEXT ("<Bound>") : ACE_TEXT ("<Not Bound>"),
87 ACE_BIT_ENABLED (this->opts_, OPT_NULLIFACE_ALL) ?
88 ACE_TEXT ("<All Ifaces>") : ACE_TEXT ("<Default Iface>")));
90 // Show default send addr, port#, and interface.
91 ACE_SDM_helpers::addr_to_string (this->send_addr_, addr_string,
92 sizeof addr_string, 0);
93 ACELIB_DEBUG ((LM_DEBUG,
94 ACE_TEXT ("Send addr=%s iface=%s\n"),
95 addr_string,
96 this->send_net_if_ ? this->send_net_if_
97 : ACE_TEXT ("<default>")));
99 // Show list of subscribed addresses.
100 ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("Subscription list:\n")));
102 ACE_MT (ACE_GUARD (ACE_SDM_LOCK, guard, this->subscription_list_lock_));
103 subscription_list_iter_t iter (this->subscription_list_);
104 for ( ; !iter.done (); iter.advance ())
106 ACE_TCHAR iface_string[MAXNAMELEN + 1];
107 ip_mreq *pm = iter.next ();
109 // Get subscribed address (w/out port# info - not relevant).
110 ACE_INET_Addr ip_addr (static_cast<u_short> (0),
111 ACE_NTOHL (pm->IMR_MULTIADDR.s_addr));
112 ACE_SDM_helpers::addr_to_string (ip_addr, addr_string,
113 sizeof addr_string, 1);
115 // Get interface address/specification.
116 ACE_INET_Addr if_addr (static_cast<u_short> (0),
117 ACE_NTOHL (pm->imr_interface.s_addr));
118 ACE_SDM_helpers::addr_to_string (if_addr, iface_string,
119 sizeof iface_string, 1);
120 if (ACE_OS::strcmp (iface_string, ACE_TEXT ("0.0.0.0")) == 0)
121 // Receives on system default iface. (Note that null_iface_opt_
122 // option processing has already occurred.)
123 ACE_OS::strcpy (iface_string, ACE_TEXT ("<default>"));
125 // Dump info.
126 ACELIB_DEBUG ((LM_DEBUG,
127 ACE_TEXT ("\taddr=%s iface=%s\n"),
128 addr_string,
129 iface_string));
131 # endif /* ACE_SOCK_DGRAM_MCAST_DUMPABLE */
132 ACELIB_DEBUG ((LM_DEBUG, ACE_END_DUMP));
133 #endif /* ACE_HAS_DUMP */
136 // Constructor.
137 ACE_SOCK_Dgram_Mcast::ACE_SOCK_Dgram_Mcast
138 (ACE_SOCK_Dgram_Mcast::options opts)
139 : opts_ (opts),
140 send_net_if_ (0)
142 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::ACE_SOCK_Dgram_Mcast");
145 // Destructor.
146 ACE_SOCK_Dgram_Mcast::~ACE_SOCK_Dgram_Mcast (void)
148 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::~ACE_SOCK_Dgram_Mcast");
150 // Free memory and optionally unsubscribe from currently subscribed group(s).
151 #if defined (ACE_HAS_ALLOC_HOOKS)
152 ACE_Allocator::instance()->free(this->send_net_if_);
153 #else
154 delete [] this->send_net_if_;
155 #endif /* ACE_HAS_ALLOC_HOOKS */
156 this->clear_subs_list ();
160 ACE_SOCK_Dgram_Mcast::open (const ACE_INET_Addr &mcast_addr,
161 const ACE_TCHAR *net_if,
162 int reuse_addr)
164 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::open");
166 // Only perform the <open> initialization if we haven't been opened
167 // earlier.
168 // No sanity check? We should probably flag an error if the user
169 // makes multiple calls to open().
170 if (this->get_handle () != ACE_INVALID_HANDLE)
171 return 0;
173 // Invoke lower-layer ::open.
174 if (ACE_SOCK::open (SOCK_DGRAM,
175 mcast_addr.get_type (),
176 0, // always use 0
177 reuse_addr) == -1)
178 return -1;
180 return this->open_i (mcast_addr, net_if, reuse_addr);
184 ACE_SOCK_Dgram_Mcast::open_i (const ACE_INET_Addr &mcast_addr,
185 const ACE_TCHAR *net_if,
186 int reuse_addr)
188 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::open_i");
189 // ACE_SOCK::open calls this if reuse_addr is set, so we only need to
190 // process port reuse option.
191 if (reuse_addr)
193 #if defined (SO_REUSEPORT)
194 int one = 1;
195 if (this->ACE_SOCK::set_option (SOL_SOCKET,
196 SO_REUSEPORT,
197 &one,
198 sizeof one) == -1)
199 return -1;
200 #endif /* SO_REUSEPORT */
203 // Create an address/port# to bind the socket to. Use mcast_addr to
204 // initialize bind_addy to pick up the correct protocol family. If
205 // OPT_BINDADDR_YES is set, then we're done. Else use mcast_addr's
206 // port number and use the "any" address.
207 ACE_INET_Addr bind_addy (mcast_addr);
208 if (ACE_BIT_DISABLED (this->opts_, OPT_BINDADDR_YES))
210 #if defined (ACE_HAS_IPV6)
211 if (mcast_addr.get_type () == PF_INET6)
213 if (bind_addy.set (mcast_addr.get_port_number (), "::",
214 1, AF_INET6) == -1)
215 return -1;
217 else
218 // Bind to "any" address and explicit port#.
219 if (bind_addy.set (mcast_addr.get_port_number ()) == -1)
220 return -1;
221 #else
222 // Bind to "any" address and explicit port#.
223 if (bind_addy.set (mcast_addr.get_port_number ()) == -1)
224 return -1;
225 #endif /* ACE_HAS_IPV6 */
228 // Bind to the address (which may be INADDR_ANY) and port# (which may be 0)
229 if (ACE_SOCK_Dgram::shared_open (bind_addy, bind_addy.get_type ()) == -1)
230 return -1;
232 // Cache the actual bound address (which may be INADDR_ANY)
233 // and the actual bound port# (which will be a valid, non-zero port#).
234 ACE_INET_Addr bound_addy;
235 if (this->get_local_addr (bound_addy) == -1)
237 // (Unexpected failure - should be bound to something)
238 if (bound_addy.set (bind_addy) == -1)
240 // (Shouldn't happen - bind_addy is a valid addy; punt.)
241 return -1;
245 this->send_addr_ = mcast_addr;
246 this->send_addr_.set_port_number (bound_addy.get_port_number ());
247 if (net_if)
249 if (this->set_nic (net_if, mcast_addr.get_type ()))
250 return -1;
251 #if defined (ACE_HAS_ALLOC_HOOKS)
252 this->send_net_if_ = static_cast<ACE_TCHAR*>(ACE_Allocator::instance()->malloc(sizeof(ACE_TCHAR) * (ACE_OS::strlen (net_if) + 1)));
253 #else
254 this->send_net_if_ = new ACE_TCHAR[ACE_OS::strlen (net_if) + 1];
255 #endif /* ACE_HAS_ALLOC_HOOKS */
256 ACE_OS::strcpy (this->send_net_if_, net_if);
259 return 0;
263 ACE_SOCK_Dgram_Mcast::subscribe_ifs (const ACE_INET_Addr &mcast_addr,
264 const ACE_TCHAR *net_if,
265 int reuse_addr)
267 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::subscribe_ifs");
269 if (ACE_BIT_ENABLED (this->opts_, OPT_NULLIFACE_ALL)
270 && net_if == 0)
272 int family = mcast_addr.get_type ();
273 size_t nr_subscribed = 0;
275 #if defined (ACE_HAS_GETIFADDRS)
277 // Take advantage of the BSD getifaddrs function that simplifies
278 // access to connected interfaces.
279 struct ifaddrs *ifap = 0;
280 struct ifaddrs *p_if = 0;
282 if (::getifaddrs (&ifap) != 0)
283 return -1;
285 // Not every interface is for IP, and not all are up and multicast.
286 for (p_if = ifap;
287 p_if != 0;
288 p_if = p_if->ifa_next)
290 // Some OSes can return interfaces with no ifa_addr if the
291 // interface has no assigned address.
292 // If there is an address but it's not the family we want, ignore it.
293 if (p_if->ifa_addr == 0 || p_if->ifa_addr->sa_family != family)
294 continue;
296 // Check to see if it's up and supports multicast.
297 unsigned int wanted = IFF_UP | IFF_MULTICAST;
298 if ((p_if->ifa_flags & wanted) != wanted)
299 continue;
301 // Sometimes the kernel returns 0.0.0.0 as the interface
302 // address, skip those...
303 if (p_if->ifa_addr->sa_family == PF_INET)
305 struct sockaddr_in *addr =
306 reinterpret_cast<sockaddr_in *> (p_if->ifa_addr);
308 if (addr->sin_addr.s_addr == INADDR_ANY)
309 continue;
311 # if defined (ACE_HAS_IPV6)
312 else if (p_if->ifa_addr->sa_family == AF_INET6)
314 struct sockaddr_in6 *addr =
315 reinterpret_cast<sockaddr_in6 *> (p_if->ifa_addr);
317 // Skip the ANY address
318 if (IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr))
319 continue;
321 # endif /* ACE_HAS_IPV6 */
323 // Ok, now join on this interface.
324 if (this->join (mcast_addr,
325 reuse_addr,
326 ACE_TEXT_CHAR_TO_TCHAR(p_if->ifa_name)) == 0)
327 ++nr_subscribed;
330 ::freeifaddrs (ifap);
332 # elif defined (ACE_WIN32)
334 IP_ADAPTER_ADDRESSES tmp_addrs;
335 // Initial call to determine actual memory size needed
336 DWORD dwRetVal;
337 ULONG bufLen = 0;
338 // Note... GetAdaptersAddresses returns different bufLen values depending
339 // on how many multicast joins there are on the system. To avoid this,
340 // specify that we don't want to know about multicast addresses. This
341 // does not avoid multicastable interfaces and makes the size-check
342 // more reliable across varying conditions.
343 DWORD flags = GAA_FLAG_SKIP_MULTICAST;
344 if ((dwRetVal = ::GetAdaptersAddresses (family,
345 flags,
347 &tmp_addrs,
348 &bufLen)) != ERROR_BUFFER_OVERFLOW)
350 errno = dwRetVal;
351 return -1; // With output bufferlength 0 this can't be right.
354 // Get required output buffer and retrieve info for real.
355 PIP_ADAPTER_ADDRESSES pAddrs;
356 char *buf;
357 ACE_NEW_RETURN (buf,
358 char[bufLen],
359 -1);
360 pAddrs = reinterpret_cast<PIP_ADAPTER_ADDRESSES> (buf);
361 if ((dwRetVal = ::GetAdaptersAddresses (family,
362 flags,
364 pAddrs,
365 &bufLen)) != NO_ERROR)
367 delete[] buf; // clean up
368 errno = dwRetVal;
369 return -1;
372 for (; pAddrs; pAddrs = pAddrs->Next)
374 if (pAddrs->OperStatus != IfOperStatusUp)
375 continue;
377 // The ACE_SOCK_Dgram::make_multicast_ifaddr (IPv4), called by join(),
378 // can only deal with a dotted-decimal address, not an interface name.
379 if (family == AF_INET)
381 ACE_INET_Addr intf_addr ((sockaddr_in*)(pAddrs->FirstUnicastAddress->Address.lpSockaddr),
382 pAddrs->FirstUnicastAddress->Address.iSockaddrLength);
383 char intf_addr_str[INET_ADDRSTRLEN];
384 intf_addr.get_host_addr (intf_addr_str, sizeof (intf_addr_str));
385 if (this->join (mcast_addr, reuse_addr,
386 ACE_TEXT_CHAR_TO_TCHAR(intf_addr_str)) == 0)
387 ++nr_subscribed;
389 else
391 if (this->join (mcast_addr, reuse_addr,
392 ACE_TEXT_CHAR_TO_TCHAR(pAddrs->AdapterName)) == 0)
393 ++nr_subscribed;
397 delete[] buf; // clean up
399 # else
401 // Subscribe on all local multicast-capable network interfaces, by
402 // doing recursive calls with specific interfaces.
404 ACE_INET_Addr *if_addrs = 0;
405 size_t if_cnt;
407 if (ACE::get_ip_interfaces (if_cnt, if_addrs) != 0)
408 return -1;
410 if (if_cnt < 2)
412 if (this->join (mcast_addr,
413 reuse_addr,
414 ACE_TEXT ("0.0.0.0")) == 0)
415 ++nr_subscribed;
417 else
419 // Iterate through all the interfaces, figure out which ones
420 // offer multicast service, and subscribe to them.
421 while (if_cnt > 0)
423 --if_cnt;
425 // Convert to 0-based for indexing, next loop check.
426 if (if_addrs[if_cnt].get_type () != family || if_addrs[if_cnt].is_loopback ())
427 continue;
428 char addr_buf[INET6_ADDRSTRLEN];
429 if (this->join (mcast_addr,
430 reuse_addr,
431 ACE_TEXT_CHAR_TO_TCHAR
432 (if_addrs[if_cnt].get_host_addr (addr_buf, INET6_ADDRSTRLEN))) == 0)
433 ++nr_subscribed;
437 delete [] if_addrs;
439 # endif /* ACE_WIN32 */
441 if (nr_subscribed == 0)
443 errno = ENODEV;
444 return -1;
447 return 1;
450 // Subscribe on a specific interface, or on the default interface.
452 #if defined (ACE_HAS_IPV6)
453 if (mcast_addr.get_type () == AF_INET6)
455 if (this->make_multicast_ifaddr6 (0, mcast_addr, net_if) == -1)
456 return -1;
458 else
459 #endif /* ACE_HAS_IPV6 - Fall into IPv4-only case */
461 // Validate passed multicast addr and iface specifications.
462 if (this->make_multicast_ifaddr (0,
463 mcast_addr,
464 net_if) == -1)
465 return -1;
468 return 0;
473 ACE_SOCK_Dgram_Mcast::join (const ACE_INET_Addr &mcast_addr,
474 int reuse_addr,
475 const ACE_TCHAR *net_if)
477 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::join");
478 ACE_INET_Addr subscribe_addr = mcast_addr;
480 // If port# is 0, insert bound port# if it is set. (To satisfy lower-level
481 // port# validation.)
482 u_short def_port_number = this->send_addr_.get_port_number ();
483 if (subscribe_addr.get_port_number () == 0
484 && def_port_number != 0)
486 subscribe_addr.set_port_number (def_port_number);
489 // Check for port# different than bound port#.
490 u_short sub_port_number = mcast_addr.get_port_number ();
491 if (sub_port_number != 0
492 && def_port_number != 0
493 && sub_port_number != def_port_number)
495 ACELIB_ERROR ((LM_ERROR,
496 ACE_TEXT ("Subscribed port# (%u) different than bound ")
497 ACE_TEXT ("port# (%u).\n"),
498 (u_int) sub_port_number,
499 (u_int) def_port_number));
500 errno = ENXIO;
501 return -1;
504 // If bind_addr_opt_ is enabled, check for address different than
505 // bound address.
506 ACE_INET_Addr tmp_addr (this->send_addr_);
507 tmp_addr.set_port_number (mcast_addr.get_port_number ()); // force equal port numbers
508 if (ACE_BIT_ENABLED (this->opts_, OPT_BINDADDR_YES)
509 && !this->send_addr_.is_any ()
510 && this->send_addr_ != mcast_addr)
512 ACE_TCHAR sub_addr_string[MAXNAMELEN + 1];
513 ACE_TCHAR bound_addr_string[MAXNAMELEN + 1];
514 ACE_SDM_helpers::addr_to_string (mcast_addr, sub_addr_string,
515 sizeof sub_addr_string, 1);
516 ACE_SDM_helpers::addr_to_string (this->send_addr_, bound_addr_string,
517 sizeof bound_addr_string, 1);
518 ACELIB_ERROR ((LM_ERROR,
519 ACE_TEXT ("Subscribed address (%s) different than ")
520 ACE_TEXT ("bound address (%s).\n"),
521 sub_addr_string,
522 bound_addr_string));
523 errno = ENXIO;
524 return -1;
527 // Attempt subscription.
528 int result = this->subscribe_i (subscribe_addr, reuse_addr, net_if);
530 #if defined (ACE_SOCK_DGRAM_MCAST_DUMPABLE)
531 if (result == 0)
533 // Add this addr/iface info to the list of subscriptions.
534 // (Assumes this is unique addr/iface combo - most systems don't allow
535 // re-sub to same addr/iface.)
536 ip_mreq *pmreq = new ip_mreq;
537 // (should not fail)
538 if (this->make_multicast_ifaddr (pmreq, subscribe_addr, net_if) != -1)
540 ACE_MT (ACE_GUARD_RETURN (ACE_SDM_LOCK, guard,
541 this->subscription_list_lock_, -1));
542 this->subscription_list_.insert_tail (pmreq);
543 return 0;
545 // this still isn't really right. If ACE_GUARD_RETURN fails, we leak.
546 // Need to add one of Chris' fancy ace auto pointers (bound?).
547 delete pmreq;
549 #endif /* ACE_SOCK_DGRAM_MCAST_DUMPABLE */
551 return result >= 0 ? 0 : result;
554 // Attempt subscribe and return status.
556 ACE_SOCK_Dgram_Mcast::subscribe_i (const ACE_INET_Addr &mcast_addr,
557 int reuse_addr,
558 const ACE_TCHAR *net_if)
560 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::subscribe_i");
561 ip_mreq mreq;
562 #if defined (ACE_HAS_IPV6)
563 ipv6_mreq mreq6;
564 #endif /* ACE_LINUX && ACE_HAS_IPV6 */
566 // Open the socket IFF this is the first ::subscribe and ::open
567 // was not explicitly invoked.
568 if (this->open (mcast_addr,
569 net_if,
570 reuse_addr) == -1)
571 return -1;
573 // Only do this if net_if == 0, i.e., INADDR_ANY
574 if (net_if == 0)
576 int result = this->subscribe_ifs (mcast_addr,
577 net_if,
578 reuse_addr);
579 // Check for error or "short-circuit" return.
580 if (result != 0)
581 return result;
584 #if defined (ACE_HAS_IPV6)
585 if (mcast_addr.get_type () == AF_INET6)
587 if (this->make_multicast_ifaddr6 (&mreq6, mcast_addr, net_if) == -1)
588 return -1;
589 // Tell IP stack to pass messages sent to this group.
590 else if (this->ACE_SOCK::set_option (IPPROTO_IPV6,
591 IPV6_JOIN_GROUP,
592 &mreq6,
593 sizeof mreq6) == -1)
594 return -1;
596 return 0;
598 // Fall through for IPv4 case
599 #endif /* ACE_HAS_IPV6 */
601 // Create multicast addr/if struct.
602 if (this->make_multicast_ifaddr (&mreq, mcast_addr, net_if) == -1)
603 return -1;
604 // Tell IP stack to pass messages sent to this group.
605 else if (this->ACE_SOCK::set_option (IPPROTO_IP,
606 IP_ADD_MEMBERSHIP,
607 &mreq,
608 sizeof mreq) == -1)
609 return -1;
611 return 0;
615 ACE_SOCK_Dgram_Mcast::unsubscribe_ifs (const ACE_INET_Addr &mcast_addr,
616 const ACE_TCHAR *net_if)
618 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::unsubscribe_ifs");
621 if (ACE_BIT_ENABLED (this->opts_, OPT_NULLIFACE_ALL)
622 && net_if == 0)
624 #if defined (ACE_HAS_IPV6)
625 if (mcast_addr.get_type () == AF_INET6)
627 size_t nr_unsubscribed = 0;
628 # if defined(ACE_LINUX)
630 struct if_nameindex *intf;
632 intf = ACE_OS::if_nameindex ();
634 if (intf == 0)
635 return -1;
637 int index = 0;
638 while (intf[index].if_index != 0 || intf[index].if_name != 0)
640 if (this->leave (mcast_addr, ACE_TEXT_CHAR_TO_TCHAR(intf[index].if_name)) == 0)
641 ++nr_unsubscribed;
643 ++index;
646 ACE_OS::if_freenameindex (intf);
648 # elif defined (ACE_WIN32)
650 IP_ADAPTER_ADDRESSES tmp_addrs;
651 // Initial call to determine actual memory size needed
652 DWORD dwRetVal;
653 ULONG bufLen = 0;
654 if ((dwRetVal = ::GetAdaptersAddresses (AF_INET6,
657 &tmp_addrs,
658 &bufLen)) != ERROR_BUFFER_OVERFLOW)
659 return -1; // With output bufferlength 0 this can't be right.
661 // Get required output buffer and retrieve info for real.
662 PIP_ADAPTER_ADDRESSES pAddrs;
663 char *buf;
664 ACE_NEW_RETURN (buf,
665 char[bufLen],
666 -1);
667 pAddrs = reinterpret_cast<PIP_ADAPTER_ADDRESSES> (buf);
668 if ((dwRetVal = ::GetAdaptersAddresses (AF_INET6,
671 pAddrs,
672 &bufLen)) != NO_ERROR)
674 delete[] buf; // clean up
675 return -1;
678 while (pAddrs)
680 if (this->leave (mcast_addr, ACE_TEXT_CHAR_TO_TCHAR(pAddrs->AdapterName)) == 0)
681 ++nr_unsubscribed;
683 pAddrs = pAddrs->Next;
686 delete[] buf; // clean up
688 # endif /* ACE_WIN32 */
690 if (nr_unsubscribed == 0)
692 errno = ENODEV;
693 return -1;
696 return 1;
700 else
702 // Unsubscribe on all local multicast-capable network interfaces, by
703 // doing recursive calls with specific interfaces.
705 ACE_INET_Addr *if_addrs = 0;
706 size_t if_cnt;
708 // NOTE - <get_ip_interfaces> doesn't always get all of the
709 // interfaces. In particular, it may not get a PPP interface. This
710 // is a limitation of the way <get_ip_interfaces> works with
711 // old versions of MSVC. The reliable way of getting the interface
712 // list is available only with MSVC 5 and newer.
713 if (ACE::get_ip_interfaces (if_cnt, if_addrs) != 0)
714 return -1;
716 size_t nr_unsubscribed = 0;
718 if (if_cnt < 2)
720 if (this->leave (mcast_addr,
721 ACE_TEXT ("0.0.0.0")) == 0)
722 ++nr_unsubscribed;
724 else
726 while (if_cnt > 0)
728 --if_cnt;
729 // Convert to 0-based for indexing, next loop check
730 if (if_addrs[if_cnt].get_type () != AF_INET || if_addrs[if_cnt].is_loopback ())
731 continue;
732 char addr_buf[INET6_ADDRSTRLEN];
733 if (this->leave (mcast_addr,
734 ACE_TEXT_CHAR_TO_TCHAR
735 (if_addrs[if_cnt].get_host_addr (addr_buf, INET6_ADDRSTRLEN))) == 0)
736 ++nr_unsubscribed;
740 delete [] if_addrs;
742 if (nr_unsubscribed == 0)
744 errno = ENODEV;
745 return -1;
748 return 1;
751 #else /* ACE_HAS_IPV6 */
752 // Unsubscribe on all local multicast-capable network interfaces, by
753 // doing recursive calls with specific interfaces.
755 ACE_INET_Addr *if_addrs = 0;
756 size_t if_cnt;
758 // NOTE - <get_ip_interfaces> doesn't always get all of the
759 // interfaces. In particular, it may not get a PPP interface. This
760 // is a limitation of the way <get_ip_interfaces> works with
761 // old versions of MSVC. The reliable way of getting the interface list
762 // is available only with MSVC 5 and newer.
763 if (ACE::get_ip_interfaces (if_cnt, if_addrs) != 0)
764 return -1;
766 size_t nr_unsubscribed = 0;
768 if (if_cnt < 2)
770 if (this->leave (mcast_addr,
771 ACE_TEXT ("0.0.0.0")) == 0)
772 ++nr_unsubscribed;
774 else
776 while (if_cnt > 0)
778 --if_cnt;
779 // Convert to 0-based for indexing, next loop check
780 if (if_addrs[if_cnt].is_loopback ())
781 continue;
782 char addr_buf[INET6_ADDRSTRLEN];
783 if (this->leave (mcast_addr,
784 ACE_TEXT_CHAR_TO_TCHAR
785 (if_addrs[if_cnt].get_host_addr (addr_buf, INET6_ADDRSTRLEN))) == 0)
786 ++nr_unsubscribed;
790 delete [] if_addrs;
792 if (nr_unsubscribed == 0)
794 errno = ENODEV;
795 return -1;
798 return 1;
799 #endif /* !ACE_HAS_IPV6 */
802 return 0;
807 ACE_SOCK_Dgram_Mcast::leave (const ACE_INET_Addr &mcast_addr,
808 const ACE_TCHAR *net_if)
810 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::leave");
811 // Unsubscribe.
812 int result = this->unsubscribe_i (mcast_addr,
813 net_if);
815 #if defined (ACE_SOCK_DGRAM_MCAST_DUMPABLE)
816 // (Unconditionally) Remove this addr/if from subscription list.
817 // (Addr/if is removed even if unsubscribe failed)
818 ip_mreq tgt_mreq;
819 if (this->make_multicast_ifaddr (&tgt_mreq,
820 mcast_addr,
821 net_if) != -1)
823 ACE_MT (ACE_GUARD_RETURN (ACE_SDM_LOCK, guard,
824 this->subscription_list_lock_, -1));
825 subscription_list_iter_t iter (this->subscription_list_);
826 for (; !iter.done (); iter.advance ())
828 ip_mreq *pm = iter.next ();
829 if (ACE_SDM_helpers::is_equal (*pm, tgt_mreq))
831 iter.remove ();
832 delete pm;
833 break;
837 #endif /* ACE_SOCK_DGRAM_MCAST_DUMPABLE */
839 return result >= 0 ? 0 : result;
842 // Attempt unsubscribe and return status.
844 ACE_SOCK_Dgram_Mcast::unsubscribe_i (const ACE_INET_Addr &mcast_addr,
845 const ACE_TCHAR *net_if)
847 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::unsubscribe_i");
849 int result = this->unsubscribe_ifs (mcast_addr,
850 net_if);
852 // Check for error or "short-circuit" return.
853 if (result != 0)
854 return result;
856 #if defined (ACE_HAS_IPV6)
857 if (mcast_addr.get_type () == AF_INET6)
859 // Validate addr/if specifications and create addr/if struct.
860 ipv6_mreq mreq;
861 if (this->make_multicast_ifaddr6 (&mreq, mcast_addr, net_if) == -1)
863 return -1;
865 // Tell network device driver to stop reading datagrams with the
866 // <mcast_addr>.
867 else if (ACE_SOCK::set_option (IPPROTO_IPV6,
868 IPV6_LEAVE_GROUP,
869 &mreq,
870 sizeof mreq) == -1)
872 return -1;
876 else // IPv4
878 // Validate addr/if specifications and create addr/if struct.
879 ip_mreq mreq;
880 if (this->make_multicast_ifaddr (&mreq, mcast_addr, net_if) == -1)
882 return -1;
884 // Tell network device driver to stop reading datagrams with the
885 // <mcast_addr>.
886 else if (ACE_SOCK::set_option (IPPROTO_IP,
887 IP_DROP_MEMBERSHIP,
888 &mreq,
889 sizeof mreq) == -1)
891 return -1;
895 #else
896 // Validate addr/if specifications and create addr/if struct.
897 ip_mreq mreq;
898 if (this->make_multicast_ifaddr (&mreq, mcast_addr, net_if) == -1)
900 return -1;
902 // Tell network device driver to stop reading datagrams with the
903 // <mcast_addr>.
904 // Note, this is not IPv6 friendly...
905 else if (ACE_SOCK::set_option (IPPROTO_IP,
906 IP_DROP_MEMBERSHIP,
907 &mreq,
908 sizeof mreq) == -1)
910 return -1;
912 #endif /* ACE_HAS_IPV6 */
914 return 0;
918 ACE_SOCK_Dgram_Mcast::clear_subs_list (void)
920 ACE_TRACE ("ACE_SOCK_Dgram_Mcast::clear_subs_list");
921 int result = 0;
923 #if defined (ACE_SOCK_DGRAM_MCAST_DUMPABLE)
924 ACE_MT (ACE_GUARD_RETURN (ACE_SDM_LOCK, guard,
925 this->subscription_list_lock_, -1));
926 subscription_list_iter_t iter (this->subscription_list_);
927 for (; !iter.done (); /*Hack: Do _not_ ::advance after remove*/)
929 ip_mreq *pm = iter.next ();
930 iter.remove ();
931 delete pm;
933 #endif /* ACE_SOCK_DGRAM_MCAST_DUMPABLE */
934 return result;
937 ACE_END_VERSIONED_NAMESPACE_DECL