Beispiel #1
0
    def datagram_received(self, data, addr) -> None:
        """Handle discovery responses."""
        ip, port = addr
        if ip in self.discovered_devices:
            return

        if port == 9999:
            info = json.loads(TPLinkSmartHomeProtocol.decrypt(data))
            device_class = Discover._get_device_class(info)
            device = device_class(ip)
        else:
            info = json.loads(data[16:])
            device_class = Discover._get_new_device_class(info)
            owner = Discover._get_new_owner(info)
            if owner is not None:
                owner_bin = bytes.fromhex(owner)

            _LOGGER.debug(
                "[DISCOVERY] Device owner is %s, empty owner is %s",
                owner_bin,
                self.emptyUser,
            )
            if owner is None or owner == "" or owner_bin == self.emptyUser:
                _LOGGER.debug("[DISCOVERY] Device %s has no owner", ip)
                device = device_class(ip, Auth())
            elif (self.authentication is not None
                  and owner_bin == self.authentication.owner()):
                _LOGGER.debug("[DISCOVERY] Device %s has authenticated owner",
                              ip)
                device = device_class(ip, self.authentication)
            else:
                _LOGGER.debug("[DISCOVERY] Found %s with unknown owner %s", ip,
                              owner)
                return

        _LOGGER.debug("[DISCOVERY] %s << %s", ip, info)

        asyncio.ensure_future(device.update())

        self.discovered_devices[ip] = device
        self.discovered_devices_raw[ip] = info

        if device_class is not None:
            if self.on_discovered is not None:
                asyncio.ensure_future(self.on_discovered(device))
        else:
            _LOGGER.error("Received invalid response: %s", info)
def read_payloads_from_file(file):
    """Read the given pcap file and yield json payloads."""
    pcap = dpkt.pcap.Reader(file)
    for ts, pkt in pcap:
        eth = Ethernet(pkt)
        if eth.type != ETH_TYPE_IP:
            continue

        ip = eth.ip
        if ip.p == 6:
            transport = ip.tcp
        elif ip == 17:
            transport = ip.udp
        else:
            continue

        if transport.sport != 9999 and transport.dport != 9999:
            continue

        data = transport.data

        try:
            decrypted = TPLinkSmartHomeProtocol.decrypt(data[4:])
        except Exception as ex:
            click.echo(
                click.style(f"Unable to decrypt the data, ignoring: {ex}", fg="red")
            )
            continue

        try:
            json_payload = json.loads(decrypted)
        except Exception as ex:
            click.echo(
                click.style(f"Unable to parse payload, ignoring: {ex}", fg="red")
            )
            continue

        if not json_payload:  # ignore empty payloads
            click.echo(click.style("Got empty payload, ignoring", fg="red"))
            continue

        yield json_payload
Beispiel #3
0
    def datagram_received(self, data, addr) -> None:
        """Handle discovery responses."""
        ip, port = addr
        if ip in self.discovered_devices:
            return

        info = json.loads(TPLinkSmartHomeProtocol.decrypt(data))
        _LOGGER.debug("[DISCOVERY] %s << %s", ip, info)

        try:
            device = Discover._create_device_from_discovery_info(ip, info)
        except SmartDeviceException as ex:
            _LOGGER.debug("Unable to find device type from %s: %s", info, ex)
            return

        self.discovered_devices[ip] = device

        if device is not None:
            if self.on_discovered is not None:
                asyncio.ensure_future(self.on_discovered(device))
        else:
            _LOGGER.error("Received invalid response: %s", info)
Beispiel #4
0
class _DiscoverProtocol(asyncio.DatagramProtocol):
    """Implementation of the discovery protocol handler.

    This is internal class, use :func:Discover.discover: instead.
    """

    discovered_devices: Dict[str, SmartDevice]
    discovered_devices_raw: Dict[str, Dict]

    def __init__(
        self,
        *,
        on_discovered: OnDiscoveredCallable = None,
        target: str = "255.255.255.255",
        timeout: int = 5,
        discovery_packets: int = 3,
    ):
        self.transport = None
        self.tries = discovery_packets
        self.timeout = timeout
        self.on_discovered = on_discovered
        self.protocol = TPLinkSmartHomeProtocol()
        self.target = (target, Discover.DISCOVERY_PORT)
        self.discovered_devices = {}
        self.discovered_devices_raw = {}

    def connection_made(self, transport) -> None:
        """Set socket options for broadcasting."""
        self.transport = transport
        sock = transport.get_extra_info("socket")
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        self.do_discover()

    def do_discover(self) -> None:
        """Send number of discovery datagrams."""
        req = json.dumps(Discover.DISCOVERY_QUERY)
        _LOGGER.debug("[DISCOVERY] %s >> %s", self.target,
                      Discover.DISCOVERY_QUERY)
        encrypted_req = self.protocol.encrypt(req)
        for i in range(self.tries):
            self.transport.sendto(encrypted_req[4:],
                                  self.target)  # type: ignore

    def datagram_received(self, data, addr) -> None:
        """Handle discovery responses."""
        ip, port = addr
        if ip in self.discovered_devices:
            return

        info = json.loads(self.protocol.decrypt(data))
        _LOGGER.debug("[DISCOVERY] %s << %s", ip, info)

        device_class = Discover._get_device_class(info)
        device = device_class(ip)

        self.discovered_devices[ip] = device
        self.discovered_devices_raw[ip] = info

        if device_class is not None:
            if self.on_discovered is not None:
                asyncio.ensure_future(self.on_discovered(device))
        else:
            _LOGGER.error("Received invalid response: %s", info)

    def error_received(self, ex):
        """Handle asyncio.Protocol errors."""
        _LOGGER.error("Got error: %s", ex)

    def connection_lost(self, ex):
        """NOP implementation of connection lost."""