@@ -458,15 +458,34 @@ def create_sockets(
458458 if multicast_addresses and unicast :
459459 raise ValueError ("multicast_addresses is incompatible with unicast=True" )
460460
461+ # Reject IP-version-incompatible entries up front so callers get a clear
462+ # error instead of a confusing adapter-lookup or socket-syscall failure.
463+ if multicast_addresses :
464+ if ip_version == IPVersion .V4Only :
465+ for entry in multicast_addresses :
466+ if isinstance (entry , (int , tuple )) or (
467+ isinstance (entry , str ) and ipaddress .ip_address (entry ).version == 6
468+ ):
469+ raise ValueError ("multicast_addresses contains IPv6 entries but ip_version is V4Only" )
470+ elif ip_version == IPVersion .V6Only :
471+ for entry in multicast_addresses :
472+ if isinstance (entry , str ) and ipaddress .ip_address (entry ).version == 4 :
473+ raise ValueError ("multicast_addresses contains IPv4 entries but ip_version is V6Only" )
474+
461475 if unicast :
462476 listen_socket = None
463477 else :
464478 listen_socket = new_socket (bind_addr = ("" ,), ip_version = ip_version , apple_p2p = apple_p2p )
465479
466480 normalized_interfaces = normalize_interface_choice (interfaces , ip_version )
467- extra_multicast_members = (
468- normalize_interface_choice (list (multicast_addresses ), ip_version ) if multicast_addresses else []
469- )
481+ if multicast_addresses :
482+ extra_multicast_members = normalize_interface_choice (list (multicast_addresses ), ip_version )
483+ # Strip entries already covered by ``interfaces`` so add_multicast_member
484+ # is not called twice for the same membership.
485+ interface_set = set (normalized_interfaces )
486+ extra_multicast_members = [m for m in extra_multicast_members if m not in interface_set ]
487+ else :
488+ extra_multicast_members = []
470489
471490 # If we are using InterfaceChoice.Default with only IPv4 or only IPv6, we can use
472491 # a single socket to listen and respond.
@@ -510,17 +529,29 @@ def can_send_to(ipv6_socket: bool, address: str) -> bool:
510529 return ":" in address if ipv6_socket else ":" not in address
511530
512531
513- def autodetect_ip_version (interfaces : InterfacesType ) -> IPVersion :
532+ def autodetect_ip_version (
533+ interfaces : InterfacesType ,
534+ multicast_addresses : Sequence [str | int | tuple [tuple [str , int , int ], int ]] | None = None ,
535+ ) -> IPVersion :
514536 """Auto detect the IP version when it is not provided."""
537+ has_v6 = False
538+ has_v4 = False
515539 if isinstance (interfaces , list ):
516540 has_v6 = any (
517541 isinstance (i , int ) or (isinstance (i , str ) and ipaddress .ip_address (i ).version == 6 )
518542 for i in interfaces
519543 )
520544 has_v4 = any (isinstance (i , str ) and ipaddress .ip_address (i ).version == 4 for i in interfaces )
521- if has_v4 and has_v6 :
522- return IPVersion .All
523- if has_v6 :
524- return IPVersion .V6Only
525-
545+ if multicast_addresses :
546+ has_v6 = has_v6 or any (
547+ isinstance (i , (int , tuple )) or (isinstance (i , str ) and ipaddress .ip_address (i ).version == 6 )
548+ for i in multicast_addresses
549+ )
550+ has_v4 = has_v4 or any (
551+ isinstance (i , str ) and ipaddress .ip_address (i ).version == 4 for i in multicast_addresses
552+ )
553+ if has_v4 and has_v6 :
554+ return IPVersion .All
555+ if has_v6 :
556+ return IPVersion .V6Only
526557 return IPVersion .V4Only
0 commit comments