# Buggy code
def _maintain_redirects(self):
    # setup the gateway if necessary
    if not self.upnp:
        try:
            self.upnp = yield from_future(UPnP.discover())
            log.info("found upnp gateway: %s", self.upnp.gateway.manufacturer_string)
        except Exception as err:
            log.warning("upnp discovery failed: %s", err)
            return

    # update the external ip
    try:
        external_ip = yield from_future(self.upnp.get_external_ip())
        if external_ip == "0.0.0.0":
            log.warning(
                "upnp doesn't know the external ip address (returned 0.0.0.0), using fallback"
            )
            external_ip = CS.get_external_ip()
        if self.external_ip and self.external_ip != external_ip:
            log.info("external ip changed from %s to %s", self.external_ip, external_ip)
        elif not self.external_ip:
            log.info("got external ip: %s", external_ip)
        self.external_ip = external_ip
    except (asyncio.TimeoutError, UPnPError):
        pass

    if not self.upnp_redirects:  # setup missing redirects
        try:
            upnp_redirects = yield DeferredDict(
                {
                    "UDP": from_future(
                        self.upnp.get_next_mapping(
                            self._int_dht_node_port, "UDP", "LBRY DHT port"
                        )
                    ),
                    "TCP": from_future(
                        self.upnp.get_next_mapping(
                            self._int_peer_port, "TCP", "LBRY peer port"
                        )
                    ),
                }
            )
            self.upnp_redirects.update(upnp_redirects)
        except (asyncio.TimeoutError, UPnPError):
            self.upnp = None
            return self._maintain_redirects()
    else:  # check existing redirects are still active
        found = set()
        mappings = yield from_future(self.upnp.get_redirects())
        for mapping in mappings:
            proto = mapping["NewProtocol"]
            if (
                proto in self.upnp_redirects
                and mapping["NewExternalPort"] == self.upnp_redirects[proto]
            ):
                if mapping["NewInternalClient"] == self.upnp.lan_address:
                    found.add(proto)
        if "UDP" not in found:
            try:
                udp_port = yield from_future(
                    self.upnp.get_next_mapping(
                        self._int_dht_node_port, "UDP", "LBRY DHT port"
                    )
                )
                self.upnp_redirects["UDP"] = udp_port
                log.info("refreshed upnp redirect for dht port: %i", udp_port)
            except (asyncio.TimeoutError, UPnPError):
                del self.upnp_redirects["UDP"]
        if "TCP" not in found:
            try:
                tcp_port = yield from_future(
                    self.upnp.get_next_mapping(
                        self._int_peer_port, "TCP", "LBRY peer port"
                    )
                )
                self.upnp_redirects["TCP"] = tcp_port
                log.info("refreshed upnp redirect for peer port: %i", tcp_port)
            except (asyncio.TimeoutError, UPnPError):
                del self.upnp_redirects["TCP"]
        if "TCP" in self.upnp_redirects and "UDP" in self.upnp_redirects:
            log.debug("upnp redirects are still active")


