コード例 #1
0
ファイル: upnp.py プロジェクト: paul-axe/darknet
 def handleWanServiceDesc(self, body):
     log.msg("got WANServiceDesc from %s"%(self.urlbase,), system='UPnP')
     data = body.read()
     self.soap = SOAPRequestFactory(self.controlURL,
                         "urn:schemas-upnp-org:service:WANIPConnection:1")
     self.soap.setSCPD(data)
     return self.completedDiscovery()
コード例 #2
0
ファイル: upnp.py プロジェクト: paul-axe/darknet
class UPnPClientServer(SocketServer.UDPServer):
    def __init__(self, *args, **kwargs):
        SocketServer.UDPServer.__init__(self, *args, **kwargs)

        self.timeoutEvent_ = threading.Event()
        self.controlURL = None
        self.upnpInfo = {}
        self.urlbase = None
        self.gotSearchResponse = False

    def discoverUPnP(self):
        "Discover UPnP devices. Returns a Deferred"

        self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)

        search = cannedUPnPSearch()
        try:
            self.socket.sendto(search, (UPNP_MCAST, UPNP_PORT))
            self.socket.sendto(search, (UPNP_MCAST, UPNP_PORT))
            self.socket.sendto(search, (UPNP_MCAST, UPNP_PORT))
        except socket.error:
            self.timeoutEvent_.set()
            raise NoUPnPFound('no network available')

        startTime = time.time()
        while not self.timeoutEvent_.isSet():
            self.socket.settimeout(6)
            self.handle_request()
            if time.time() > startTime + 6:
                break
        #self.timeoutEvent_.wait(6)
        self.timeoutEvent_.set()

        if self.urlbase is None:
            raise NoUPnPFound()

        return self

    def handle(self, handler):
        dgram = handler.rfile.read()
        address = handler.client_address

        #log.msg("got a response from %s, dgram is %s "%(address, dgram), system='UPnP')

        if not "\r\n" in dgram:
            return
        response, message = dgram.split('\r\n', 1)
        version, status, textstatus = response.split(None, 2)
        if not version.startswith('HTTP'):
            return
        if self.controlURL:
            return
        #slog.msg("got a response from %s, status %s "%(address, status),
        #                               system='UPnP')
        if status == "200":
            self.gotSearchResponse = True
            self.handleSearchResponse(message)
            self.timeoutEvent_.set()

    def handleSearchResponse(self, message):
        import urlparse
        headers, body = self.parseSearchResponse(message)
        loc = headers.get('location')
        if not loc:
            log.msg("No location header in response to M-SEARCH!",
                                                            system='UPnP')
            return
        loc = loc[0]
        # === Should catch some errors from urlopen
        f = urlopen(loc)
        log.msg("found a UPnP device at %s"%loc, system="UPnP")
        return self.handleIGDeviceResponse(f, loc)

    def parseSearchResponse(self, message):
        hdict = {}
        body = ''
        remaining = message
        while remaining:
            line, remaining = remaining.split('\r\n', 1)
            line = line.strip()
            if not line:
                body = remaining
                break
            key, val = line.split(':', 1)
            key = key.lower()
            hdict.setdefault(key, []).append(val.strip())
        return hdict, body

    def completedDiscovery(self):
        log.msg("discovery completed", system="UPnP")
        self.timeoutEvent_.set()
        if self.controlURL is not None:
            return self

    def handleIGDeviceResponse(self, body, loc):
        log.msg("got an IGDevice from %s"%(loc,), system='UPnP')
        if self.controlURL is not None:
            log.msg("already found UPnP, discarding duplicate response",
                                                                system="UPnP")
            # We already got a working one - ignore this one
            return
        data = body.read()
        bs = BeautifulSoap(data)
        manufacturer = bs.first('manufacturer')
        if manufacturer and manufacturer.contents:
            log.msg("you're behind a %s"%(manufacturer.contents[0]),
                                                                system='UPnP')
            self.upnpInfo['manufacturer'] = manufacturer.contents[0]
        friendly = bs.first('friendlyName')
        if friendly:
            self.upnpInfo['friendlyName'] = str(friendly.contents[0])
        urlbase = bs.first('URLBase')
        if urlbase and urlbase.contents:
            self.urlbase = str(urlbase.contents[0])
            log.msg("upnp urlbase is %s"%(self.urlbase), system='UPnP')
        else:
            log.msg("upnp response has no urlbase, falling back to %s"%(loc,), system='UPnP')
            self.urlbase = loc

        wanservices = bs.fetch('service',
            dict(serviceType='urn:schemas-upnp-org:service:WANIPConnection:%'))
        for service in wanservices:
            scpdurl = service.get('SCPDURL')
            controlurl = service.get('controlURL')
            if scpdurl and controlurl:
                break
        else:
            log.msg("upnp response showed no WANIPConnections", system='UPnP')
            return

        self.controlURL = urlparse.urljoin(self.urlbase, controlurl)
        log.msg("upnp %r controlURL is %s"%(self, self.controlURL), system='UPnP')
        # === catch exceptions from urlopen
        f = urlopen(urlparse.urljoin(self.urlbase, scpdurl))
        return self.handleWanServiceDesc(f)

    def handleWanServiceDesc(self, body):
        log.msg("got WANServiceDesc from %s"%(self.urlbase,), system='UPnP')
        data = body.read()
        self.soap = SOAPRequestFactory(self.controlURL,
                            "urn:schemas-upnp-org:service:WANIPConnection:1")
        self.soap.setSCPD(data)
        return self.completedDiscovery()

    def getExternalIPAddress(self):
        req = self.soap.GetExternalIPAddress()
        try:
            f = soapenurl(req)
            return self.cb_gotExternalIPAddress(f)
        except soap.SOAPError, e:
            raise self.cb_failedExternalIPAddress(e)