role_based_system/venv/Lib/site-packages/twisted/internet/_multicast.py

162 lines
5.1 KiB
Python

# -*- test-case-name: twisted.test.test_udp -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
from __future__ import annotations
import socket
import struct
from typing import Any
from twisted.internet.abstract import isIPAddress, isIPv6Address
from twisted.internet.defer import Deferred, succeed
from twisted.internet.error import MulticastJoinError
from twisted.internet.interfaces import IReactorCore
def _maybeResolve(reactor: IReactorCore, addr: str) -> Deferred[str]:
if isIPv6Address(addr) or isIPAddress(addr):
return succeed(addr)
return reactor.resolve(addr)
class MulticastMixin:
"""
Implement multicast functionality.
"""
addressFamily: socket.AddressFamily
reactor: Any
socket: socket.socket
def _addrpack(self, addr: str) -> bytes:
"""
Pack an IP address literal into bytes, according to the address family
of this transport.
"""
try:
return socket.inet_pton(self.addressFamily, addr)
except OSError:
raise MulticastJoinError(
f"invalid address literal for {socket.AddressFamily(self.addressFamily).name}: {addr!r}"
)
@property
def _ipproto(self) -> int:
return (
socket.IPPROTO_IP
if self.addressFamily == socket.AF_INET
else socket.IPPROTO_IPV6
)
@property
def _multiloop(self) -> int:
return (
socket.IP_MULTICAST_LOOP
if self.addressFamily == socket.AF_INET
else socket.IPV6_MULTICAST_LOOP
)
@property
def _multiif(self) -> int:
return (
socket.IP_MULTICAST_IF
if self.addressFamily == socket.AF_INET
else socket.IPV6_MULTICAST_IF
)
@property
def _joingroup(self) -> int:
return (
socket.IP_ADD_MEMBERSHIP
if self.addressFamily == socket.AF_INET
else socket.IPV6_JOIN_GROUP
)
@property
def _leavegroup(self) -> int:
return (
socket.IP_DROP_MEMBERSHIP
if self.addressFamily == socket.AF_INET
else socket.IPV6_LEAVE_GROUP
)
def getOutgoingInterface(self) -> str | int:
blen = 0x4 if self.addressFamily == socket.AF_INET else 0x10
ipproto = self._ipproto
multiif = self._multiif
i = self.socket.getsockopt(ipproto, multiif, blen)
from sys import byteorder
if self.addressFamily == socket.AF_INET6:
return int.from_bytes(i, byteorder)
return socket.inet_ntop(self.addressFamily, i)
def setOutgoingInterface(self, addr: str | int) -> Deferred[int]:
"""
@see: L{IMulticastTransport.setOutgoingInterface}
"""
async def asynchronously() -> int:
i: bytes | int
if self.addressFamily == socket.AF_INET:
assert isinstance(
addr, str
), "IPv4 interfaces are specified as addresses"
i = self._addrpack(await _maybeResolve(self.reactor, addr))
else:
assert isinstance(
addr, int
), "IPv6 interfaces are specified as integers"
i = addr
self.socket.setsockopt(self._ipproto, self._multiif, i)
return 1
return Deferred.fromCoroutine(asynchronously())
def getLoopbackMode(self) -> bool:
return bool(self.socket.getsockopt(self._ipproto, self._multiloop))
def setLoopbackMode(self, mode: int) -> None:
# mode = struct.pack("b", bool(mode))
a = self._ipproto
b = self._multiloop
self.socket.setsockopt(a, b, int(bool(mode)))
def getTTL(self) -> int:
return self.socket.getsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL)
def setTTL(self, ttl: int) -> None:
bttl = struct.pack("B", ttl)
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, bttl)
def _joinleave(self, addr: str, interface: str, join: bool) -> Deferred[None]:
cmd = self._joingroup if join else self._leavegroup
if not interface:
interface = "0.0.0.0" if self.addressFamily == socket.AF_INET else "::"
async def impl() -> None:
resaddr = await _maybeResolve(self.reactor, addr)
resif = await _maybeResolve(self.reactor, interface)
packaddr = self._addrpack(resaddr)
packif = self._addrpack(resif)
try:
self.socket.setsockopt(self._ipproto, cmd, packaddr + packif)
except OSError as e:
raise MulticastJoinError(addr, interface, *e.args) from e
return Deferred.fromCoroutine(impl())
def joinGroup(self, addr: str, interface: str = "") -> Deferred[None]:
"""
@see: L{IMulticastTransport.joinGroup}
"""
return self._joinleave(addr, interface, True)
def leaveGroup(self, addr: str, interface: str = "") -> Deferred[None]:
"""
@see: L{IMulticastTransport.leaveGroup}
"""
return self._joinleave(addr, interface, False)