def handleQuery(self, msg, addr, port): """Deal with incoming query packets. Provides a response if possible.""" out = None # Support unicast client responses # if port != dns._MDNS_PORT: out = dns.DNSOutgoing(dns._FLAGS_QR_RESPONSE | dns._FLAGS_AA, 0) for question in msg.questions: out.addQuestion(question) log.debug( 'Questions...') for question in msg.questions: log.debug( 'Question: %s', question ) if question.type == dns._TYPE_PTR: for service in list(self.services.values()): if question.name == service.type: log.info( 'Service query found %s', service.name ) if out is None: out = dns.DNSOutgoing(dns._FLAGS_QR_RESPONSE | dns._FLAGS_AA) out.addAnswer(msg, dns.DNSPointer(service.type, dns._TYPE_PTR, dns._CLASS_IN, dns._DNS_TTL, service.name)) # devices such as AAstra phones will not re-query to # resolve the pointer, they expect the final IP to show up # in the response out.addAdditionalAnswer(dns.DNSText(service.name, dns._TYPE_TXT, dns._CLASS_IN | dns._CLASS_UNIQUE, dns._DNS_TTL, service.text)) out.addAdditionalAnswer(dns.DNSService(service.name, dns._TYPE_SRV, dns._CLASS_IN | dns._CLASS_UNIQUE, dns._DNS_TTL, service.priority, service.weight, service.port, service.server)) out.addAdditionalAnswer(dns.DNSAddress(service.server, dns._TYPE_A, dns._CLASS_IN | dns._CLASS_UNIQUE, dns._DNS_TTL, service.address)) else: try: if out is None: out = dns.DNSOutgoing(dns._FLAGS_QR_RESPONSE | dns._FLAGS_AA) # Answer A record queries for any service addresses we know if question.type == dns._TYPE_A or question.type == dns._TYPE_ANY: for service in list(self.services.values()): if service.server == question.name.lower(): out.addAnswer(msg, DNSAddress(question.name, dns._TYPE_A, dns._CLASS_IN | dns._CLASS_UNIQUE, dns._DNS_TTL, service.address)) service = self.services.get(question.name.lower(), None) if not service: continue if question.type == dns._TYPE_SRV or question.type == dns._TYPE_ANY: out.addAnswer(msg, dns.DNSService(question.name, dns._TYPE_SRV, dns._CLASS_IN | dns._CLASS_UNIQUE, dns._DNS_TTL, service.priority, service.weight, service.port, service.server)) if question.type == dns._TYPE_TXT or question.type == dns._TYPE_ANY: out.addAnswer(msg, dns.DNSText(question.name, dns._TYPE_TXT, dns._CLASS_IN | dns._CLASS_UNIQUE, dns._DNS_TTL, service.text)) if question.type == dns._TYPE_SRV: out.addAdditionalAnswer(dns.DNSAddress(service.server, dns._TYPE_A, dns._CLASS_IN | dns._CLASS_UNIQUE, dns._DNS_TTL, service.address)) except Exception as err: log.error( 'Error handling query: %s',traceback.format_exc() ) if out is not None and out.answers: out.id = msg.id self.send(out, addr, port) else: log.debug( 'No answer for %s', [q for q in msg.questions] )
def testResponseHeaderBits(self): generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) bytes = generated.packet() print("bytes", bytes.encode()) flags = ord(bytes[2].encode()) << 8 | ord(bytes[3].encode()) print("flags ", flags) self.assertEqual(flags, 0x8000)
def unregisterService(self, info): """Unregister a service.""" try: del (self.services[info.name.lower()]) except: pass now = dns.currentTimeMillis() nextTime = now i = 0 while i < 3: if now < nextTime: self.wait(nextTime - now) now = dns.currentTimeMillis() continue out = dns.DNSOutgoing(dns._FLAGS_QR_RESPONSE | dns._FLAGS_AA) out.addAnswerAtTime( dns.DNSPointer(info.type, dns._TYPE_PTR, dns._CLASS_IN, 0, info.name), 0) out.addAnswerAtTime( dns.DNSService(info.name, dns._TYPE_SRV, dns._CLASS_IN, 0, info.priority, info.weight, info.port, info.name), 0) out.addAnswerAtTime( dns.DNSText(info.name, dns._TYPE_TXT, dns._CLASS_IN, 0, info.text), 0) if info.address: out.addAnswerAtTime( dns.DNSAddress(info.server, dns._TYPE_A, dns._CLASS_IN, 0, info.address), 0) self.send(out) i += 1 nextTime += _UNREGISTER_TIME
def test_response_header_bits(self): generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) b = generated.packet() print("bytes", b) #flags = ord(b[2]) << 8 | ord(b[3]) flags = b[2] << 8 | b[3] self.assertEqual(flags, 0x8000)
def registerService(self, info, ttl=dns._DNS_TTL): """Registers service information to the network with a default TTL of 60 seconds. Zeroconf will then respond to requests for information for that service. The name of the service may be changed if needed to make it unique on the network.""" #print "REGISTERING SERVICE!" #log.debug(">>",self.services[info.name.lower()]) #import pdb #pdb.set_trace() self.checkService(info) self.services[info.name.lower()] = info log.debug(">>",self.services[info.name.lower()]) now = dns.currentTimeMillis() nextTime = now i = 0 while i < 3: if now < nextTime: self.wait(nextTime - now) now = dns.currentTimeMillis() continue out = dns.DNSOutgoing(dns._FLAGS_QR_RESPONSE | dns._FLAGS_AA) out.addAnswerAtTime(dns.DNSPointer(info.type, dns._TYPE_PTR, dns._CLASS_IN, ttl, info.name), 0) out.addAnswerAtTime(dns.DNSService(info.name, dns._TYPE_SRV, dns._CLASS_IN, ttl, info.priority, info.weight, info.port, info.server), 0) out.addAnswerAtTime(dns.DNSText(info.name, dns._TYPE_TXT, dns._CLASS_IN, ttl, info.text), 0) if info.address: out.addAnswerAtTime(dns.DNSAddress(info.server, dns._TYPE_A, dns._CLASS_IN, ttl, info.address), 0) self.send(out) i += 1 nextTime += _REGISTER_TIME print("registerService end")
def run(self): while 1: event = None now = dns.currentTimeMillis() if len(self.list) == 0 and self.nextTime > now: self.zeroconf.wait(self.nextTime - now) if globals()['_GLOBAL_DONE'] or self.done: return now = dns.currentTimeMillis() if self.nextTime <= now: out = dns.DNSOutgoing(dns._FLAGS_QR_QUERY) out.addQuestion(dns.DNSQuestion(self.type, dns._TYPE_PTR, dns._CLASS_IN)) for record in list(self.services.values()): if not record.isExpired(now): out.addAnswerAtTime(record, now) self.zeroconf.send(out) self.nextTime = now + self.delay self.delay = min(20 * 1000, self.delay * 2) if len(self.list) > 0: event = self.list.pop(0) if event is not None: event(self.zeroconf)
def checkService(self, info): """Checks the network for a unique service name, modifying the ServiceInfo passed in if it is not unique.""" now = dns.currentTimeMillis() nextTime = now i = 0 while i < 3: for record in self.cache.entriesWithName(info.type): if record.type == dns._TYPE_PTR and not record.isExpired(now) and record.alias == info.name: if (info.name.find('.') < 0): info.name = info.name + ".[" + info.address + ":" + info.port + "]." + info.type self.checkService(info) return raise NonUniqueNameException if now < nextTime: self.wait(nextTime - now) now = dns.currentTimeMillis() continue out = dns.DNSOutgoing(dns._FLAGS_QR_QUERY | dns._FLAGS_AA) self.debug = out out.addQuestion(dns.DNSQuestion(info.type, dns._TYPE_PTR, dns._CLASS_IN)) out.addAuthorativeAnswer(dns.DNSPointer(info.type, dns._TYPE_PTR, dns._CLASS_IN, dns._DNS_TTL, info.name)) self.send(out) i += 1 nextTime += _CHECK_TIME
def testSameName(self): name = "paired.local." generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) question = r.DNSQuestion(name, r._TYPE_SRV, r._CLASS_IN) generated.addQuestion(question) generated.addQuestion(question) parsed = r.DNSIncoming(generated.packet())
def testLongName(self): generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) question = r.DNSQuestion( "this.is.a.very.long.name.with.lots.of.parts.in.it.local.", r._TYPE_SRV, r._CLASS_IN) generated.addQuestion(question) parsed = r.DNSIncoming(generated.packet())
def unregisterAllServices(self): """Unregister all registered services.""" if len(self.services) > 0: now = dns.currentTimeMillis() nextTime = now i = 0 while i < 3: if now < nextTime: self.wait(nextTime - now) now = dns.currentTimeMillis() continue out = dns.DNSOutgoing(dns._FLAGS_QR_RESPONSE | dns._FLAGS_AA) for info in self.services.values(): out.addAnswerAtTime( dns.DNSPointer(info.type, dns._TYPE_PTR, dns._CLASS_IN, 0, info.name), 0) out.addAnswerAtTime( dns.DNSService(info.name, dns._TYPE_SRV, dns._CLASS_IN, 0, info.priority, info.weight, info.port, info.server), 0) out.addAnswerAtTime( dns.DNSText(info.name, dns._TYPE_TXT, dns._CLASS_IN, 0, info.text), 0) if info.address: out.addAnswerAtTime( dns.DNSAddress(info.server, dns._TYPE_A, dns._CLASS_IN, 0, info.address), 0) self.send(out) i += 1 nextTime += _UNREGISTER_TIME
def testMatchQuestion(self): generated = r.DNSOutgoing(r._FLAGS_QR_QUERY) question = r.DNSQuestion("testname.local.", r._TYPE_SRV, r._CLASS_IN) generated.addQuestion(question) parsed = r.DNSIncoming(generated.packet()) self.assertEqual(len(generated.questions), 1) self.assertEqual(len(generated.questions), len(parsed.questions)) self.assertEqual(question, parsed.questions[0])
def serviceAnnouncement( cls, info, ttl=dns._DNS_TTL ): out = dns.DNSOutgoing(dns._FLAGS_QR_RESPONSE | dns._FLAGS_AA) out.addAnswerAtTime(dns.DNSPointer(info.type, dns._TYPE_PTR, dns._CLASS_IN, ttl, info.name), 0) out.addAnswerAtTime(dns.DNSService(info.name, dns._TYPE_SRV, dns._CLASS_IN, ttl, info.priority, info.weight, info.port, info.server), 0) out.addAnswerAtTime(dns.DNSText(info.name, dns._TYPE_TXT, dns._CLASS_IN, ttl, info.text), 0) if info.address: out.addAnswerAtTime(dns.DNSAddress(info.server, dns._TYPE_A, dns._CLASS_IN, ttl, info.address), 0) return out
def handleQuery(self, msg, addr, port): """Deal with incoming query packets. Provides a response if possible.""" out = None # Support unicast client responses # if port != dns._MDNS_PORT: out = dns.DNSOutgoing(dns._FLAGS_QR_RESPONSE | dns._FLAGS_AA, 0) for question in msg.questions: out.addQuestion(question) log.debug('Questions...') for question in msg.questions: log.debug('Question: %s', question) if out is None: out = dns.DNSOutgoing(dns._FLAGS_QR_RESPONSE | dns._FLAGS_AA) try: self.responses(question, msg, out) except Exception, err: log.error('Error handling query: %s', traceback.format_exc())
def testNumbers(self): generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) bytes = generated.packet() numQuestions = ord(bytes[4]) << 8 | ord(bytes[5]) numAnswers = ord(bytes[6]) << 8 | ord(bytes[7]) numAuthorities = ord(bytes[8]) << 8 | ord(bytes[9]) numAddtionals = ord(bytes[10]) << 8 | ord(bytes[11]) self.assertEqual(numQuestions, 0) self.assertEqual(numAnswers, 0) self.assertEqual(numAuthorities, 0) self.assertEqual(numAddtionals, 0)
def testNumbersQuestions(self): generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) question = r.DNSQuestion("testname.local.", r._TYPE_SRV, r._CLASS_IN) for i in range(0, 10): generated.addQuestion(question) bytes = generated.packet() numQuestions = ord(bytes[4]) << 8 | ord(bytes[5]) numAnswers = ord(bytes[6]) << 8 | ord(bytes[7]) numAuthorities = ord(bytes[8]) << 8 | ord(bytes[9]) numAddtionals = ord(bytes[10]) << 8 | ord(bytes[11]) self.assertEqual(numQuestions, 10) self.assertEqual(numAnswers, 0) self.assertEqual(numAuthorities, 0) self.assertEqual(numAddtionals, 0)
def testExceedinglyLongName(self): generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) name = "%slocal." % ("part." * 1000) question = r.DNSQuestion(name, r._TYPE_SRV, r._CLASS_IN) generated.addQuestion(question) parsed = r.DNSIncoming(generated.packet())
def testParseOwnPacketSimpleUnicast(self): generated = r.DNSOutgoing(0, 0) parsed = r.DNSIncoming(generated.packet())
def testTransactionID(self): """ID must be zero in a DNS-SD packet""" generated = r.DNSOutgoing(r._FLAGS_QR_QUERY) bytes = generated.packet() id = ord(bytes[0]) << 8 | ord(bytes[1]) self.assertEqual(id, 0)
def testQueryHeaderBits(self): generated = r.DNSOutgoing(r._FLAGS_QR_QUERY) bytes = generated.packet() flags = ord(bytes[2]) << 8 | ord(bytes[3]) self.assertEqual(flags, 0x0)
def testResponseHeaderBits(self): generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) bytes = generated.packet() flags = ord(bytes[2]) << 8 | ord(bytes[3]) self.assertEqual(flags, 0x8000)
def test_query_header_bits(self): generated = r.DNSOutgoing(r._FLAGS_QR_QUERY) b = generated.packet().decode() flags = ord(b[2]) << 8 | ord(b[3]) self.assertEqual(flags, 0x0)
def test_transaction_id(self): # ID must be zero in a DNS-SD packet generated = r.DNSOutgoing(r._FLAGS_QR_QUERY) b = generated.packet().decode() id = ord(b[0]) << 8 | ord(b[1]) self.assertEqual(id, 0)
def testParseOwnPacketQuestion(self): generated = r.DNSOutgoing(r._FLAGS_QR_QUERY) generated.addQuestion( r.DNSQuestion("testname.local.", r._TYPE_SRV, r._CLASS_IN)) parsed = r.DNSIncoming(generated.packet()) print("parsed questions %s " % parsed.questions)
def testParseOwnPacketFlags(self): generated = r.DNSOutgoing(r._FLAGS_QR_QUERY) parsed = r.DNSIncoming(generated.packet()) print("parsed %s " % parsed)
def testParseOwnPacketSimpleUnicast(self): generated = r.DNSOutgoing(0, 0) parsed = r.DNSIncoming(generated.packet()) print("parsed %s " % parsed) self.assertEqual(type(parsed), r.DNSIncoming)
def testExceedinglyLongNamePart(self): name = "%s.local." % ("a" * 1000) generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) question = r.DNSQuestion(name, r._TYPE_SRV, r._CLASS_IN) generated.addQuestion(question) self.assertRaises(r.NamePartTooLongException, generated.packet)