util/Zeroconf.py
changeset 1730 64d8f52bc8c8
parent 726 ae63ccc29444
child 1736 7e61baa047f0
equal deleted inserted replaced
1726:d51af006fa6b 1730:64d8f52bc8c8
    17     Lesser General Public License for more details.
    17     Lesser General Public License for more details.
    18 
    18 
    19     You should have received a copy of the GNU Lesser General Public
    19     You should have received a copy of the GNU Lesser General Public
    20     License along with this library; if not, write to the Free Software
    20     License along with this library; if not, write to the Free Software
    21     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    21     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    22     
    22 
    23 """
    23 """
    24 
    24 
    25 """0.12 update - allow selection of binding interface
    25 """0.12 update - allow selection of binding interface
    26          typo fix - Thanks A. M. Kuchlingi
    26          typo fix - Thanks A. M. Kuchlingi
    27          removed all use of word 'Rendezvous' - this is an API change"""
    27          removed all use of word 'Rendezvous' - this is an API change"""
    99 _REGISTER_TIME = 225
    99 _REGISTER_TIME = 225
   100 _LISTENER_TIME = 200
   100 _LISTENER_TIME = 200
   101 _BROWSER_TIME = 500
   101 _BROWSER_TIME = 500
   102 
   102 
   103 # Some DNS constants
   103 # Some DNS constants
   104     
   104 
   105 _MDNS_ADDR = '224.0.0.251'
   105 _MDNS_ADDR = '224.0.0.251'
   106 _MDNS_PORT = 5353;
   106 _MDNS_PORT = 5353;
   107 _DNS_PORT = 53;
   107 _DNS_PORT = 53;
   108 _DNS_TTL = 60 * 60; # one hour default TTL
   108 _DNS_TTL = 60 * 60; # one hour default TTL
   109 
   109 
   206 
   206 
   207 # implementation classes
   207 # implementation classes
   208 
   208 
   209 class DNSEntry(object):
   209 class DNSEntry(object):
   210     """A DNS entry"""
   210     """A DNS entry"""
   211     
   211 
   212     def __init__(self, name, type, clazz):
   212     def __init__(self, name, type, clazz):
   213         self.key = string.lower(name)
   213         self.key = string.lower(name)
   214         self.name = name
   214         self.name = name
   215         self.type = type
   215         self.type = type
   216         self.clazz = clazz & _CLASS_MASK
   216         self.clazz = clazz & _CLASS_MASK
   254             result += "]"
   254             result += "]"
   255         return result
   255         return result
   256 
   256 
   257 class DNSQuestion(DNSEntry):
   257 class DNSQuestion(DNSEntry):
   258     """A DNS question entry"""
   258     """A DNS question entry"""
   259     
   259 
   260     def __init__(self, name, type, clazz):
   260     def __init__(self, name, type, clazz):
   261         if not name.endswith(".local."):
   261         if not name.endswith(".local."):
   262             raise NonLocalNameException
   262             raise NonLocalNameException
   263         DNSEntry.__init__(self, name, type, clazz)
   263         DNSEntry.__init__(self, name, type, clazz)
   264 
   264 
   271         return DNSEntry.toString(self, "question", None)
   271         return DNSEntry.toString(self, "question", None)
   272 
   272 
   273 
   273 
   274 class DNSRecord(DNSEntry):
   274 class DNSRecord(DNSEntry):
   275     """A DNS record - like a DNS entry, but has a TTL"""
   275     """A DNS record - like a DNS entry, but has a TTL"""
   276     
   276 
   277     def __init__(self, name, type, clazz, ttl):
   277     def __init__(self, name, type, clazz, ttl):
   278         DNSEntry.__init__(self, name, type, clazz)
   278         DNSEntry.__init__(self, name, type, clazz)
   279         self.ttl = ttl
   279         self.ttl = ttl
   280         self.created = currentTimeMillis()
   280         self.created = currentTimeMillis()
   281 
   281 
   332         arg = "%s/%s,%s" % (self.ttl, self.getRemainingTTL(currentTimeMillis()), other)
   332         arg = "%s/%s,%s" % (self.ttl, self.getRemainingTTL(currentTimeMillis()), other)
   333         return DNSEntry.toString(self, "record", arg)
   333         return DNSEntry.toString(self, "record", arg)
   334 
   334 
   335 class DNSAddress(DNSRecord):
   335 class DNSAddress(DNSRecord):
   336     """A DNS address record"""
   336     """A DNS address record"""
   337     
   337 
   338     def __init__(self, name, type, clazz, ttl, address):
   338     def __init__(self, name, type, clazz, ttl, address):
   339         DNSRecord.__init__(self, name, type, clazz, ttl)
   339         DNSRecord.__init__(self, name, type, clazz, ttl)
   340         self.address = address
   340         self.address = address
   341 
   341 
   342     def write(self, out):
   342     def write(self, out):
   376         return 0
   376         return 0
   377 
   377 
   378     def __repr__(self):
   378     def __repr__(self):
   379         """String representation"""
   379         """String representation"""
   380         return self.cpu + " " + self.os
   380         return self.cpu + " " + self.os
   381     
   381 
   382 class DNSPointer(DNSRecord):
   382 class DNSPointer(DNSRecord):
   383     """A DNS pointer record"""
   383     """A DNS pointer record"""
   384     
   384 
   385     def __init__(self, name, type, clazz, ttl, alias):
   385     def __init__(self, name, type, clazz, ttl, alias):
   386         DNSRecord.__init__(self, name, type, clazz, ttl)
   386         DNSRecord.__init__(self, name, type, clazz, ttl)
   387         self.alias = alias
   387         self.alias = alias
   388 
   388 
   389     def write(self, out):
   389     def write(self, out):
   400         """String representation"""
   400         """String representation"""
   401         return self.toString(self.alias)
   401         return self.toString(self.alias)
   402 
   402 
   403 class DNSText(DNSRecord):
   403 class DNSText(DNSRecord):
   404     """A DNS text record"""
   404     """A DNS text record"""
   405     
   405 
   406     def __init__(self, name, type, clazz, ttl, text):
   406     def __init__(self, name, type, clazz, ttl, text):
   407         DNSRecord.__init__(self, name, type, clazz, ttl)
   407         DNSRecord.__init__(self, name, type, clazz, ttl)
   408         self.text = text
   408         self.text = text
   409 
   409 
   410     def write(self, out):
   410     def write(self, out):
   424         else:
   424         else:
   425             return self.toString(self.text)
   425             return self.toString(self.text)
   426 
   426 
   427 class DNSService(DNSRecord):
   427 class DNSService(DNSRecord):
   428     """A DNS service record"""
   428     """A DNS service record"""
   429     
   429 
   430     def __init__(self, name, type, clazz, ttl, priority, weight, port, server):
   430     def __init__(self, name, type, clazz, ttl, priority, weight, port, server):
   431         DNSRecord.__init__(self, name, type, clazz, ttl)
   431         DNSRecord.__init__(self, name, type, clazz, ttl)
   432         self.priority = priority
   432         self.priority = priority
   433         self.weight = weight
   433         self.weight = weight
   434         self.port = port
   434         self.port = port
   451         """String representation"""
   451         """String representation"""
   452         return self.toString("%s:%s" % (self.server, self.port))
   452         return self.toString("%s:%s" % (self.server, self.port))
   453 
   453 
   454 class DNSIncoming(object):
   454 class DNSIncoming(object):
   455     """Object representation of an incoming DNS packet"""
   455     """Object representation of an incoming DNS packet"""
   456     
   456 
   457     def __init__(self, data):
   457     def __init__(self, data):
   458         """Constructor from string holding bytes of packet"""
   458         """Constructor from string holding bytes of packet"""
   459         self.offset = 0
   459         self.offset = 0
   460         self.data = data
   460         self.data = data
   461         self.questions = []
   461         self.questions = []
   462         self.answers = []
   462         self.answers = []
   463         self.numQuestions = 0
   463         self.numQuestions = 0
   464         self.numAnswers = 0
   464         self.numAnswers = 0
   465         self.numAuthorities = 0
   465         self.numAuthorities = 0
   466         self.numAdditionals = 0
   466         self.numAdditionals = 0
   467         
   467 
   468         self.readHeader()
   468         self.readHeader()
   469         self.readQuestions()
   469         self.readQuestions()
   470         self.readOthers()
   470         self.readOthers()
   471 
   471 
   472     def readHeader(self):
   472     def readHeader(self):
   489         length = struct.calcsize(format)
   489         length = struct.calcsize(format)
   490         for i in range(0, self.numQuestions):
   490         for i in range(0, self.numQuestions):
   491             name = self.readName()
   491             name = self.readName()
   492             info = struct.unpack(format, self.data[self.offset:self.offset+length])
   492             info = struct.unpack(format, self.data[self.offset:self.offset+length])
   493             self.offset += length
   493             self.offset += length
   494             
   494 
   495             question = DNSQuestion(name, info[0], info[1])
   495             question = DNSQuestion(name, info[0], info[1])
   496             self.questions.append(question)
   496             self.questions.append(question)
   497 
   497 
   498     def readInt(self):
   498     def readInt(self):
   499         """Reads an integer from the packet"""
   499         """Reads an integer from the packet"""
   559                 #raise BadTypeInNameException
   559                 #raise BadTypeInNameException
   560                 pass
   560                 pass
   561 
   561 
   562             if rec is not None:
   562             if rec is not None:
   563                 self.answers.append(rec)
   563                 self.answers.append(rec)
   564                 
   564 
   565     def isQuery(self):
   565     def isQuery(self):
   566         """Returns true if this is a query"""
   566         """Returns true if this is a query"""
   567         return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_QUERY
   567         return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_QUERY
   568 
   568 
   569     def isResponse(self):
   569     def isResponse(self):
   572 
   572 
   573     def readUTF(self, offset, len):
   573     def readUTF(self, offset, len):
   574         """Reads a UTF-8 string of a given length from the packet"""
   574         """Reads a UTF-8 string of a given length from the packet"""
   575         result = self.data[offset:offset+len].decode('utf-8')
   575         result = self.data[offset:offset+len].decode('utf-8')
   576         return result
   576         return result
   577         
   577 
   578     def readName(self):
   578     def readName(self):
   579         """Reads a domain name from the packet"""
   579         """Reads a domain name from the packet"""
   580         result = ''
   580         result = ''
   581         off = self.offset
   581         off = self.offset
   582         next = -1
   582         next = -1
   605             self.offset = next
   605             self.offset = next
   606         else:
   606         else:
   607             self.offset = off
   607             self.offset = off
   608 
   608 
   609         return result
   609         return result
   610     
   610 
   611         
   611 
   612 class DNSOutgoing(object):
   612 class DNSOutgoing(object):
   613     """Object representation of an outgoing packet"""
   613     """Object representation of an outgoing packet"""
   614     
   614 
   615     def __init__(self, flags, multicast = 1):
   615     def __init__(self, flags, multicast = 1):
   616         self.finished = 0
   616         self.finished = 0
   617         self.id = 0
   617         self.id = 0
   618         self.multicast = multicast
   618         self.multicast = multicast
   619         self.flags = flags
   619         self.flags = flags
   620         self.names = {}
   620         self.names = {}
   621         self.data = []
   621         self.data = []
   622         self.size = 12
   622         self.size = 12
   623         
   623 
   624         self.questions = []
   624         self.questions = []
   625         self.answers = []
   625         self.answers = []
   626         self.authorities = []
   626         self.authorities = []
   627         self.additionals = []
   627         self.additionals = []
   628 
   628 
   658     def insertShort(self, index, value):
   658     def insertShort(self, index, value):
   659         """Inserts an unsigned short in a certain position in the packet"""
   659         """Inserts an unsigned short in a certain position in the packet"""
   660         format = '!H'
   660         format = '!H'
   661         self.data.insert(index, struct.pack(format, value))
   661         self.data.insert(index, struct.pack(format, value))
   662         self.size += 2
   662         self.size += 2
   663         
   663 
   664     def writeShort(self, value):
   664     def writeShort(self, value):
   665         """Writes an unsigned short to the packet"""
   665         """Writes an unsigned short to the packet"""
   666         format = '!H'
   666         format = '!H'
   667         self.data.append(struct.pack(format, value))
   667         self.data.append(struct.pack(format, value))
   668         self.size += 2
   668         self.size += 2
   737         # Adjust size for the short we will write before this record
   737         # Adjust size for the short we will write before this record
   738         #
   738         #
   739         self.size += 2
   739         self.size += 2
   740         record.write(self)
   740         record.write(self)
   741         self.size -= 2
   741         self.size -= 2
   742         
   742 
   743         length = len(''.join(self.data[index:]))
   743         length = len(''.join(self.data[index:]))
   744         self.insertShort(index, length) # Here is the short we adjusted for
   744         self.insertShort(index, length) # Here is the short we adjusted for
   745 
   745 
   746     def packet(self):
   746     def packet(self):
   747         """Returns a string containing the packet's bytes
   747         """Returns a string containing the packet's bytes
   756                 self.writeRecord(answer, time)
   756                 self.writeRecord(answer, time)
   757             for authority in self.authorities:
   757             for authority in self.authorities:
   758                 self.writeRecord(authority, 0)
   758                 self.writeRecord(authority, 0)
   759             for additional in self.additionals:
   759             for additional in self.additionals:
   760                 self.writeRecord(additional, 0)
   760                 self.writeRecord(additional, 0)
   761         
   761 
   762             self.insertShort(0, len(self.additionals))
   762             self.insertShort(0, len(self.additionals))
   763             self.insertShort(0, len(self.authorities))
   763             self.insertShort(0, len(self.authorities))
   764             self.insertShort(0, len(self.answers))
   764             self.insertShort(0, len(self.answers))
   765             self.insertShort(0, len(self.questions))
   765             self.insertShort(0, len(self.questions))
   766             self.insertShort(0, self.flags)
   766             self.insertShort(0, self.flags)
   771         return ''.join(self.data)
   771         return ''.join(self.data)
   772 
   772 
   773 
   773 
   774 class DNSCache(object):
   774 class DNSCache(object):
   775     """A cache of DNS entries"""
   775     """A cache of DNS entries"""
   776     
   776 
   777     def __init__(self):
   777     def __init__(self):
   778         self.cache = {}
   778         self.cache = {}
   779 
   779 
   780     def add(self, entry):
   780     def add(self, entry):
   781         """Adds an entry"""
   781         """Adds an entry"""
   870         result = []
   870         result = []
   871         self.condition.acquire()
   871         self.condition.acquire()
   872         result = self.readers.keys()
   872         result = self.readers.keys()
   873         self.condition.release()
   873         self.condition.release()
   874         return result
   874         return result
   875     
   875 
   876     def addReader(self, reader, socket):
   876     def addReader(self, reader, socket):
   877         self.condition.acquire()
   877         self.condition.acquire()
   878         self.readers[socket] = reader
   878         self.readers[socket] = reader
   879         self.condition.notify()
   879         self.condition.notify()
   880         self.condition.release()
   880         self.condition.release()
   895     group to which DNS messages are sent, allowing the implementation
   895     group to which DNS messages are sent, allowing the implementation
   896     to cache information as it arrives.
   896     to cache information as it arrives.
   897 
   897 
   898     It requires registration with an Engine object in order to have
   898     It requires registration with an Engine object in order to have
   899     the read() method called when a socket is availble for reading."""
   899     the read() method called when a socket is availble for reading."""
   900     
   900 
   901     def __init__(self, zeroconf):
   901     def __init__(self, zeroconf):
   902         self.zeroconf = zeroconf
   902         self.zeroconf = zeroconf
   903         self.zeroconf.engine.addReader(self, self.zeroconf.socket)
   903         self.zeroconf.engine.addReader(self, self.zeroconf.socket)
   904 
   904 
   905     def handle_read(self):
   905     def handle_read(self):
   922 
   922 
   923 
   923 
   924 class Reaper(threading.Thread):
   924 class Reaper(threading.Thread):
   925     """A Reaper is used by this module to remove cache entries that
   925     """A Reaper is used by this module to remove cache entries that
   926     have expired."""
   926     have expired."""
   927     
   927 
   928     def __init__(self, zeroconf):
   928     def __init__(self, zeroconf):
   929         threading.Thread.__init__(self)
   929         threading.Thread.__init__(self)
   930         self.zeroconf = zeroconf
   930         self.zeroconf = zeroconf
   931         self.start()
   931         self.start()
   932 
   932 
   946     """Used to browse for a service of a specific type.
   946     """Used to browse for a service of a specific type.
   947 
   947 
   948     The listener object will have its addService() and
   948     The listener object will have its addService() and
   949     removeService() methods called when this browser
   949     removeService() methods called when this browser
   950     discovers changes in the services availability."""
   950     discovers changes in the services availability."""
   951     
   951 
   952     def __init__(self, zeroconf, type, listener):
   952     def __init__(self, zeroconf, type, listener):
   953         """Creates a browser for a specific type"""
   953         """Creates a browser for a specific type"""
   954         threading.Thread.__init__(self)
   954         threading.Thread.__init__(self)
   955         self.zeroconf = zeroconf
   955         self.zeroconf = zeroconf
   956         self.type = type
   956         self.type = type
   957         self.listener = listener
   957         self.listener = listener
   958         self.services = {}
   958         self.services = {}
   959         self.nextTime = currentTimeMillis()
   959         self.nextTime = currentTimeMillis()
   960         self.delay = _BROWSER_TIME
   960         self.delay = _BROWSER_TIME
   961         self.list = []
   961         self.list = []
   962         
   962 
   963         self.done = 0
   963         self.done = 0
   964 
   964 
   965         self.zeroconf.addListener(self, DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN))
   965         self.zeroconf.addListener(self, DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN))
   966         self.start()
   966         self.start()
   967 
   967 
  1017             if len(self.list) > 0:
  1017             if len(self.list) > 0:
  1018                 event = self.list.pop(0)
  1018                 event = self.list.pop(0)
  1019 
  1019 
  1020             if event is not None:
  1020             if event is not None:
  1021                 event(self.zeroconf)
  1021                 event(self.zeroconf)
  1022                 
  1022 
  1023 
  1023 
  1024 class ServiceInfo(object):
  1024 class ServiceInfo(object):
  1025     """Service information"""
  1025     """Service information"""
  1026     
  1026 
  1027     def __init__(self, type, name, address=None, port=None, weight=0, priority=0, properties=None, server=None):
  1027     def __init__(self, type, name, address=None, port=None, weight=0, priority=0, properties=None, server=None):
  1028         """Create a service description.
  1028         """Create a service description.
  1029 
  1029 
  1030         type: fully qualified service type name
  1030         type: fully qualified service type name
  1031         name: fully qualified service name
  1031         name: fully qualified service name
  1087             while index < end:
  1087             while index < end:
  1088                 length = ord(text[index])
  1088                 length = ord(text[index])
  1089                 index += 1
  1089                 index += 1
  1090                 strs.append(text[index:index+length])
  1090                 strs.append(text[index:index+length])
  1091                 index += length
  1091                 index += length
  1092             
  1092 
  1093             for s in strs:
  1093             for s in strs:
  1094                 eindex = s.find('=')
  1094                 eindex = s.find('=')
  1095                 if eindex == -1:
  1095                 if eindex == -1:
  1096                     # No equals sign at all
  1096                     # No equals sign at all
  1097                     key = s
  1097                     key = s
  1110 
  1110 
  1111             self.properties = result
  1111             self.properties = result
  1112         except:
  1112         except:
  1113             traceback.print_exc()
  1113             traceback.print_exc()
  1114             self.properties = None
  1114             self.properties = None
  1115             
  1115 
  1116     def getType(self):
  1116     def getType(self):
  1117         """Type accessor"""
  1117         """Type accessor"""
  1118         return self.type
  1118         return self.type
  1119 
  1119 
  1120     def getName(self):
  1120     def getName(self):
  1198                 zeroconf.wait(min(next, last) - now)
  1198                 zeroconf.wait(min(next, last) - now)
  1199                 now = currentTimeMillis()
  1199                 now = currentTimeMillis()
  1200             result = 1
  1200             result = 1
  1201         finally:
  1201         finally:
  1202             zeroconf.removeListener(self)
  1202             zeroconf.removeListener(self)
  1203             
  1203 
  1204         return result
  1204         return result
  1205 
  1205 
  1206     def __eq__(self, other):
  1206     def __eq__(self, other):
  1207         """Tests equality of service name"""
  1207         """Tests equality of service name"""
  1208         if isinstance(other, ServiceInfo):
  1208         if isinstance(other, ServiceInfo):
  1223                 result += self.text
  1223                 result += self.text
  1224             else:
  1224             else:
  1225                 result += self.text[:17] + "..."
  1225                 result += self.text[:17] + "..."
  1226         result += "]"
  1226         result += "]"
  1227         return result
  1227         return result
  1228                 
  1228 
  1229 
  1229 
  1230 class Zeroconf(object):
  1230 class Zeroconf(object):
  1231     """Implementation of Zeroconf Multicast DNS Service Discovery
  1231     """Implementation of Zeroconf Multicast DNS Service Discovery
  1232 
  1232 
  1233     Supports registration, unregistration, queries and browsing.
  1233     Supports registration, unregistration, queries and browsing.
  1272         self.services = {}
  1272         self.services = {}
  1273 
  1273 
  1274         self.cache = DNSCache()
  1274         self.cache = DNSCache()
  1275 
  1275 
  1276         self.condition = threading.Condition()
  1276         self.condition = threading.Condition()
  1277         
  1277 
  1278         self.engine = Engine(self)
  1278         self.engine = Engine(self)
  1279         self.listener = Listener(self)
  1279         self.listener = Listener(self)
  1280         self.reaper = Reaper(self)
  1280         self.reaper = Reaper(self)
  1281 
  1281 
  1282     def isLoopback(self):
  1282     def isLoopback(self):
  1463                     if entry is not None:
  1463                     if entry is not None:
  1464                         entry.resetTTL(record)
  1464                         entry.resetTTL(record)
  1465                         record = entry
  1465                         record = entry
  1466             else:
  1466             else:
  1467                 self.cache.add(record)
  1467                 self.cache.add(record)
  1468                 
  1468 
  1469             self.updateRecord(now, record)
  1469             self.updateRecord(now, record)
  1470 
  1470 
  1471     def handleQuery(self, msg, addr, port):
  1471     def handleQuery(self, msg, addr, port):
  1472         """Deal with incoming query packets.  Provides a response if
  1472         """Deal with incoming query packets.  Provides a response if
  1473         possible."""
  1473         possible."""
  1477         #
  1477         #
  1478         if port != _MDNS_PORT:
  1478         if port != _MDNS_PORT:
  1479             out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA, 0)
  1479             out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA, 0)
  1480             for question in msg.questions:
  1480             for question in msg.questions:
  1481                 out.addQuestion(question)
  1481                 out.addQuestion(question)
  1482         
  1482 
  1483         for question in msg.questions:
  1483         for question in msg.questions:
  1484             if question.type == _TYPE_PTR:
  1484             if question.type == _TYPE_PTR:
  1485                 for service in self.services.values():
  1485                 for service in self.services.values():
  1486                     if question.name == service.type:
  1486                     if question.name == service.type:
  1487                         if out is None:
  1487                         if out is None:
  1489                         out.addAnswer(msg, DNSPointer(service.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, service.name))
  1489                         out.addAnswer(msg, DNSPointer(service.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, service.name))
  1490             else:
  1490             else:
  1491                 try:
  1491                 try:
  1492                     if out is None:
  1492                     if out is None:
  1493                         out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA)
  1493                         out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA)
  1494                     
  1494 
  1495                     # Answer A record queries for any service addresses we know
  1495                     # Answer A record queries for any service addresses we know
  1496                     if question.type == _TYPE_A or question.type == _TYPE_ANY:
  1496                     if question.type == _TYPE_A or question.type == _TYPE_ANY:
  1497                         for service in self.services.values():
  1497                         for service in self.services.values():
  1498                             if service.server == question.name.lower():
  1498                             if service.server == question.name.lower():
  1499                                 out.addAnswer(msg, DNSAddress(question.name, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address))
  1499                                 out.addAnswer(msg, DNSAddress(question.name, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address))
  1500                     
  1500 
  1501                     service = self.services.get(question.name.lower(), None)
  1501                     service = self.services.get(question.name.lower(), None)
  1502                     if not service: continue
  1502                     if not service: continue
  1503                     
  1503 
  1504                     if question.type == _TYPE_SRV or question.type == _TYPE_ANY:
  1504                     if question.type == _TYPE_SRV or question.type == _TYPE_ANY:
  1505                         out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.priority, service.weight, service.port, service.server))
  1505                         out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.priority, service.weight, service.port, service.server))
  1506                     if question.type == _TYPE_TXT or question.type == _TYPE_ANY:
  1506                     if question.type == _TYPE_TXT or question.type == _TYPE_ANY:
  1507                         out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text))
  1507                         out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text))
  1508                     if question.type == _TYPE_SRV:
  1508                     if question.type == _TYPE_SRV:
  1509                         out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address))
  1509                         out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address))
  1510                 except:
  1510                 except:
  1511                     traceback.print_exc()
  1511                     traceback.print_exc()
  1512                 
  1512 
  1513         if out is not None and out.answers:
  1513         if out is not None and out.answers:
  1514             out.id = msg.id
  1514             out.id = msg.id
  1515             self.send(out, addr, port)
  1515             self.send(out, addr, port)
  1516 
  1516 
  1517     def send(self, out, addr = _MDNS_ADDR, port = _MDNS_PORT):
  1517     def send(self, out, addr = _MDNS_ADDR, port = _MDNS_PORT):
  1532             self.notifyAll()
  1532             self.notifyAll()
  1533             self.engine.notify()
  1533             self.engine.notify()
  1534             self.unregisterAllServices()
  1534             self.unregisterAllServices()
  1535             self.socket.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0'))
  1535             self.socket.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0'))
  1536             self.socket.close()
  1536             self.socket.close()
  1537             
  1537 
  1538 # Test a few module features, including service registration, service
  1538 # Test a few module features, including service registration, service
  1539 # query (for Zoe), and service unregistration.
  1539 # query (for Zoe), and service unregistration.
  1540 
  1540 
  1541 if __name__ == '__main__':    
  1541 if __name__ == '__main__':
  1542     print "Multicast DNS Service Discovery for Python, version", __version__
  1542     print "Multicast DNS Service Discovery for Python, version", __version__
  1543     r = Zeroconf()
  1543     r = Zeroconf()
  1544     print "1. Testing registration of a service..."
  1544     print "1. Testing registration of a service..."
  1545     desc = {'version':'0.10','a':'test value', 'b':'another value'}
  1545     desc = {'version':'0.10','a':'test value', 'b':'another value'}
  1546     info = ServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.", socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc)
  1546     info = ServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.", socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc)