@@ -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 interface) override ;
88+ void socket_multicast_drop (socket_handle_t socket, address group, address interface) override ;
89+ void socket_multicast_set_send_interface (socket_handle_t socket, address interface) 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>(); }
@@ -245,7 +252,7 @@ namespace asyncpp::io::detail {
245252 if (setsockopt (listener, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, (socklen_t )sizeof (reuse)) == -1 )
246253 close_and_throw (" setsockopt" , listener);
247254
248- struct sockaddr_in inaddr {};
255+ struct sockaddr_in inaddr{};
249256 inaddr.sin_family = AF_INET;
250257 inaddr.sin_addr .s_addr = htonl (INADDR_LOOPBACK);
251258 if (bind (listener, reinterpret_cast <sockaddr*>(&inaddr), sizeof (inaddr)) == SOCKET_ERROR)
@@ -345,6 +352,99 @@ 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 interface) {
356+ if (group.type () != interface.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 = interface.ipv4 ().to_sockaddr_in ().first .sin_addr ;
363+ auto res = setsockopt (socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mc_req, sizeof (mc_req));
364+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
365+ } else if (group.is_ipv6 ()) {
366+ struct ipv6_mreq mc_req{};
367+ mc_req.ipv6mr_multiaddr = group.ipv6 ().to_sockaddr_in6 ().first .sin6_addr ;
368+ mc_req.ipv6mr_interface = interface.ipv6 ().to_sockaddr_in6 ().first .sin6_scope_id ;
369+ auto res = setsockopt (socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mc_req, sizeof (mc_req));
370+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
371+ } else {
372+ throw std::system_error (std::make_error_code (std::errc::not_supported),
373+ " multicast is only supported on IPv4/IPv6" );
374+ }
375+ }
376+
377+ void io_engine_iocp::socket_multicast_drop (socket_handle_t socket, address group, address interface) {
378+ if (group.type () != interface.type ())
379+ throw std::system_error (std::make_error_code (std::errc::invalid_argument),
380+ " group and interface need to be of the same type" );
381+ if (group.is_ipv4 ()) {
382+ struct ip_mreq mc_req{};
383+ mc_req.imr_multiaddr = group.ipv4 ().to_sockaddr_in ().first .sin_addr ;
384+ mc_req.imr_interface = interface.ipv4 ().to_sockaddr_in ().first .sin_addr ;
385+ auto res = setsockopt (socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mc_req, sizeof (mc_req));
386+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
387+ } else if (group.is_ipv6 ()) {
388+ struct ipv6_mreq mc_req{};
389+ mc_req.ipv6mr_multiaddr = group.ipv6 ().to_sockaddr_in6 ().first .sin6_addr ;
390+ mc_req.ipv6mr_interface = interface.ipv6 ().to_sockaddr_in6 ().first .sin6_scope_id ;
391+ auto res = setsockopt (socket, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mc_req, sizeof (mc_req));
392+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
393+ } else {
394+ throw std::system_error (std::make_error_code (std::errc::not_supported),
395+ " multicast is only supported on IPv4/IPv6" );
396+ }
397+ }
398+
399+ void io_engine_iocp::socket_multicast_set_send_interface (socket_handle_t socket, address interface) {
400+ if (interface.is_ipv4 ()) {
401+ auto addr = interface.ipv4 ().to_sockaddr_in ().first .sin_addr .s_addr ;
402+ auto res = setsockopt (socket, IPPROTO_IP, IP_MULTICAST_IF, reinterpret_cast <char *>(&addr), sizeof (addr));
403+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
404+ } else if (interface.is_ipv6 ()) {
405+ auto scope = interface.ipv6 ().to_sockaddr_in6 ().first .sin6_scope_id ;
406+ auto res =
407+ setsockopt (socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, reinterpret_cast <char *>(&scope), sizeof (scope));
408+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
409+ } else {
410+ throw std::system_error (std::make_error_code (std::errc::not_supported),
411+ " multicast is only supported on IPv4/IPv6" );
412+ }
413+ }
414+
415+ void io_engine_iocp::socket_multicast_set_ttl (socket_handle_t socket, size_t ttl) {
416+ auto type = get_handle_type (socket);
417+ if (ttl > std::numeric_limits<int >::max ()) throw std::invalid_argument (" ttl value out of range" );
418+ int ittl = ttl;
419+ if (type == address_type::ipv4) {
420+ auto res = setsockopt (socket, IPPROTO_IP, IP_MULTICAST_TTL, reinterpret_cast <char *>(&ittl), sizeof (ittl));
421+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
422+ } else if (type == address_type::ipv6) {
423+ auto res =
424+ setsockopt (socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, reinterpret_cast <char *>(&ittl), sizeof (ittl));
425+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
426+ } else {
427+ throw std::system_error (std::make_error_code (std::errc::not_supported),
428+ " multicast is only supported on IPv4/IPv6" );
429+ }
430+ }
431+
432+ void io_engine_iocp::socket_multicast_set_loopback (socket_handle_t socket, bool enabled) {
433+ auto type = get_handle_type (socket);
434+ int val = enabled ? 1 : 0 ;
435+ if (type == address_type::ipv4) {
436+ auto res = setsockopt (socket, IPPROTO_IP, IP_MULTICAST_LOOP, reinterpret_cast <char *>(&val), sizeof (val));
437+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
438+ } else if (type == address_type::ipv6) {
439+ auto res =
440+ setsockopt (socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, reinterpret_cast <char *>(&val), sizeof (val));
441+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " setsockopt failed" );
442+ } else {
443+ throw std::system_error (std::make_error_code (std::errc::not_supported),
444+ " multicast is only supported on IPv4/IPv6" );
445+ }
446+ }
447+
348448 void io_engine_iocp::socket_shutdown (socket_handle_t socket, bool receive, bool send) {
349449 int mode = 0 ;
350450 if (receive && send)
@@ -532,6 +632,18 @@ namespace asyncpp::io::detail {
532632 }
533633 }
534634
635+ address_type io_engine_iocp::get_handle_type (socket_handle_t socket) {
636+ WSAPROTOCOL_INFO info{};
637+ socklen_t length = sizeof (info);
638+ auto res = getsockopt (socket, SOL_SOCKET, SO_PROTOCOL_INFO, &info, &length);
639+ if (res < 0 ) throw std::system_error (WSAGetLastError (), std::system_category (), " getsockopt failed" );
640+ switch (info.iAddressFamily ) {
641+ case AF_INET: return address_type::ipv4;
642+ case AF_INET6: return address_type::ipv6;
643+ default : throw std::logic_error (" unknown socket type" );
644+ }
645+ }
646+
535647 io_engine::file_handle_t io_engine_iocp::file_open (const char * filename, std::ios_base::openmode mode) {
536648 DWORD access_mode = 0 ;
537649 if ((mode & std::ios_base::in) == std::ios_base::in) access_mode |= GENERIC_READ;
0 commit comments