# Traceback (Exception)
2018-10-28 21:39:16,952 WARNING  lbrynet.daemon.Components:712: upnp discovery failed: {http://schemas.microsoft.com/windows/pnpx/2005/11}X_hardwareId
2018-10-28 21:39:16,953 ERROR    lbrynet.daemon.Components:782: failed to setup upnp
Unhandled error in Deferred:
2018-10-28 21:40:32,480 CRITICAL twisted:154: Unhandled error in Deferred:

2018-10-28 21:40:32,481 CRITICAL twisted:154:
Traceback (most recent call last):
File "twisted\internet\defer.py", line 1418, in _inlineCallbacks
File "lbrynet\dht\node.py", line 635, in _iterativeFind
File "twisted\internet\defer.py", line 1362, in returnValue
twisted.internet.defer._DefGen_Return: [<lbrynet.dht.contact._Contact object at 0x067573D0>, <lbrynet.dht.contact._Contact object at 0x06796470>, <lbrynet.dht.contact._Contact object at 0x064A5B30>, <lbrynet.dht.contact._Contact object at 0x067798B0>, <lbrynet.dht.contact._Contact object at 0x06793DB0>, <lbrynet.dht.contact._Contact object at 0x0647D510>, <lbrynet.dht.contact._Contact object at 0x06779890>, <lbrynet.dht.contact._Contact object at 0x06488CF0>]

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "twisted\internet\defer.py", line 1416, in _inlineCallbacks
File "twisted\python\failure.py", line 491, in throwExceptionIntoGenerator
File "lbrynet\dht\hashannouncer.py", line 38, in do_store
File "twisted\internet\defer.py", line 1418, in _inlineCallbacks
File "lbrynet\dht\node.py", line 326, in announceHaveBlob
Exception: Cannot determine external IP: None
Unhandled error in Deferred:

# Fixed code
def _maintain_redirects(self):
    # setup the gateway if necessary
    if not self.upnp:
        try:
            self.upnp = yield from_future(UPnP.discover())
            log.info("found upnp gateway: %s", self.upnp.gateway.manufacturer_string)
        except Exception as err:
            log.warning("upnp discovery failed: %s", err)
            self.upnp = None

    # update the external ip
    external_ip = None
    if self.upnp:
        try:
            external_ip = yield from_future(self.upnp.get_external_ip())
            if external_ip != "0.0.0.0":
                log.info("got external ip from UPnP: %s", external_ip)
        except (asyncio.TimeoutError, UPnPError):
            pass

    if external_ip == "0.0.0.0" or not external_ip:
        log.warning("unable to get external ip from UPnP, checking lbry.io fallback")
        external_ip = CS.get_external_ip()
    if self.external_ip and self.external_ip != external_ip:
        log.info("external ip changed from %s to %s", self.external_ip, external_ip)
    self.external_ip = external_ip
    assert self.external_ip is not None  # TODO: handle going/starting offline

    if not self.upnp_redirects and self.upnp:  # setup missing redirects
        try:
            log.info("add UPnP port mappings")
            upnp_redirects = yield DeferredDict(
                {
                    "UDP": from_future(
                        self.upnp.get_next_mapping(
                            self._int_dht_node_port, "UDP", "LBRY DHT port"
                        )
                    ),
                    "TCP": from_future(
                        self.upnp.get_next_mapping(
                            self._int_peer_port, "TCP", "LBRY peer port"
                        )
                    ),
                }
            )
            log.info("set up redirects: %s", upnp_redirects)
            self.upnp_redirects.update(upnp_redirects)
        except (asyncio.TimeoutError, UPnPError):
            self.upnp = None
            return self._maintain_redirects()
    elif self.upnp:  # check existing redirects are still active
        found = set()
        mappings = yield from_future(self.upnp.get_redirects())
        for mapping in mappings:
            proto = mapping["NewProtocol"]
            if (
                proto in self.upnp_redirects
                and mapping["NewExternalPort"] == self.upnp_redirects[proto]
            ):
                if mapping["NewInternalClient"] == self.upnp.lan_address:
                    found.add(proto)
        if "UDP" not in found:
            try:
                udp_port = yield from_future(
                    self.upnp.get_next_mapping(
                        self._int_dht_node_port, "UDP", "LBRY DHT port"
                    )
                )
                self.upnp_redirects["UDP"] = udp_port
                log.info("refreshed upnp redirect for dht port: %i", udp_port)
            except (asyncio.TimeoutError, UPnPError):
                del self.upnp_redirects["UDP"]
        if "TCP" not in found:
            try:
                tcp_port = yield from_future(
                    self.upnp.get_next_mapping(
                        self._int_peer_port, "TCP", "LBRY peer port"
                    )
                )
                self.upnp_redirects["TCP"] = tcp_port
                log.info("refreshed upnp redirect for peer port: %i", tcp_port)
            except (asyncio.TimeoutError, UPnPError):
                del self.upnp_redirects["TCP"]
        if "TCP" in self.upnp_redirects and "UDP" in self.upnp_redirects:
            log.debug("upnp redirects are still active")