@@ -84,6 +84,11 @@ namespace asyncpp::io::detail {
8484 endpoint socket_local_endpoint (socket_handle_t socket) override ;
8585 endpoint socket_remote_endpoint (socket_handle_t socket) override ;
8686 void socket_enable_broadcast (socket_handle_t socket, bool enable) override ;
87+ void socket_multicast_join (socket_handle_t socket, address group, address iface) override ;
88+ void socket_multicast_drop (socket_handle_t socket, address group, address iface) override ;
89+ void socket_multicast_set_send_interface (socket_handle_t socket, address iface) override ;
90+ void socket_multicast_set_ttl (socket_handle_t socket, size_t ttl) override ;
91+ void socket_multicast_set_loopback (socket_handle_t socket, bool enabled) override ;
8792 void socket_shutdown (socket_handle_t socket, bool receive, bool send) override ;
8893 bool enqueue_connect (socket_handle_t socket, endpoint ep, completion_data* cd) override ;
8994 bool enqueue_accept (socket_handle_t socket, completion_data* cd) override ;
@@ -109,6 +114,8 @@ namespace asyncpp::io::detail {
109114 private:
110115 HANDLE m_completion_port = INVALID_HANDLE_VALUE;
111116 std::atomic<size_t > m_inflight_count{};
117+
118+ address_type get_handle_type (socket_handle_t socket);
112119 };
113120
114121 std::unique_ptr<io_engine> create_io_engine_iocp () { return std::make_unique<io_engine_iocp>(); }
@@ -345,6 +352,106 @@ namespace asyncpp::io::detail {
345352 if (res == SOCKET_ERROR) throw std::system_error (WSAGetLastError (), std::system_category ());
346353 }
347354
355+ void io_engine_iocp::socket_multicast_join (socket_handle_t socket, address group, address iface) {
356+ if (group.type () != iface.type ())
357+ throw std::system_error (std::make_error_code (std::errc::invalid_argument),
358+ " group and interface need to be of the same type" );
359+ if (group.is_ipv4 ()) {
360+ struct ip_mreq mc_req {};
361+ mc_req.imr_multiaddr = group.ipv4 ().to_sockaddr_in ().first .sin_addr ;
362+ mc_req.imr_interface = iface.ipv4 ().to_sockaddr_in ().first .sin_addr ;
363+ auto res = setsockopt (socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast <const char *>(&mc_req),
364+ sizeof (mc_req));
365+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
366+ } else if (group.is_ipv6 ()) {
367+ struct ipv6_mreq mc_req {};
368+ mc_req.ipv6mr_multiaddr = group.ipv6 ().to_sockaddr_in6 ().first .sin6_addr ;
369+ mc_req.ipv6mr_interface = iface.ipv6 ().to_sockaddr_in6 ().first .sin6_scope_id ;
370+ auto res = setsockopt (socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, reinterpret_cast <const char *>(&mc_req),
371+ sizeof (mc_req));
372+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
373+ } else {
374+ throw std::system_error (std::make_error_code (std::errc::not_supported),
375+ " multicast is only supported on IPv4/IPv6" );
376+ }
377+ }
378+
379+ void io_engine_iocp::socket_multicast_drop (socket_handle_t socket, address group, address iface) {
380+ if (group.type () != iface.type ())
381+ throw std::system_error (std::make_error_code (std::errc::invalid_argument),
382+ " group and interface need to be of the same type" );
383+ if (group.is_ipv4 ()) {
384+ struct ip_mreq mc_req {};
385+ mc_req.imr_multiaddr = group.ipv4 ().to_sockaddr_in ().first .sin_addr ;
386+ mc_req.imr_interface = iface.ipv4 ().to_sockaddr_in ().first .sin_addr ;
387+ auto res = setsockopt (socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, reinterpret_cast <const char *>(&mc_req),
388+ sizeof (mc_req));
389+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
390+ } else if (group.is_ipv6 ()) {
391+ struct ipv6_mreq mc_req {};
392+ mc_req.ipv6mr_multiaddr = group.ipv6 ().to_sockaddr_in6 ().first .sin6_addr ;
393+ mc_req.ipv6mr_interface = iface.ipv6 ().to_sockaddr_in6 ().first .sin6_scope_id ;
394+ auto res = setsockopt (socket, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, reinterpret_cast <const char *>(&mc_req),
395+ sizeof (mc_req));
396+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
397+ } else {
398+ throw std::system_error (std::make_error_code (std::errc::not_supported),
399+ " multicast is only supported on IPv4/IPv6" );
400+ }
401+ }
402+
403+ void io_engine_iocp::socket_multicast_set_send_interface (socket_handle_t socket, address iface) {
404+ if (iface.is_ipv4 ()) {
405+ auto addr = iface.ipv4 ().to_sockaddr_in ().first .sin_addr .s_addr ;
406+ auto res =
407+ setsockopt (socket, IPPROTO_IP, IP_MULTICAST_IF, reinterpret_cast <const char *>(&addr), sizeof (addr));
408+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
409+ } else if (iface.is_ipv6 ()) {
410+ auto scope = iface.ipv6 ().to_sockaddr_in6 ().first .sin6_scope_id ;
411+ auto res = setsockopt (socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, reinterpret_cast <const char *>(&scope),
412+ sizeof (scope));
413+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
414+ } else {
415+ throw std::system_error (std::make_error_code (std::errc::not_supported),
416+ " multicast is only supported on IPv4/IPv6" );
417+ }
418+ }
419+
420+ void io_engine_iocp::socket_multicast_set_ttl (socket_handle_t socket, size_t ttl) {
421+ auto type = get_handle_type (socket);
422+ if (ttl > (std::numeric_limits<int >::max)()) throw std::invalid_argument (" ttl value out of range" );
423+ int ittl = ttl;
424+ if (type == address_type::ipv4) {
425+ auto res =
426+ setsockopt (socket, IPPROTO_IP, IP_MULTICAST_TTL, reinterpret_cast <const char *>(&ittl), sizeof (ittl));
427+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
428+ } else if (type == address_type::ipv6) {
429+ auto res = setsockopt (socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, reinterpret_cast <const char *>(&ittl),
430+ sizeof (ittl));
431+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
432+ } else {
433+ throw std::system_error (std::make_error_code (std::errc::not_supported),
434+ " multicast is only supported on IPv4/IPv6" );
435+ }
436+ }
437+
438+ void io_engine_iocp::socket_multicast_set_loopback (socket_handle_t socket, bool enabled) {
439+ auto type = get_handle_type (socket);
440+ int val = enabled ? 1 : 0 ;
441+ if (type == address_type::ipv4) {
442+ auto res =
443+ setsockopt (socket, IPPROTO_IP, IP_MULTICAST_LOOP, reinterpret_cast <const char *>(&val), sizeof (val));
444+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
445+ } else if (type == address_type::ipv6) {
446+ auto res =
447+ setsockopt (socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, reinterpret_cast <const char *>(&val), sizeof (val));
448+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
449+ } else {
450+ throw std::system_error (std::make_error_code (std::errc::not_supported),
451+ " multicast is only supported on IPv4/IPv6" );
452+ }
453+ }
454+
348455 void io_engine_iocp::socket_shutdown (socket_handle_t socket, bool receive, bool send) {
349456 int mode = 0 ;
350457 if (receive && send)
@@ -532,6 +639,18 @@ namespace asyncpp::io::detail {
532639 }
533640 }
534641
642+ address_type io_engine_iocp::get_handle_type (socket_handle_t socket) {
643+ WSAPROTOCOL_INFO info{};
644+ socklen_t length = sizeof (info);
645+ auto res = getsockopt (socket, SOL_SOCKET, SO_PROTOCOL_INFO, reinterpret_cast <char *>(&info), &length);
646+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " getsockopt failed" );
647+ switch (info.iAddressFamily ) {
648+ case AF_INET: return address_type::ipv4;
649+ case AF_INET6: return address_type::ipv6;
650+ default : throw std::logic_error (" unknown socket type" );
651+ }
652+ }
653+
535654 io_engine::file_handle_t io_engine_iocp::file_open (const char * filename, std::ios_base::openmode mode) {
536655 DWORD access_mode = 0 ;
537656 if ((mode & std::ios_base::in) == std::ios_base::in) access_mode |= GENERIC_READ;
0 commit comments