1 | // |
2 | // MulticastSocket.cpp |
3 | // |
4 | // Library: Net |
5 | // Package: Sockets |
6 | // Module: MulticastSocket |
7 | // |
8 | // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/Net/MulticastSocket.h" |
16 | |
17 | |
18 | #ifdef POCO_NET_HAS_INTERFACE |
19 | |
20 | |
21 | #include "Poco/Net/NetException.h" |
22 | #include <cstring> |
23 | |
24 | |
25 | #if defined(hpux) && defined(_XOPEN_SOURCE_EXTENDED) && defined(POCO_HPUX_IP_MREQ_HACK) |
26 | // netinet/in.h does not define struct ip_mreq if |
27 | // _XOPEN_SOURCE_EXTENDED is #define'd in HP-UX 11.x |
28 | // versions prior to 11.30. Compile with -DPOCO_HPUX_IP_MREQ_HACK |
29 | // if you experience problems. |
30 | struct ip_mreq |
31 | { |
32 | struct in_addr imr_multiaddr; |
33 | struct in_addr imr_interface; |
34 | }; |
35 | #endif |
36 | |
37 | |
38 | // some Unix variants don't have IPV6_ADD_MEMBERSHIP/IPV6_DROP_MEMBERSHIP |
39 | #if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP) |
40 | #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP |
41 | #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP |
42 | #endif |
43 | |
44 | |
45 | namespace Poco { |
46 | namespace Net { |
47 | |
48 | |
49 | MulticastSocket::MulticastSocket() |
50 | { |
51 | } |
52 | |
53 | |
54 | MulticastSocket::MulticastSocket(SocketAddress::Family family): DatagramSocket(family) |
55 | { |
56 | #if defined(POCO_OS_FAMILY_UNIX) |
57 | if (family == SocketAddress::UNIX_LOCAL) |
58 | throw Poco::InvalidArgumentException("Cannot create a MulticastSocket with UNIX_LOCAL socket" ); |
59 | #endif |
60 | } |
61 | |
62 | |
63 | MulticastSocket::MulticastSocket(const SocketAddress& address, bool reuseAddress): DatagramSocket(address, reuseAddress) |
64 | { |
65 | } |
66 | |
67 | |
68 | MulticastSocket::MulticastSocket(const Socket& socket): DatagramSocket(socket) |
69 | { |
70 | } |
71 | |
72 | |
73 | MulticastSocket::~MulticastSocket() |
74 | { |
75 | } |
76 | |
77 | |
78 | MulticastSocket& MulticastSocket::operator = (const Socket& socket) |
79 | { |
80 | DatagramSocket::operator = (socket); |
81 | return *this; |
82 | } |
83 | |
84 | |
85 | void MulticastSocket::setInterface(const NetworkInterface& interfc) |
86 | { |
87 | if (address().family() == SocketAddress::IPv4) |
88 | { |
89 | impl()->setOption(IPPROTO_IP, IP_MULTICAST_IF, interfc.firstAddress(IPAddress::IPv4)); |
90 | } |
91 | #if defined(POCO_HAVE_IPv6) |
92 | else if (address().family() == SocketAddress::IPv6) |
93 | { |
94 | impl()->setOption(IPPROTO_IPV6, IPV6_MULTICAST_IF, interfc.index()); |
95 | } |
96 | #endif |
97 | else throw UnsupportedFamilyException("Unknown or unsupported socket family." ); |
98 | } |
99 | |
100 | |
101 | NetworkInterface MulticastSocket::getInterface() const |
102 | { |
103 | try |
104 | { |
105 | IPAddress addr; |
106 | impl()->getOption(IPPROTO_IP, IP_MULTICAST_IF, addr); |
107 | return NetworkInterface::forAddress(addr); |
108 | } |
109 | catch (Poco::Exception&) |
110 | { |
111 | #if defined(POCO_HAVE_IPv6) |
112 | int ix; |
113 | impl()->getOption(IPPROTO_IPV6, IPV6_MULTICAST_IF, ix); |
114 | return NetworkInterface::forIndex(ix); |
115 | #else |
116 | throw; |
117 | #endif |
118 | } |
119 | } |
120 | |
121 | |
122 | void MulticastSocket::setLoopback(bool flag) |
123 | { |
124 | if (address().af() == AF_INET) |
125 | { |
126 | unsigned char uflag = flag ? 1 : 0; |
127 | impl()->setOption(IPPROTO_IP, IP_MULTICAST_LOOP, uflag); |
128 | } |
129 | else |
130 | { |
131 | #if defined(POCO_HAVE_IPv6) |
132 | unsigned uflag = flag ? 1 : 0; |
133 | impl()->setOption(IPPROTO_IPV6, IPV6_MULTICAST_LOOP, uflag); |
134 | #endif |
135 | } |
136 | } |
137 | |
138 | |
139 | bool MulticastSocket::getLoopback() const |
140 | { |
141 | bool flag = false; |
142 | if (address().af() == AF_INET) |
143 | { |
144 | unsigned char uflag; |
145 | impl()->getOption(IPPROTO_IP, IP_MULTICAST_LOOP, uflag); |
146 | flag = uflag != 0; |
147 | } |
148 | else |
149 | { |
150 | #if defined(POCO_HAVE_IPv6) |
151 | unsigned uflag; |
152 | impl()->getOption(IPPROTO_IPV6, IPV6_MULTICAST_LOOP, uflag); |
153 | flag = uflag != 0; |
154 | #endif |
155 | } |
156 | return flag; |
157 | } |
158 | |
159 | |
160 | void MulticastSocket::setTimeToLive(unsigned value) |
161 | { |
162 | if (address().af() == AF_INET) |
163 | { |
164 | unsigned char ttl = (unsigned char) value; |
165 | impl()->setOption(IPPROTO_IP, IP_MULTICAST_TTL, ttl); |
166 | } |
167 | else |
168 | { |
169 | #if defined(POCO_HAVE_IPv6) |
170 | impl()->setOption(IPPROTO_IPV6, IPV6_MULTICAST_HOPS, value); |
171 | #endif |
172 | } |
173 | } |
174 | |
175 | |
176 | unsigned MulticastSocket::getTimeToLive() const |
177 | { |
178 | unsigned ttl(0); |
179 | if (address().af() == AF_INET) |
180 | { |
181 | unsigned char cttl; |
182 | impl()->getOption(IPPROTO_IP, IP_MULTICAST_TTL, cttl); |
183 | ttl = cttl; |
184 | } |
185 | else |
186 | { |
187 | #if defined(POCO_HAVE_IPv6) |
188 | impl()->getOption(IPPROTO_IPV6, IPV6_MULTICAST_HOPS, ttl); |
189 | #endif |
190 | } |
191 | return ttl; |
192 | } |
193 | |
194 | |
195 | void MulticastSocket::joinGroup(const IPAddress& groupAddress) |
196 | { |
197 | joinGroup(groupAddress, findFirstInterface(groupAddress)); |
198 | } |
199 | |
200 | |
201 | void MulticastSocket::joinGroup(const IPAddress& groupAddress, const NetworkInterface& interfc) |
202 | { |
203 | if (groupAddress.af() == AF_INET) |
204 | { |
205 | struct ip_mreq mr; |
206 | std::memcpy(&mr.imr_multiaddr, groupAddress.addr(), groupAddress.length()); |
207 | std::memcpy(&mr.imr_interface, interfc.firstAddress(IPAddress::IPv4).addr(), interfc.firstAddress(IPAddress::IPv4).length()); |
208 | impl()->setRawOption(IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr, sizeof(mr)); |
209 | } |
210 | else |
211 | { |
212 | #if defined(POCO_HAVE_IPv6) |
213 | struct ipv6_mreq mr; |
214 | std::memcpy(&mr.ipv6mr_multiaddr, groupAddress.addr(), groupAddress.length()); |
215 | mr.ipv6mr_interface = interfc.index(); |
216 | impl()->setRawOption(IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(mr)); |
217 | #endif |
218 | } |
219 | } |
220 | |
221 | |
222 | NetworkInterface MulticastSocket::findFirstInterface(const IPAddress& groupAddress) |
223 | { |
224 | NetworkInterface::Map m = NetworkInterface::map(); |
225 | if (groupAddress.family() == IPAddress::IPv4) |
226 | { |
227 | for (NetworkInterface::Map::const_iterator it = m.begin(); it != m.end(); ++it) |
228 | { |
229 | if (it->second.supportsIPv4() && |
230 | it->second.firstAddress(IPAddress::IPv4).isUnicast() && |
231 | !it->second.isLoopback() && |
232 | !it->second.isPointToPoint()) |
233 | { |
234 | return it->second; |
235 | } |
236 | } |
237 | } |
238 | #ifdef POCO_HAVE_IPv6 |
239 | else if (groupAddress.family() == IPAddress::IPv6) |
240 | { |
241 | for (NetworkInterface::Map::const_iterator it = m.begin(); it != m.end(); ++it) |
242 | { |
243 | if (it->second.supportsIPv6() && |
244 | it->second.firstAddress(IPAddress::IPv6).isUnicast() && |
245 | !it->second.isLoopback() && |
246 | !it->second.isPointToPoint()) |
247 | { |
248 | return it->second; |
249 | } |
250 | } |
251 | } |
252 | #endif // POCO_HAVE_IPv6 |
253 | |
254 | throw NotFoundException("No multicast-eligible network interface found." ); |
255 | } |
256 | |
257 | |
258 | void MulticastSocket::leaveGroup(const IPAddress& groupAddress) |
259 | { |
260 | NetworkInterface intf; |
261 | leaveGroup(groupAddress, intf); |
262 | } |
263 | |
264 | |
265 | void MulticastSocket::leaveGroup(const IPAddress& groupAddress, const NetworkInterface& interfc) |
266 | { |
267 | if (groupAddress.af() == AF_INET) |
268 | { |
269 | struct ip_mreq mr; |
270 | std::memcpy(&mr.imr_multiaddr, groupAddress.addr(), groupAddress.length()); |
271 | std::memcpy(&mr.imr_interface, interfc.firstAddress(IPAddress::IPv4).addr(), interfc.firstAddress(IPAddress::IPv4).length()); |
272 | impl()->setRawOption(IPPROTO_IP, IP_DROP_MEMBERSHIP, &mr, sizeof(mr)); |
273 | } |
274 | else |
275 | { |
276 | #if defined(POCO_HAVE_IPv6) |
277 | struct ipv6_mreq mr; |
278 | std::memcpy(&mr.ipv6mr_multiaddr, groupAddress.addr(), groupAddress.length()); |
279 | mr.ipv6mr_interface = interfc.index(); |
280 | impl()->setRawOption(IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mr, sizeof(mr)); |
281 | #endif |
282 | } |
283 | } |
284 | |
285 | |
286 | } } // namespace Poco::Net |
287 | |
288 | |
289 | #endif // POCO_NET_HAS_INTERFACE |
290 | |