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()
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)