239 |
239 |
240 def getClazz(self, clazz): |
240 def getClazz(self, clazz): |
241 """Class accessor""" |
241 """Class accessor""" |
242 try: |
242 try: |
243 return _CLASSES[clazz] |
243 return _CLASSES[clazz] |
244 except: |
244 except Exception: |
245 return "?(%s)" % (clazz) |
245 return "?(%s)" % (clazz) |
246 |
246 |
247 def getType(self, type): |
247 def getType(self, type): |
248 """Type accessor""" |
248 """Type accessor""" |
249 try: |
249 try: |
250 return _TYPES[type] |
250 return _TYPES[type] |
251 except: |
251 except Exception: |
252 return "?(%s)" % (type) |
252 return "?(%s)" % (type) |
253 |
253 |
254 def toString(self, hdr, other): |
254 def toString(self, hdr, other): |
255 """String representation with additional information""" |
255 """String representation with additional information""" |
256 result = "%s[%s,%s" % (hdr, self.getType(self.type), self.getClazz(self.clazz)) |
256 result = "%s[%s,%s" % (hdr, self.getType(self.type), self.getClazz(self.clazz)) |
797 |
797 |
798 def add(self, entry): |
798 def add(self, entry): |
799 """Adds an entry""" |
799 """Adds an entry""" |
800 try: |
800 try: |
801 list = self.cache[entry.key] |
801 list = self.cache[entry.key] |
802 except: |
802 except Exception: |
803 list = self.cache[entry.key] = [] |
803 list = self.cache[entry.key] = [] |
804 list.append(entry) |
804 list.append(entry) |
805 |
805 |
806 def remove(self, entry): |
806 def remove(self, entry): |
807 """Removes an entry""" |
807 """Removes an entry""" |
808 try: |
808 try: |
809 list = self.cache[entry.key] |
809 list = self.cache[entry.key] |
810 list.remove(entry) |
810 list.remove(entry) |
811 except: |
811 except Exception: |
812 pass |
812 pass |
813 |
813 |
814 def get(self, entry): |
814 def get(self, entry): |
815 """Gets an entry by key. Will return None if there is no |
815 """Gets an entry by key. Will return None if there is no |
816 matching entry.""" |
816 matching entry.""" |
817 try: |
817 try: |
818 list = self.cache[entry.key] |
818 list = self.cache[entry.key] |
819 return list[list.index(entry)] |
819 return list[list.index(entry)] |
820 except: |
820 except Exception: |
821 return None |
821 return None |
822 |
822 |
823 def getByDetails(self, name, type, clazz): |
823 def getByDetails(self, name, type, clazz): |
824 """Gets an entry by details. Will return None if there is |
824 """Gets an entry by details. Will return None if there is |
825 no matching entry.""" |
825 no matching entry.""" |
828 |
828 |
829 def entriesWithName(self, name): |
829 def entriesWithName(self, name): |
830 """Returns a list of entries whose key matches the name.""" |
830 """Returns a list of entries whose key matches the name.""" |
831 try: |
831 try: |
832 return self.cache[name] |
832 return self.cache[name] |
833 except: |
833 except Exception: |
834 return [] |
834 return [] |
835 |
835 |
836 def entries(self): |
836 def entries(self): |
837 """Returns a list of all entries""" |
837 """Returns a list of all entries""" |
838 def add(x, y): return x+y |
838 def add(x, y): return x+y |
839 try: |
839 try: |
840 return reduce(add, self.cache.values()) |
840 return reduce(add, self.cache.values()) |
841 except: |
841 except Exception: |
842 return [] |
842 return [] |
843 |
843 |
844 |
844 |
845 class Engine(threading.Thread): |
845 class Engine(threading.Thread): |
846 """An engine wraps read access to sockets, allowing objects that |
846 """An engine wraps read access to sockets, allowing objects that |
876 try: |
876 try: |
877 rr, wr, er = select.select(rs, [], [], self.timeout) |
877 rr, wr, er = select.select(rs, [], [], self.timeout) |
878 for socket in rr: |
878 for socket in rr: |
879 try: |
879 try: |
880 self.readers[socket].handle_read() |
880 self.readers[socket].handle_read() |
881 except: |
881 except Exception: |
882 # Ignore errors that occur on shutdown |
882 # Ignore errors that occur on shutdown |
883 pass |
883 pass |
884 except: |
884 except Exception: |
885 pass |
885 pass |
886 |
886 |
887 def getReaders(self): |
887 def getReaders(self): |
888 result = [] |
888 result = [] |
889 self.condition.acquire() |
889 self.condition.acquire() |
998 def callback(x): |
998 def callback(x): |
999 return self.listener.removeService(x, self.type, record.alias) |
999 return self.listener.removeService(x, self.type, record.alias) |
1000 del(self.services[record.alias.lower()]) |
1000 del(self.services[record.alias.lower()]) |
1001 self.list.append(callback) |
1001 self.list.append(callback) |
1002 return |
1002 return |
1003 except: |
1003 except Exception: |
1004 if not expired: |
1004 if not expired: |
1005 def callback(x): |
1005 def callback(x): |
1006 return self.listener.addService(x, self.type, record.alias) |
1006 return self.listener.addService(x, self.type, record.alias) |
1007 self.services[record.alias.lower()] = record |
1007 self.services[record.alias.lower()] = record |
1008 self.list.append(callback) |
1008 self.list.append(callback) |
1261 self.group = ('', _MDNS_PORT) |
1261 self.group = ('', _MDNS_PORT) |
1262 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
1262 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
1263 try: |
1263 try: |
1264 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
1264 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
1265 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) |
1265 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) |
1266 except: |
1266 except Exception: |
1267 # SO_REUSEADDR should be equivalent to SO_REUSEPORT for |
1267 # SO_REUSEADDR should be equivalent to SO_REUSEPORT for |
1268 # multicast UDP sockets (p 731, "TCP/IP Illustrated, |
1268 # multicast UDP sockets (p 731, "TCP/IP Illustrated, |
1269 # Volume 2"), but some BSD-derived systems require |
1269 # Volume 2"), but some BSD-derived systems require |
1270 # SO_REUSEPORT to be specified explicity. Also, not all |
1270 # SO_REUSEPORT to be specified explicity. Also, not all |
1271 # versions of Python have SO_REUSEPORT available. So |
1271 # versions of Python have SO_REUSEPORT available. So |
1276 pass |
1276 pass |
1277 self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) |
1277 self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) |
1278 self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) |
1278 self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) |
1279 try: |
1279 try: |
1280 self.socket.bind(self.group) |
1280 self.socket.bind(self.group) |
1281 except: |
1281 except Exception: |
1282 # Some versions of linux raise an exception even though |
1282 # Some versions of linux raise an exception even though |
1283 # the SO_REUSE* options have been set, so ignore it |
1283 # the SO_REUSE* options have been set, so ignore it |
1284 # |
1284 # |
1285 pass |
1285 pass |
1286 |
1286 |
1458 def removeListener(self, listener): |
1458 def removeListener(self, listener): |
1459 """Removes a listener.""" |
1459 """Removes a listener.""" |
1460 try: |
1460 try: |
1461 self.listeners.remove(listener) |
1461 self.listeners.remove(listener) |
1462 self.notifyAll() |
1462 self.notifyAll() |
1463 except: |
1463 except Exception: |
1464 pass |
1464 pass |
1465 |
1465 |
1466 def updateRecord(self, now, rec): |
1466 def updateRecord(self, now, rec): |
1467 """Used to notify listeners of new information that has updated |
1467 """Used to notify listeners of new information that has updated |
1468 a record.""" |
1468 a record.""" |
1527 out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.priority, service.weight, service.port, service.server)) |
1527 out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.priority, service.weight, service.port, service.server)) |
1528 if question.type == _TYPE_TXT or question.type == _TYPE_ANY: |
1528 if question.type == _TYPE_TXT or question.type == _TYPE_ANY: |
1529 out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text)) |
1529 out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text)) |
1530 if question.type == _TYPE_SRV: |
1530 if question.type == _TYPE_SRV: |
1531 out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) |
1531 out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) |
1532 except: |
1532 except Exception: |
1533 traceback.print_exc() |
1533 traceback.print_exc() |
1534 |
1534 |
1535 if out is not None and out.answers: |
1535 if out is not None and out.answers: |
1536 out.id = msg.id |
1536 out.id = msg.id |
1537 self.send(out, addr, port) |
1537 self.send(out, addr, port) |
1540 """Sends an outgoing packet.""" |
1540 """Sends an outgoing packet.""" |
1541 # This is a quick test to see if we can parse the packets we generate |
1541 # This is a quick test to see if we can parse the packets we generate |
1542 #temp = DNSIncoming(out.packet()) |
1542 #temp = DNSIncoming(out.packet()) |
1543 try: |
1543 try: |
1544 bytes_sent = self.socket.sendto(out.packet(), 0, (addr, port)) |
1544 bytes_sent = self.socket.sendto(out.packet(), 0, (addr, port)) |
1545 except: |
1545 except Exception: |
1546 # Ignore this, it may be a temporary loss of network connection |
1546 # Ignore this, it may be a temporary loss of network connection |
1547 pass |
1547 pass |
1548 |
1548 |
1549 def close(self): |
1549 def close(self): |
1550 """Ends the background threads, and prevent this instance from |
1550 """Ends the background threads, and prevent this instance from |