python-zeroconf
changeset 1:ed4f4e7ad09b
convert tabs to spaces
| author | Alexander Solovyov <piranha@piranha.org.ua> |
|---|---|
| date | Mon Mar 23 15:55:51 2009 +0200 |
| parents | 72b67d13900b |
| children | d9eeb67de097 |
| files | Zeroconf.py |
| diffstat | 1 files changed, 1204 insertions(+), 1204 deletions(-) [+] |
line diff
1.1 --- a/Zeroconf.py Mon Mar 23 15:02:44 2009 +0200 1.2 +++ b/Zeroconf.py Mon Mar 23 15:55:51 2009 +0200 1.3 @@ -23,29 +23,29 @@ 1.4 """ 1.5 1.6 """0.12 update - allow selection of binding interface 1.7 - typo fix - Thanks A. M. Kuchlingi 1.8 - removed all use of word 'Rendezvous' - this is an API change""" 1.9 + typo fix - Thanks A. M. Kuchlingi 1.10 + removed all use of word 'Rendezvous' - this is an API change""" 1.11 1.12 """0.11 update - correction to comments for addListener method 1.13 support for new record types seen from OS X 1.14 - - IPv6 address 1.15 - - hostinfo 1.16 - ignore unknown DNS record types 1.17 - fixes to name decoding 1.18 - works alongside other processes using port 5353 (e.g. on Mac OS X) 1.19 - tested against Mac OS X 10.3.2's mDNSResponder 1.20 - corrections to removal of list entries for service browser""" 1.21 + - IPv6 address 1.22 + - hostinfo 1.23 + ignore unknown DNS record types 1.24 + fixes to name decoding 1.25 + works alongside other processes using port 5353 (e.g. on Mac OS X) 1.26 + tested against Mac OS X 10.3.2's mDNSResponder 1.27 + corrections to removal of list entries for service browser""" 1.28 1.29 """0.10 update - Jonathon Paisley contributed these corrections: 1.30 always multicast replies, even when query is unicast 1.31 - correct a pointer encoding problem 1.32 - can now write records in any order 1.33 - traceback shown on failure 1.34 - better TXT record parsing 1.35 - server is now separate from name 1.36 - can cancel a service browser 1.37 + correct a pointer encoding problem 1.38 + can now write records in any order 1.39 + traceback shown on failure 1.40 + better TXT record parsing 1.41 + server is now separate from name 1.42 + can cancel a service browser 1.43 1.44 - modified some unit tests to accommodate these changes""" 1.45 + modified some unit tests to accommodate these changes""" 1.46 1.47 """0.09 update - remove all records on service unregistration 1.48 fix DOS security problem with readName""" 1.49 @@ -54,25 +54,25 @@ 1.50 1.51 """0.07 update - faster shutdown on engine 1.52 pointer encoding of outgoing names 1.53 - ServiceBrowser now works 1.54 - new unit tests""" 1.55 + ServiceBrowser now works 1.56 + new unit tests""" 1.57 1.58 """0.06 update - small improvements with unit tests 1.59 added defined exception types 1.60 - new style objects 1.61 - fixed hostname/interface problem 1.62 - fixed socket timeout problem 1.63 - fixed addServiceListener() typo bug 1.64 - using select() for socket reads 1.65 - tested on Debian unstable with Python 2.2.2""" 1.66 + new style objects 1.67 + fixed hostname/interface problem 1.68 + fixed socket timeout problem 1.69 + fixed addServiceListener() typo bug 1.70 + using select() for socket reads 1.71 + tested on Debian unstable with Python 2.2.2""" 1.72 1.73 """0.05 update - ensure case insensitivty on domain names 1.74 support for unicast DNS queries""" 1.75 1.76 """0.04 update - added some unit tests 1.77 added __ne__ adjuncts where required 1.78 - ensure names end in '.local.' 1.79 - timeout on receiving socket for clean shutdown""" 1.80 + ensure names end in '.local.' 1.81 + timeout on receiving socket for clean shutdown""" 1.82 1.83 __author__ = "Paul Scott-Murphy" 1.84 __email__ = "paul at scott dash murphy dot com" 1.85 @@ -155,1416 +155,1416 @@ 1.86 # Mapping constants to names 1.87 1.88 _CLASSES = { _CLASS_IN : "in", 1.89 - _CLASS_CS : "cs", 1.90 - _CLASS_CH : "ch", 1.91 - _CLASS_HS : "hs", 1.92 - _CLASS_NONE : "none", 1.93 - _CLASS_ANY : "any" } 1.94 + _CLASS_CS : "cs", 1.95 + _CLASS_CH : "ch", 1.96 + _CLASS_HS : "hs", 1.97 + _CLASS_NONE : "none", 1.98 + _CLASS_ANY : "any" } 1.99 1.100 _TYPES = { _TYPE_A : "a", 1.101 - _TYPE_NS : "ns", 1.102 - _TYPE_MD : "md", 1.103 - _TYPE_MF : "mf", 1.104 - _TYPE_CNAME : "cname", 1.105 - _TYPE_SOA : "soa", 1.106 - _TYPE_MB : "mb", 1.107 - _TYPE_MG : "mg", 1.108 - _TYPE_MR : "mr", 1.109 - _TYPE_NULL : "null", 1.110 - _TYPE_WKS : "wks", 1.111 - _TYPE_PTR : "ptr", 1.112 - _TYPE_HINFO : "hinfo", 1.113 - _TYPE_MINFO : "minfo", 1.114 - _TYPE_MX : "mx", 1.115 - _TYPE_TXT : "txt", 1.116 - _TYPE_AAAA : "quada", 1.117 - _TYPE_SRV : "srv", 1.118 - _TYPE_ANY : "any" } 1.119 + _TYPE_NS : "ns", 1.120 + _TYPE_MD : "md", 1.121 + _TYPE_MF : "mf", 1.122 + _TYPE_CNAME : "cname", 1.123 + _TYPE_SOA : "soa", 1.124 + _TYPE_MB : "mb", 1.125 + _TYPE_MG : "mg", 1.126 + _TYPE_MR : "mr", 1.127 + _TYPE_NULL : "null", 1.128 + _TYPE_WKS : "wks", 1.129 + _TYPE_PTR : "ptr", 1.130 + _TYPE_HINFO : "hinfo", 1.131 + _TYPE_MINFO : "minfo", 1.132 + _TYPE_MX : "mx", 1.133 + _TYPE_TXT : "txt", 1.134 + _TYPE_AAAA : "quada", 1.135 + _TYPE_SRV : "srv", 1.136 + _TYPE_ANY : "any" } 1.137 1.138 # utility functions 1.139 1.140 def currentTimeMillis(): 1.141 - """Current system time in milliseconds""" 1.142 - return time.time() * 1000 1.143 + """Current system time in milliseconds""" 1.144 + return time.time() * 1000 1.145 1.146 # Exceptions 1.147 1.148 class NonLocalNameException(Exception): 1.149 - pass 1.150 + pass 1.151 1.152 class NonUniqueNameException(Exception): 1.153 - pass 1.154 + pass 1.155 1.156 class NamePartTooLongException(Exception): 1.157 - pass 1.158 + pass 1.159 1.160 class AbstractMethodException(Exception): 1.161 - pass 1.162 + pass 1.163 1.164 class BadTypeInNameException(Exception): 1.165 - pass 1.166 + pass 1.167 1.168 # implementation classes 1.169 1.170 class DNSEntry(object): 1.171 - """A DNS entry""" 1.172 + """A DNS entry""" 1.173 1.174 - def __init__(self, name, type, clazz): 1.175 - self.key = string.lower(name) 1.176 - self.name = name 1.177 - self.type = type 1.178 - self.clazz = clazz & _CLASS_MASK 1.179 - self.unique = (clazz & _CLASS_UNIQUE) != 0 1.180 + def __init__(self, name, type, clazz): 1.181 + self.key = string.lower(name) 1.182 + self.name = name 1.183 + self.type = type 1.184 + self.clazz = clazz & _CLASS_MASK 1.185 + self.unique = (clazz & _CLASS_UNIQUE) != 0 1.186 1.187 - def __eq__(self, other): 1.188 - """Equality test on name, type, and class""" 1.189 - if isinstance(other, DNSEntry): 1.190 - return self.name == other.name and self.type == other.type and self.clazz == other.clazz 1.191 - return 0 1.192 + def __eq__(self, other): 1.193 + """Equality test on name, type, and class""" 1.194 + if isinstance(other, DNSEntry): 1.195 + return self.name == other.name and self.type == other.type and self.clazz == other.clazz 1.196 + return 0 1.197 1.198 - def __ne__(self, other): 1.199 - """Non-equality test""" 1.200 - return not self.__eq__(other) 1.201 + def __ne__(self, other): 1.202 + """Non-equality test""" 1.203 + return not self.__eq__(other) 1.204 1.205 - def getClazz(self, clazz): 1.206 - """Class accessor""" 1.207 - try: 1.208 - return _CLASSES[clazz] 1.209 - except: 1.210 - return "?(%s)" % (clazz) 1.211 + def getClazz(self, clazz): 1.212 + """Class accessor""" 1.213 + try: 1.214 + return _CLASSES[clazz] 1.215 + except: 1.216 + return "?(%s)" % (clazz) 1.217 1.218 - def getType(self, type): 1.219 - """Type accessor""" 1.220 - try: 1.221 - return _TYPES[type] 1.222 - except: 1.223 - return "?(%s)" % (type) 1.224 + def getType(self, type): 1.225 + """Type accessor""" 1.226 + try: 1.227 + return _TYPES[type] 1.228 + except: 1.229 + return "?(%s)" % (type) 1.230 1.231 - def toString(self, hdr, other): 1.232 - """String representation with additional information""" 1.233 - result = "%s[%s,%s" % (hdr, self.getType(self.type), self.getClazz(self.clazz)) 1.234 - if self.unique: 1.235 - result += "-unique," 1.236 - else: 1.237 - result += "," 1.238 - result += self.name 1.239 - if other is not None: 1.240 - result += ",%s]" % (other) 1.241 - else: 1.242 - result += "]" 1.243 - return result 1.244 + def toString(self, hdr, other): 1.245 + """String representation with additional information""" 1.246 + result = "%s[%s,%s" % (hdr, self.getType(self.type), self.getClazz(self.clazz)) 1.247 + if self.unique: 1.248 + result += "-unique," 1.249 + else: 1.250 + result += "," 1.251 + result += self.name 1.252 + if other is not None: 1.253 + result += ",%s]" % (other) 1.254 + else: 1.255 + result += "]" 1.256 + return result 1.257 1.258 class DNSQuestion(DNSEntry): 1.259 - """A DNS question entry""" 1.260 + """A DNS question entry""" 1.261 1.262 - def __init__(self, name, type, clazz): 1.263 - if not name.endswith(".local."): 1.264 - raise NonLocalNameException 1.265 - DNSEntry.__init__(self, name, type, clazz) 1.266 + def __init__(self, name, type, clazz): 1.267 + if not name.endswith(".local."): 1.268 + raise NonLocalNameException 1.269 + DNSEntry.__init__(self, name, type, clazz) 1.270 1.271 - def answeredBy(self, rec): 1.272 - """Returns true if the question is answered by the record""" 1.273 - return self.clazz == rec.clazz and (self.type == rec.type or self.type == _TYPE_ANY) and self.name == rec.name 1.274 + def answeredBy(self, rec): 1.275 + """Returns true if the question is answered by the record""" 1.276 + return self.clazz == rec.clazz and (self.type == rec.type or self.type == _TYPE_ANY) and self.name == rec.name 1.277 1.278 - def __repr__(self): 1.279 - """String representation""" 1.280 - return DNSEntry.toString(self, "question", None) 1.281 + def __repr__(self): 1.282 + """String representation""" 1.283 + return DNSEntry.toString(self, "question", None) 1.284 1.285 1.286 class DNSRecord(DNSEntry): 1.287 - """A DNS record - like a DNS entry, but has a TTL""" 1.288 + """A DNS record - like a DNS entry, but has a TTL""" 1.289 1.290 - def __init__(self, name, type, clazz, ttl): 1.291 - DNSEntry.__init__(self, name, type, clazz) 1.292 - self.ttl = ttl 1.293 - self.created = currentTimeMillis() 1.294 + def __init__(self, name, type, clazz, ttl): 1.295 + DNSEntry.__init__(self, name, type, clazz) 1.296 + self.ttl = ttl 1.297 + self.created = currentTimeMillis() 1.298 1.299 - def __eq__(self, other): 1.300 - """Tests equality as per DNSRecord""" 1.301 - if isinstance(other, DNSRecord): 1.302 - return DNSEntry.__eq__(self, other) 1.303 - return 0 1.304 + def __eq__(self, other): 1.305 + """Tests equality as per DNSRecord""" 1.306 + if isinstance(other, DNSRecord): 1.307 + return DNSEntry.__eq__(self, other) 1.308 + return 0 1.309 1.310 - def suppressedBy(self, msg): 1.311 - """Returns true if any answer in a message can suffice for the 1.312 - information held in this record.""" 1.313 - for record in msg.answers: 1.314 - if self.suppressedByAnswer(record): 1.315 - return 1 1.316 - return 0 1.317 + def suppressedBy(self, msg): 1.318 + """Returns true if any answer in a message can suffice for the 1.319 + information held in this record.""" 1.320 + for record in msg.answers: 1.321 + if self.suppressedByAnswer(record): 1.322 + return 1 1.323 + return 0 1.324 1.325 - def suppressedByAnswer(self, other): 1.326 - """Returns true if another record has same name, type and class, 1.327 - and if its TTL is at least half of this record's.""" 1.328 - if self == other and other.ttl > (self.ttl / 2): 1.329 - return 1 1.330 - return 0 1.331 + def suppressedByAnswer(self, other): 1.332 + """Returns true if another record has same name, type and class, 1.333 + and if its TTL is at least half of this record's.""" 1.334 + if self == other and other.ttl > (self.ttl / 2): 1.335 + return 1 1.336 + return 0 1.337 1.338 - def getExpirationTime(self, percent): 1.339 - """Returns the time at which this record will have expired 1.340 - by a certain percentage.""" 1.341 - return self.created + (percent * self.ttl * 10) 1.342 + def getExpirationTime(self, percent): 1.343 + """Returns the time at which this record will have expired 1.344 + by a certain percentage.""" 1.345 + return self.created + (percent * self.ttl * 10) 1.346 1.347 - def getRemainingTTL(self, now): 1.348 - """Returns the remaining TTL in seconds.""" 1.349 - return max(0, (self.getExpirationTime(100) - now) / 1000) 1.350 + def getRemainingTTL(self, now): 1.351 + """Returns the remaining TTL in seconds.""" 1.352 + return max(0, (self.getExpirationTime(100) - now) / 1000) 1.353 1.354 - def isExpired(self, now): 1.355 - """Returns true if this record has expired.""" 1.356 - return self.getExpirationTime(100) <= now 1.357 + def isExpired(self, now): 1.358 + """Returns true if this record has expired.""" 1.359 + return self.getExpirationTime(100) <= now 1.360 1.361 - def isStale(self, now): 1.362 - """Returns true if this record is at least half way expired.""" 1.363 - return self.getExpirationTime(50) <= now 1.364 + def isStale(self, now): 1.365 + """Returns true if this record is at least half way expired.""" 1.366 + return self.getExpirationTime(50) <= now 1.367 1.368 - def resetTTL(self, other): 1.369 - """Sets this record's TTL and created time to that of 1.370 - another record.""" 1.371 - self.created = other.created 1.372 - self.ttl = other.ttl 1.373 + def resetTTL(self, other): 1.374 + """Sets this record's TTL and created time to that of 1.375 + another record.""" 1.376 + self.created = other.created 1.377 + self.ttl = other.ttl 1.378 1.379 - def write(self, out): 1.380 - """Abstract method""" 1.381 - raise AbstractMethodException 1.382 + def write(self, out): 1.383 + """Abstract method""" 1.384 + raise AbstractMethodException 1.385 1.386 - def toString(self, other): 1.387 - """String representation with addtional information""" 1.388 - arg = "%s/%s,%s" % (self.ttl, self.getRemainingTTL(currentTimeMillis()), other) 1.389 - return DNSEntry.toString(self, "record", arg) 1.390 + def toString(self, other): 1.391 + """String representation with addtional information""" 1.392 + arg = "%s/%s,%s" % (self.ttl, self.getRemainingTTL(currentTimeMillis()), other) 1.393 + return DNSEntry.toString(self, "record", arg) 1.394 1.395 class DNSAddress(DNSRecord): 1.396 - """A DNS address record""" 1.397 + """A DNS address record""" 1.398 1.399 - def __init__(self, name, type, clazz, ttl, address): 1.400 - DNSRecord.__init__(self, name, type, clazz, ttl) 1.401 - self.address = address 1.402 + def __init__(self, name, type, clazz, ttl, address): 1.403 + DNSRecord.__init__(self, name, type, clazz, ttl) 1.404 + self.address = address 1.405 1.406 - def write(self, out): 1.407 - """Used in constructing an outgoing packet""" 1.408 - out.writeString(self.address, len(self.address)) 1.409 + def write(self, out): 1.410 + """Used in constructing an outgoing packet""" 1.411 + out.writeString(self.address, len(self.address)) 1.412 1.413 - def __eq__(self, other): 1.414 - """Tests equality on address""" 1.415 - if isinstance(other, DNSAddress): 1.416 - return self.address == other.address 1.417 - return 0 1.418 + def __eq__(self, other): 1.419 + """Tests equality on address""" 1.420 + if isinstance(other, DNSAddress): 1.421 + return self.address == other.address 1.422 + return 0 1.423 1.424 - def __repr__(self): 1.425 - """String representation""" 1.426 - try: 1.427 - return socket.inet_ntoa(self.address) 1.428 - except: 1.429 - return self.address 1.430 + def __repr__(self): 1.431 + """String representation""" 1.432 + try: 1.433 + return socket.inet_ntoa(self.address) 1.434 + except: 1.435 + return self.address 1.436 1.437 class DNSHinfo(DNSRecord): 1.438 - """A DNS host information record""" 1.439 + """A DNS host information record""" 1.440 1.441 - def __init__(self, name, type, clazz, ttl, cpu, os): 1.442 - DNSRecord.__init__(self, name, type, clazz, ttl) 1.443 - self.cpu = cpu 1.444 - self.os = os 1.445 + def __init__(self, name, type, clazz, ttl, cpu, os): 1.446 + DNSRecord.__init__(self, name, type, clazz, ttl) 1.447 + self.cpu = cpu 1.448 + self.os = os 1.449 1.450 - def write(self, out): 1.451 - """Used in constructing an outgoing packet""" 1.452 - out.writeString(self.cpu, len(self.cpu)) 1.453 - out.writeString(self.os, len(self.os)) 1.454 + def write(self, out): 1.455 + """Used in constructing an outgoing packet""" 1.456 + out.writeString(self.cpu, len(self.cpu)) 1.457 + out.writeString(self.os, len(self.os)) 1.458 1.459 - def __eq__(self, other): 1.460 - """Tests equality on cpu and os""" 1.461 - if isinstance(other, DNSHinfo): 1.462 - return self.cpu == other.cpu and self.os == other.os 1.463 - return 0 1.464 + def __eq__(self, other): 1.465 + """Tests equality on cpu and os""" 1.466 + if isinstance(other, DNSHinfo): 1.467 + return self.cpu == other.cpu and self.os == other.os 1.468 + return 0 1.469 1.470 - def __repr__(self): 1.471 - """String representation""" 1.472 - return self.cpu + " " + self.os 1.473 + def __repr__(self): 1.474 + """String representation""" 1.475 + return self.cpu + " " + self.os 1.476 1.477 class DNSPointer(DNSRecord): 1.478 - """A DNS pointer record""" 1.479 + """A DNS pointer record""" 1.480 1.481 - def __init__(self, name, type, clazz, ttl, alias): 1.482 - DNSRecord.__init__(self, name, type, clazz, ttl) 1.483 - self.alias = alias 1.484 + def __init__(self, name, type, clazz, ttl, alias): 1.485 + DNSRecord.__init__(self, name, type, clazz, ttl) 1.486 + self.alias = alias 1.487 1.488 - def write(self, out): 1.489 - """Used in constructing an outgoing packet""" 1.490 - out.writeName(self.alias) 1.491 + def write(self, out): 1.492 + """Used in constructing an outgoing packet""" 1.493 + out.writeName(self.alias) 1.494 1.495 - def __eq__(self, other): 1.496 - """Tests equality on alias""" 1.497 - if isinstance(other, DNSPointer): 1.498 - return self.alias == other.alias 1.499 - return 0 1.500 + def __eq__(self, other): 1.501 + """Tests equality on alias""" 1.502 + if isinstance(other, DNSPointer): 1.503 + return self.alias == other.alias 1.504 + return 0 1.505 1.506 - def __repr__(self): 1.507 - """String representation""" 1.508 - return self.toString(self.alias) 1.509 + def __repr__(self): 1.510 + """String representation""" 1.511 + return self.toString(self.alias) 1.512 1.513 class DNSText(DNSRecord): 1.514 - """A DNS text record""" 1.515 + """A DNS text record""" 1.516 1.517 - def __init__(self, name, type, clazz, ttl, text): 1.518 - DNSRecord.__init__(self, name, type, clazz, ttl) 1.519 - self.text = text 1.520 + def __init__(self, name, type, clazz, ttl, text): 1.521 + DNSRecord.__init__(self, name, type, clazz, ttl) 1.522 + self.text = text 1.523 1.524 - def write(self, out): 1.525 - """Used in constructing an outgoing packet""" 1.526 - out.writeString(self.text, len(self.text)) 1.527 + def write(self, out): 1.528 + """Used in constructing an outgoing packet""" 1.529 + out.writeString(self.text, len(self.text)) 1.530 1.531 - def __eq__(self, other): 1.532 - """Tests equality on text""" 1.533 - if isinstance(other, DNSText): 1.534 - return self.text == other.text 1.535 - return 0 1.536 + def __eq__(self, other): 1.537 + """Tests equality on text""" 1.538 + if isinstance(other, DNSText): 1.539 + return self.text == other.text 1.540 + return 0 1.541 1.542 - def __repr__(self): 1.543 - """String representation""" 1.544 - if len(self.text) > 10: 1.545 - return self.toString(self.text[:7] + "...") 1.546 - else: 1.547 - return self.toString(self.text) 1.548 + def __repr__(self): 1.549 + """String representation""" 1.550 + if len(self.text) > 10: 1.551 + return self.toString(self.text[:7] + "...") 1.552 + else: 1.553 + return self.toString(self.text) 1.554 1.555 class DNSService(DNSRecord): 1.556 - """A DNS service record""" 1.557 + """A DNS service record""" 1.558 1.559 - def __init__(self, name, type, clazz, ttl, priority, weight, port, server): 1.560 - DNSRecord.__init__(self, name, type, clazz, ttl) 1.561 - self.priority = priority 1.562 - self.weight = weight 1.563 - self.port = port 1.564 - self.server = server 1.565 + def __init__(self, name, type, clazz, ttl, priority, weight, port, server): 1.566 + DNSRecord.__init__(self, name, type, clazz, ttl) 1.567 + self.priority = priority 1.568 + self.weight = weight 1.569 + self.port = port 1.570 + self.server = server 1.571 1.572 - def write(self, out): 1.573 - """Used in constructing an outgoing packet""" 1.574 - out.writeShort(self.priority) 1.575 - out.writeShort(self.weight) 1.576 - out.writeShort(self.port) 1.577 - out.writeName(self.server) 1.578 + def write(self, out): 1.579 + """Used in constructing an outgoing packet""" 1.580 + out.writeShort(self.priority) 1.581 + out.writeShort(self.weight) 1.582 + out.writeShort(self.port) 1.583 + out.writeName(self.server) 1.584 1.585 - def __eq__(self, other): 1.586 - """Tests equality on priority, weight, port and server""" 1.587 - if isinstance(other, DNSService): 1.588 - return self.priority == other.priority and self.weight == other.weight and self.port == other.port and self.server == other.server 1.589 - return 0 1.590 + def __eq__(self, other): 1.591 + """Tests equality on priority, weight, port and server""" 1.592 + if isinstance(other, DNSService): 1.593 + return self.priority == other.priority and self.weight == other.weight and self.port == other.port and self.server == other.server 1.594 + return 0 1.595 1.596 - def __repr__(self): 1.597 - """String representation""" 1.598 - return self.toString("%s:%s" % (self.server, self.port)) 1.599 + def __repr__(self): 1.600 + """String representation""" 1.601 + return self.toString("%s:%s" % (self.server, self.port)) 1.602 1.603 class DNSIncoming(object): 1.604 - """Object representation of an incoming DNS packet""" 1.605 + """Object representation of an incoming DNS packet""" 1.606 1.607 - def __init__(self, data): 1.608 - """Constructor from string holding bytes of packet""" 1.609 - self.offset = 0 1.610 - self.data = data 1.611 - self.questions = [] 1.612 - self.answers = [] 1.613 - self.numQuestions = 0 1.614 - self.numAnswers = 0 1.615 - self.numAuthorities = 0 1.616 - self.numAdditionals = 0 1.617 + def __init__(self, data): 1.618 + """Constructor from string holding bytes of packet""" 1.619 + self.offset = 0 1.620 + self.data = data 1.621 + self.questions = [] 1.622 + self.answers = [] 1.623 + self.numQuestions = 0 1.624 + self.numAnswers = 0 1.625 + self.numAuthorities = 0 1.626 + self.numAdditionals = 0 1.627 1.628 - self.readHeader() 1.629 - self.readQuestions() 1.630 - self.readOthers() 1.631 + self.readHeader() 1.632 + self.readQuestions() 1.633 + self.readOthers() 1.634 1.635 - def readHeader(self): 1.636 - """Reads header portion of packet""" 1.637 - format = '!HHHHHH' 1.638 - length = struct.calcsize(format) 1.639 - info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.640 - self.offset += length 1.641 + def readHeader(self): 1.642 + """Reads header portion of packet""" 1.643 + format = '!HHHHHH' 1.644 + length = struct.calcsize(format) 1.645 + info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.646 + self.offset += length 1.647 1.648 - self.id = info[0] 1.649 - self.flags = info[1] 1.650 - self.numQuestions = info[2] 1.651 - self.numAnswers = info[3] 1.652 - self.numAuthorities = info[4] 1.653 - self.numAdditionals = info[5] 1.654 + self.id = info[0] 1.655 + self.flags = info[1] 1.656 + self.numQuestions = info[2] 1.657 + self.numAnswers = info[3] 1.658 + self.numAuthorities = info[4] 1.659 + self.numAdditionals = info[5] 1.660 1.661 - def readQuestions(self): 1.662 - """Reads questions section of packet""" 1.663 - format = '!HH' 1.664 - length = struct.calcsize(format) 1.665 - for i in range(0, self.numQuestions): 1.666 - name = self.readName() 1.667 - info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.668 - self.offset += length 1.669 + def readQuestions(self): 1.670 + """Reads questions section of packet""" 1.671 + format = '!HH' 1.672 + length = struct.calcsize(format) 1.673 + for i in range(0, self.numQuestions): 1.674 + name = self.readName() 1.675 + info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.676 + self.offset += length 1.677 1.678 - question = DNSQuestion(name, info[0], info[1]) 1.679 - self.questions.append(question) 1.680 + question = DNSQuestion(name, info[0], info[1]) 1.681 + self.questions.append(question) 1.682 1.683 - def readInt(self): 1.684 - """Reads an integer from the packet""" 1.685 - format = '!I' 1.686 - length = struct.calcsize(format) 1.687 - info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.688 - self.offset += length 1.689 - return info[0] 1.690 + def readInt(self): 1.691 + """Reads an integer from the packet""" 1.692 + format = '!I' 1.693 + length = struct.calcsize(format) 1.694 + info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.695 + self.offset += length 1.696 + return info[0] 1.697 1.698 - def readCharacterString(self): 1.699 - """Reads a character string from the packet""" 1.700 - length = ord(self.data[self.offset]) 1.701 - self.offset += 1 1.702 - return self.readString(length) 1.703 + def readCharacterString(self): 1.704 + """Reads a character string from the packet""" 1.705 + length = ord(self.data[self.offset]) 1.706 + self.offset += 1 1.707 + return self.readString(length) 1.708 1.709 - def readString(self, len): 1.710 - """Reads a string of a given length from the packet""" 1.711 - format = '!' + str(len) + 's' 1.712 - length = struct.calcsize(format) 1.713 - info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.714 - self.offset += length 1.715 - return info[0] 1.716 + def readString(self, len): 1.717 + """Reads a string of a given length from the packet""" 1.718 + format = '!' + str(len) + 's' 1.719 + length = struct.calcsize(format) 1.720 + info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.721 + self.offset += length 1.722 + return info[0] 1.723 1.724 - def readUnsignedShort(self): 1.725 - """Reads an unsigned short from the packet""" 1.726 - format = '!H' 1.727 - length = struct.calcsize(format) 1.728 - info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.729 - self.offset += length 1.730 - return info[0] 1.731 + def readUnsignedShort(self): 1.732 + """Reads an unsigned short from the packet""" 1.733 + format = '!H' 1.734 + length = struct.calcsize(format) 1.735 + info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.736 + self.offset += length 1.737 + return info[0] 1.738 1.739 - def readOthers(self): 1.740 - """Reads the answers, authorities and additionals section of the packet""" 1.741 - format = '!HHiH' 1.742 - length = struct.calcsize(format) 1.743 - n = self.numAnswers + self.numAuthorities + self.numAdditionals 1.744 - for i in range(0, n): 1.745 - domain = self.readName() 1.746 - info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.747 - self.offset += length 1.748 + def readOthers(self): 1.749 + """Reads the answers, authorities and additionals section of the packet""" 1.750 + format = '!HHiH' 1.751 + length = struct.calcsize(format) 1.752 + n = self.numAnswers + self.numAuthorities + self.numAdditionals 1.753 + for i in range(0, n): 1.754 + domain = self.readName() 1.755 + info = struct.unpack(format, self.data[self.offset:self.offset+length]) 1.756 + self.offset += length 1.757 1.758 - rec = None 1.759 - if info[0] == _TYPE_A: 1.760 - rec = DNSAddress(domain, info[0], info[1], info[2], self.readString(4)) 1.761 - elif info[0] == _TYPE_CNAME or info[0] == _TYPE_PTR: 1.762 - rec = DNSPointer(domain, info[0], info[1], info[2], self.readName()) 1.763 - elif info[0] == _TYPE_TXT: 1.764 - rec = DNSText(domain, info[0], info[1], info[2], self.readString(info[3])) 1.765 - elif info[0] == _TYPE_SRV: 1.766 - rec = DNSService(domain, info[0], info[1], info[2], self.readUnsignedShort(), self.readUnsignedShort(), self.readUnsignedShort(), self.readName()) 1.767 - elif info[0] == _TYPE_HINFO: 1.768 - rec = DNSHinfo(domain, info[0], info[1], info[2], self.readCharacterString(), self.readCharacterString()) 1.769 - elif info[0] == _TYPE_AAAA: 1.770 - rec = DNSAddress(domain, info[0], info[1], info[2], self.readString(16)) 1.771 - else: 1.772 - # Try to ignore types we don't know about 1.773 - # this may mean the rest of the name is 1.774 - # unable to be parsed, and may show errors 1.775 - # so this is left for debugging. New types 1.776 - # encountered need to be parsed properly. 1.777 - # 1.778 - #print "UNKNOWN TYPE = " + str(info[0]) 1.779 - #raise BadTypeInNameException 1.780 - pass 1.781 + rec = None 1.782 + if info[0] == _TYPE_A: 1.783 + rec = DNSAddress(domain, info[0], info[1], info[2], self.readString(4)) 1.784 + elif info[0] == _TYPE_CNAME or info[0] == _TYPE_PTR: 1.785 + rec = DNSPointer(domain, info[0], info[1], info[2], self.readName()) 1.786 + elif info[0] == _TYPE_TXT: 1.787 + rec = DNSText(domain, info[0], info[1], info[2], self.readString(info[3])) 1.788 + elif info[0] == _TYPE_SRV: 1.789 + rec = DNSService(domain, info[0], info[1], info[2], self.readUnsignedShort(), self.readUnsignedShort(), self.readUnsignedShort(), self.readName()) 1.790 + elif info[0] == _TYPE_HINFO: 1.791 + rec = DNSHinfo(domain, info[0], info[1], info[2], self.readCharacterString(), self.readCharacterString()) 1.792 + elif info[0] == _TYPE_AAAA: 1.793 + rec = DNSAddress(domain, info[0], info[1], info[2], self.readString(16)) 1.794 + else: 1.795 + # Try to ignore types we don't know about 1.796 + # this may mean the rest of the name is 1.797 + # unable to be parsed, and may show errors 1.798 + # so this is left for debugging. New types 1.799 + # encountered need to be parsed properly. 1.800 + # 1.801 + #print "UNKNOWN TYPE = " + str(info[0]) 1.802 + #raise BadTypeInNameException 1.803 + pass 1.804 1.805 - if rec is not None: 1.806 - self.answers.append(rec) 1.807 + if rec is not None: 1.808 + self.answers.append(rec) 1.809 1.810 - def isQuery(self): 1.811 - """Returns true if this is a query""" 1.812 - return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_QUERY 1.813 + def isQuery(self): 1.814 + """Returns true if this is a query""" 1.815 + return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_QUERY 1.816 1.817 - def isResponse(self): 1.818 - """Returns true if this is a response""" 1.819 - return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_RESPONSE 1.820 + def isResponse(self): 1.821 + """Returns true if this is a response""" 1.822 + return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_RESPONSE 1.823 1.824 - def readUTF(self, offset, len): 1.825 - """Reads a UTF-8 string of a given length from the packet""" 1.826 - result = self.data[offset:offset+len].decode('utf-8') 1.827 - return result 1.828 + def readUTF(self, offset, len): 1.829 + """Reads a UTF-8 string of a given length from the packet""" 1.830 + result = self.data[offset:offset+len].decode('utf-8') 1.831 + return result 1.832 1.833 - def readName(self): 1.834 - """Reads a domain name from the packet""" 1.835 - result = '' 1.836 - off = self.offset 1.837 - next = -1 1.838 - first = off 1.839 + def readName(self): 1.840 + """Reads a domain name from the packet""" 1.841 + result = '' 1.842 + off = self.offset 1.843 + next = -1 1.844 + first = off 1.845 1.846 - while 1: 1.847 - len = ord(self.data[off]) 1.848 - off += 1 1.849 - if len == 0: 1.850 - break 1.851 - t = len & 0xC0 1.852 - if t == 0x00: 1.853 - result = ''.join((result, self.readUTF(off, len) + '.')) 1.854 - off += len 1.855 - elif t == 0xC0: 1.856 - if next < 0: 1.857 - next = off + 1 1.858 - off = ((len & 0x3F) << 8) | ord(self.data[off]) 1.859 - if off >= first: 1.860 - raise "Bad domain name (circular) at " + str(off) 1.861 - first = off 1.862 - else: 1.863 - raise "Bad domain name at " + str(off) 1.864 + while 1: 1.865 + len = ord(self.data[off]) 1.866 + off += 1 1.867 + if len == 0: 1.868 + break 1.869 + t = len & 0xC0 1.870 + if t == 0x00: 1.871 + result = ''.join((result, self.readUTF(off, len) + '.')) 1.872 + off += len 1.873 + elif t == 0xC0: 1.874 + if next < 0: 1.875 + next = off + 1 1.876 + off = ((len & 0x3F) << 8) | ord(self.data[off]) 1.877 + if off >= first: 1.878 + raise "Bad domain name (circular) at " + str(off) 1.879 + first = off 1.880 + else: 1.881 + raise "Bad domain name at " + str(off) 1.882 1.883 - if next >= 0: 1.884 - self.offset = next 1.885 - else: 1.886 - self.offset = off 1.887 + if next >= 0: 1.888 + self.offset = next 1.889 + else: 1.890 + self.offset = off 1.891 1.892 - return result 1.893 + return result 1.894 1.895 1.896 class DNSOutgoing(object): 1.897 - """Object representation of an outgoing packet""" 1.898 + """Object representation of an outgoing packet""" 1.899 1.900 - def __init__(self, flags, multicast = 1): 1.901 - self.finished = 0 1.902 - self.id = 0 1.903 - self.multicast = multicast 1.904 - self.flags = flags 1.905 - self.names = {} 1.906 - self.data = [] 1.907 - self.size = 12 1.908 + def __init__(self, flags, multicast = 1): 1.909 + self.finished = 0 1.910 + self.id = 0 1.911 + self.multicast = multicast 1.912 + self.flags = flags 1.913 + self.names = {} 1.914 + self.data = [] 1.915 + self.size = 12 1.916 1.917 - self.questions = [] 1.918 - self.answers = [] 1.919 - self.authorities = [] 1.920 - self.additionals = [] 1.921 + self.questions = [] 1.922 + self.answers = [] 1.923 + self.authorities = [] 1.924 + self.additionals = [] 1.925 1.926 - def addQuestion(self, record): 1.927 - """Adds a question""" 1.928 - self.questions.append(record) 1.929 + def addQuestion(self, record): 1.930 + """Adds a question""" 1.931 + self.questions.append(record) 1.932 1.933 - def addAnswer(self, inp, record): 1.934 - """Adds an answer""" 1.935 - if not record.suppressedBy(inp): 1.936 - self.addAnswerAtTime(record, 0) 1.937 + def addAnswer(self, inp, record): 1.938 + """Adds an answer""" 1.939 + if not record.suppressedBy(inp): 1.940 + self.addAnswerAtTime(record, 0) 1.941 1.942 - def addAnswerAtTime(self, record, now): 1.943 - """Adds an answer if if does not expire by a certain time""" 1.944 - if record is not None: 1.945 - if now == 0 or not record.isExpired(now): 1.946 - self.answers.append((record, now)) 1.947 + def addAnswerAtTime(self, record, now): 1.948 + """Adds an answer if if does not expire by a certain time""" 1.949 + if record is not None: 1.950 + if now == 0 or not record.isExpired(now): 1.951 + self.answers.append((record, now)) 1.952 1.953 - def addAuthorativeAnswer(self, record): 1.954 - """Adds an authoritative answer""" 1.955 - self.authorities.append(record) 1.956 + def addAuthorativeAnswer(self, record): 1.957 + """Adds an authoritative answer""" 1.958 + self.authorities.append(record) 1.959 1.960 - def addAdditionalAnswer(self, record): 1.961 - """Adds an additional answer""" 1.962 - self.additionals.append(record) 1.963 + def addAdditionalAnswer(self, record): 1.964 + """Adds an additional answer""" 1.965 + self.additionals.append(record) 1.966 1.967 - def writeByte(self, value): 1.968 - """Writes a single byte to the packet""" 1.969 - format = '!c' 1.970 - self.data.append(struct.pack(format, chr(value))) 1.971 - self.size += 1 1.972 + def writeByte(self, value): 1.973 + """Writes a single byte to the packet""" 1.974 + format = '!c' 1.975 + self.data.append(struct.pack(format, chr(value))) 1.976 + self.size += 1 1.977 1.978 - def insertShort(self, index, value): 1.979 - """Inserts an unsigned short in a certain position in the packet""" 1.980 - format = '!H' 1.981 - self.data.insert(index, struct.pack(format, value)) 1.982 - self.size += 2 1.983 + def insertShort(self, index, value): 1.984 + """Inserts an unsigned short in a certain position in the packet""" 1.985 + format = '!H' 1.986 + self.data.insert(index, struct.pack(format, value)) 1.987 + self.size += 2 1.988 1.989 - def writeShort(self, value): 1.990 - """Writes an unsigned short to the packet""" 1.991 - format = '!H' 1.992 - self.data.append(struct.pack(format, value)) 1.993 - self.size += 2 1.994 + def writeShort(self, value): 1.995 + """Writes an unsigned short to the packet""" 1.996 + format = '!H' 1.997 + self.data.append(struct.pack(format, value)) 1.998 + self.size += 2 1.999 1.1000 - def writeInt(self, value): 1.1001 - """Writes an unsigned integer to the packet""" 1.1002 - format = '!I' 1.1003 - self.data.append(struct.pack(format, int(value))) 1.1004 - self.size += 4 1.1005 + def writeInt(self, value): 1.1006 + """Writes an unsigned integer to the packet""" 1.1007 + format = '!I' 1.1008 + self.data.append(struct.pack(format, int(value))) 1.1009 + self.size += 4 1.1010 1.1011 - def writeString(self, value, length): 1.1012 - """Writes a string to the packet""" 1.1013 - format = '!' + str(length) + 's' 1.1014 - self.data.append(struct.pack(format, value)) 1.1015 - self.size += length 1.1016 + def writeString(self, value, length): 1.1017 + """Writes a string to the packet""" 1.1018 + format = '!' + str(length) + 's' 1.1019 + self.data.append(struct.pack(format, value)) 1.1020 + self.size += length 1.1021 1.1022 - def writeUTF(self, s): 1.1023 - """Writes a UTF-8 string of a given length to the packet""" 1.1024 - utfstr = s.encode('utf-8') 1.1025 - length = len(utfstr) 1.1026 - if length > 64: 1.1027 - raise NamePartTooLongException 1.1028 - self.writeByte(length) 1.1029 - self.writeString(utfstr, length) 1.1030 + def writeUTF(self, s): 1.1031 + """Writes a UTF-8 string of a given length to the packet""" 1.1032 + utfstr = s.encode('utf-8') 1.1033 + length = len(utfstr) 1.1034 + if length > 64: 1.1035 + raise NamePartTooLongException 1.1036 + self.writeByte(length) 1.1037 + self.writeString(utfstr, length) 1.1038 1.1039 - def writeName(self, name): 1.1040 - """Writes a domain name to the packet""" 1.1041 + def writeName(self, name): 1.1042 + """Writes a domain name to the packet""" 1.1043 1.1044 - try: 1.1045 - # Find existing instance of this name in packet 1.1046 - # 1.1047 - index = self.names[name] 1.1048 - except KeyError: 1.1049 - # No record of this name already, so write it 1.1050 - # out as normal, recording the location of the name 1.1051 - # for future pointers to it. 1.1052 - # 1.1053 - self.names[name] = self.size 1.1054 - parts = name.split('.') 1.1055 - if parts[-1] == '': 1.1056 - parts = parts[:-1] 1.1057 - for part in parts: 1.1058 - self.writeUTF(part) 1.1059 - self.writeByte(0) 1.1060 - return 1.1061 + try: 1.1062 + # Find existing instance of this name in packet 1.1063 + # 1.1064 + index = self.names[name] 1.1065 + except KeyError: 1.1066 + # No record of this name already, so write it 1.1067 + # out as normal, recording the location of the name 1.1068 + # for future pointers to it. 1.1069 + # 1.1070 + self.names[name] = self.size 1.1071 + parts = name.split('.') 1.1072 + if parts[-1] == '': 1.1073 + parts = parts[:-1] 1.1074 + for part in parts: 1.1075 + self.writeUTF(part) 1.1076 + self.writeByte(0) 1.1077 + return 1.1078 1.1079 - # An index was found, so write a pointer to it 1.1080 - # 1.1081 - self.writeByte((index >> 8) | 0xC0) 1.1082 - self.writeByte(index) 1.1083 + # An index was found, so write a pointer to it 1.1084 + # 1.1085 + self.writeByte((index >> 8) | 0xC0) 1.1086 + self.writeByte(index) 1.1087 1.1088 - def writeQuestion(self, question): 1.1089 - """Writes a question to the packet""" 1.1090 - self.writeName(question.name) 1.1091 - self.writeShort(question.type) 1.1092 - self.writeShort(question.clazz) 1.1093 + def writeQuestion(self, question): 1.1094 + """Writes a question to the packet""" 1.1095 + self.writeName(question.name) 1.1096 + self.writeShort(question.type) 1.1097 + self.writeShort(question.clazz) 1.1098 1.1099 - def writeRecord(self, record, now): 1.1100 - """Writes a record (answer, authoritative answer, additional) to 1.1101 - the packet""" 1.1102 - self.writeName(record.name) 1.1103 - self.writeShort(record.type) 1.1104 - if record.unique and self.multicast: 1.1105 - self.writeShort(record.clazz | _CLASS_UNIQUE) 1.1106 - else: 1.1107 - self.writeShort(record.clazz) 1.1108 - if now == 0: 1.1109 - self.writeInt(record.ttl) 1.1110 - else: 1.1111 - self.writeInt(record.getRemainingTTL(now)) 1.1112 - index = len(self.data) 1.1113 - # Adjust size for the short we will write before this record 1.1114 - # 1.1115 - self.size += 2 1.1116 - record.write(self) 1.1117 - self.size -= 2 1.1118 + def writeRecord(self, record, now): 1.1119 + """Writes a record (answer, authoritative answer, additional) to 1.1120 + the packet""" 1.1121 + self.writeName(record.name) 1.1122 + self.writeShort(record.type) 1.1123 + if record.unique and self.multicast: 1.1124 + self.writeShort(record.clazz | _CLASS_UNIQUE) 1.1125 + else: 1.1126 + self.writeShort(record.clazz) 1.1127 + if now == 0: 1.1128 + self.writeInt(record.ttl) 1.1129 + else: 1.1130 + self.writeInt(record.getRemainingTTL(now)) 1.1131 + index = len(self.data) 1.1132 + # Adjust size for the short we will write before this record 1.1133 + # 1.1134 + self.size += 2 1.1135 + record.write(self) 1.1136 + self.size -= 2 1.1137 1.1138 - length = len(''.join(self.data[index:])) 1.1139 - self.insertShort(index, length) # Here is the short we adjusted for 1.1140 + length = len(''.join(self.data[index:])) 1.1141 + self.insertShort(index, length) # Here is the short we adjusted for 1.1142 1.1143 - def packet(self): 1.1144 - """Returns a string containing the packet's bytes 1.1145 + def packet(self): 1.1146 + """Returns a string containing the packet's bytes 1.1147 1.1148 - No further parts should be added to the packet once this 1.1149 - is done.""" 1.1150 - if not self.finished: 1.1151 - self.finished = 1 1.1152 - for question in self.questions: 1.1153 - self.writeQuestion(question) 1.1154 - for answer, time in self.answers: 1.1155 - self.writeRecord(answer, time) 1.1156 - for authority in self.authorities: 1.1157 - self.writeRecord(authority, 0) 1.1158 - for additional in self.additionals: 1.1159 - self.writeRecord(additional, 0) 1.1160 + No further parts should be added to the packet once this 1.1161 + is done.""" 1.1162 + if not self.finished: 1.1163 + self.finished = 1 1.1164 + for question in self.questions: 1.1165 + self.writeQuestion(question) 1.1166 + for answer, time in self.answers: 1.1167 + self.writeRecord(answer, time) 1.1168 + for authority in self.authorities: 1.1169 + self.writeRecord(authority, 0) 1.1170 + for additional in self.additionals: 1.1171 + self.writeRecord(additional, 0) 1.1172 1.1173 - self.insertShort(0, len(self.additionals)) 1.1174 - self.insertShort(0, len(self.authorities)) 1.1175 - self.insertShort(0, len(self.answers)) 1.1176 - self.insertShort(0, len(self.questions)) 1.1177 - self.insertShort(0, self.flags) 1.1178 - if self.multicast: 1.1179 - self.insertShort(0, 0) 1.1180 - else: 1.1181 - self.insertShort(0, self.id) 1.1182 - return ''.join(self.data) 1.1183 + self.insertShort(0, len(self.additionals)) 1.1184 + self.insertShort(0, len(self.authorities)) 1.1185 + self.insertShort(0, len(self.answers)) 1.1186 + self.insertShort(0, len(self.questions)) 1.1187 + self.insertShort(0, self.flags) 1.1188 + if self.multicast: 1.1189 + self.insertShort(0, 0) 1.1190 + else: 1.1191 + self.insertShort(0, self.id) 1.1192 + return ''.join(self.data) 1.1193 1.1194 1.1195 class DNSCache(object): 1.1196 - """A cache of DNS entries""" 1.1197 + """A cache of DNS entries""" 1.1198 1.1199 - def __init__(self): 1.1200 - self.cache = {} 1.1201 + def __init__(self): 1.1202 + self.cache = {} 1.1203 1.1204 - def add(self, entry): 1.1205 - """Adds an entry""" 1.1206 - try: 1.1207 - list = self.cache[entry.key] 1.1208 - except: 1.1209 - list = self.cache[entry.key] = [] 1.1210 - list.append(entry) 1.1211 + def add(self, entry): 1.1212 + """Adds an entry""" 1.1213 + try: 1.1214 + list = self.cache[entry.key] 1.1215 + except: 1.1216 + list = self.cache[entry.key] = [] 1.1217 + list.append(entry) 1.1218 1.1219 - def remove(self, entry): 1.1220 - """Removes an entry""" 1.1221 - try: 1.1222 - list = self.cache[entry.key] 1.1223 - list.remove(entry) 1.1224 - except: 1.1225 - pass 1.1226 + def remove(self, entry): 1.1227 + """Removes an entry""" 1.1228 + try: 1.1229 + list = self.cache[entry.key] 1.1230 + list.remove(entry) 1.1231 + except: 1.1232 + pass 1.1233 1.1234 - def get(self, entry): 1.1235 - """Gets an entry by key. Will return None if there is no 1.1236 - matching entry.""" 1.1237 - try: 1.1238 - list = self.cache[entry.key] 1.1239 - return list[list.index(entry)] 1.1240 - except: 1.1241 - return None 1.1242 + def get(self, entry): 1.1243 + """Gets an entry by key. Will return None if there is no 1.1244 + matching entry.""" 1.1245 + try: 1.1246 + list = self.cache[entry.key] 1.1247 + return list[list.index(entry)] 1.1248 + except: 1.1249 + return None 1.1250 1.1251 - def getByDetails(self, name, type, clazz): 1.1252 - """Gets an entry by details. Will return None if there is 1.1253 - no matching entry.""" 1.1254 - entry = DNSEntry(name, type, clazz) 1.1255 - return self.get(entry) 1.1256 + def getByDetails(self, name, type, clazz): 1.1257 + """Gets an entry by details. Will return None if there is 1.1258 + no matching entry.""" 1.1259 + entry = DNSEntry(name, type, clazz) 1.1260 + return self.get(entry) 1.1261 1.1262 - def entriesWithName(self, name): 1.1263 - """Returns a list of entries whose key matches the name.""" 1.1264 - try: 1.1265 - return self.cache[name] 1.1266 - except: 1.1267 - return [] 1.1268 + def entriesWithName(self, name): 1.1269 + """Returns a list of entries whose key matches the name.""" 1.1270 + try: 1.1271 + return self.cache[name] 1.1272 + except: 1.1273 + return [] 1.1274 1.1275 - def entries(self): 1.1276 - """Returns a list of all entries""" 1.1277 - def add(x, y): return x+y 1.1278 - try: 1.1279 - return reduce(add, self.cache.values()) 1.1280 - except: 1.1281 - return [] 1.1282 + def entries(self): 1.1283 + """Returns a list of all entries""" 1.1284 + def add(x, y): return x+y 1.1285 + try: 1.1286 + return reduce(add, self.cache.values()) 1.1287 + except: 1.1288 + return [] 1.1289 1.1290 1.1291 class Engine(threading.Thread): 1.1292 - """An engine wraps read access to sockets, allowing objects that 1.1293 - need to receive data from sockets to be called back when the 1.1294 - sockets are ready. 1.1295 + """An engine wraps read access to sockets, allowing objects that 1.1296 + need to receive data from sockets to be called back when the 1.1297 + sockets are ready. 1.1298 1.1299 - A reader needs a handle_read() method, which is called when the socket 1.1300 - it is interested in is ready for reading. 1.1301 + A reader needs a handle_read() method, which is called when the socket 1.1302 + it is interested in is ready for reading. 1.1303 1.1304 - Writers are not implemented here, because we only send short 1.1305 - packets. 1.1306 - """ 1.1307 + Writers are not implemented here, because we only send short 1.1308 + packets. 1.1309 + """ 1.1310 1.1311 - def __init__(self, zeroconf): 1.1312 - threading.Thread.__init__(self) 1.1313 - self.zeroconf = zeroconf 1.1314 - self.readers = {} # maps socket to reader 1.1315 - self.timeout = 5 1.1316 - self.condition = threading.Condition() 1.1317 - self.start() 1.1318 + def __init__(self, zeroconf): 1.1319 + threading.Thread.__init__(self) 1.1320 + self.zeroconf = zeroconf 1.1321 + self.readers = {} # maps socket to reader 1.1322 + self.timeout = 5 1.1323 + self.condition = threading.Condition() 1.1324 + self.start() 1.1325 1.1326 - def run(self): 1.1327 - while not globals()['_GLOBAL_DONE']: 1.1328 - rs = self.getReaders() 1.1329 - if len(rs) == 0: 1.1330 - # No sockets to manage, but we wait for the timeout 1.1331 - # or addition of a socket 1.1332 - # 1.1333 - self.condition.acquire() 1.1334 - self.condition.wait(self.timeout) 1.1335 - self.condition.release() 1.1336 - else: 1.1337 - try: 1.1338 - rr, wr, er = select.select(rs, [], [], self.timeout) 1.1339 - for socket in rr: 1.1340 - try: 1.1341 - self.readers[socket].handle_read() 1.1342 - except: 1.1343 - traceback.print_exc() 1.1344 - except: 1.1345 - pass 1.1346 + def run(self): 1.1347 + while not globals()['_GLOBAL_DONE']: 1.1348 + rs = self.getReaders() 1.1349 + if len(rs) == 0: 1.1350 + # No sockets to manage, but we wait for the timeout 1.1351 + # or addition of a socket 1.1352 + # 1.1353 + self.condition.acquire() 1.1354 + self.condition.wait(self.timeout) 1.1355 + self.condition.release() 1.1356 + else: 1.1357 + try: 1.1358 + rr, wr, er = select.select(rs, [], [], self.timeout) 1.1359 + for socket in rr: 1.1360 + try: 1.1361 + self.readers[socket].handle_read() 1.1362 + except: 1.1363 + traceback.print_exc() 1.1364 + except: 1.1365 + pass 1.1366 1.1367 - def getReaders(self): 1.1368 - self.condition.acquire() 1.1369 - result = self.readers.keys() 1.1370 - self.condition.release() 1.1371 - return result 1.1372 + def getReaders(self): 1.1373 + self.condition.acquire() 1.1374 + result = self.readers.keys() 1.1375 + self.condition.release() 1.1376 + return result 1.1377 1.1378 - def addReader(self, reader, socket): 1.1379 - self.condition.acquire() 1.1380 - self.readers[socket] = reader 1.1381 - self.condition.notify() 1.1382 - self.condition.release() 1.1383 + def addReader(self, reader, socket): 1.1384 + self.condition.acquire() 1.1385 + self.readers[socket] = reader 1.1386 + self.condition.notify() 1.1387 + self.condition.release() 1.1388 1.1389 - def delReader(self, socket): 1.1390 - self.condition.acquire() 1.1391 - del(self.readers[socket]) 1.1392 - self.condition.notify() 1.1393 - self.condition.release() 1.1394 + def delReader(self, socket): 1.1395 + self.condition.acquire() 1.1396 + del(self.readers[socket]) 1.1397 + self.condition.notify() 1.1398 + self.condition.release() 1.1399 1.1400 - def notify(self): 1.1401 - self.condition.acquire() 1.1402 - self.condition.notify() 1.1403 - self.condition.release() 1.1404 + def notify(self): 1.1405 + self.condition.acquire() 1.1406 + self.condition.notify() 1.1407 + self.condition.release() 1.1408 1.1409 class Listener(object): 1.1410 - """A Listener is used by this module to listen on the multicast 1.1411 - group to which DNS messages are sent, allowing the implementation 1.1412 - to cache information as it arrives. 1.1413 + """A Listener is used by this module to listen on the multicast 1.1414 + group to which DNS messages are sent, allowing the implementation 1.1415 + to cache information as it arrives. 1.1416 1.1417 - It requires registration with an Engine object in order to have 1.1418 - the read() method called when a socket is availble for reading.""" 1.1419 + It requires registration with an Engine object in order to have 1.1420 + the read() method called when a socket is availble for reading.""" 1.1421 1.1422 - def __init__(self, zeroconf): 1.1423 - self.zeroconf = zeroconf 1.1424 - self.zeroconf.engine.addReader(self, self.zeroconf.socket) 1.1425 + def __init__(self, zeroconf): 1.1426 + self.zeroconf = zeroconf 1.1427 + self.zeroconf.engine.addReader(self, self.zeroconf.socket) 1.1428 1.1429 - def handle_read(self): 1.1430 - data, (addr, port) = self.zeroconf.socket.recvfrom(_MAX_MSG_ABSOLUTE) 1.1431 - self.data = data 1.1432 - msg = DNSIncoming(data) 1.1433 - if msg.isQuery(): 1.1434 - # Always multicast responses 1.1435 - # 1.1436 - if port == _MDNS_PORT: 1.1437 - self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT) 1.1438 - # If it's not a multicast query, reply via unicast 1.1439 - # and multicast 1.1440 - # 1.1441 - elif port == _DNS_PORT: 1.1442 - self.zeroconf.handleQuery(msg, addr, port) 1.1443 - self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT) 1.1444 - else: 1.1445 - self.zeroconf.handleResponse(msg) 1.1446 + def handle_read(self): 1.1447 + data, (addr, port) = self.zeroconf.socket.recvfrom(_MAX_MSG_ABSOLUTE) 1.1448 + self.data = data 1.1449 + msg = DNSIncoming(data) 1.1450 + if msg.isQuery(): 1.1451 + # Always multicast responses 1.1452 + # 1.1453 + if port == _MDNS_PORT: 1.1454 + self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT) 1.1455 + # If it's not a multicast query, reply via unicast 1.1456 + # and multicast 1.1457 + # 1.1458 + elif port == _DNS_PORT: 1.1459 + self.zeroconf.handleQuery(msg, addr, port) 1.1460 + self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT) 1.1461 + else: 1.1462 + self.zeroconf.handleResponse(msg) 1.1463 1.1464 1.1465 class Reaper(threading.Thread): 1.1466 - """A Reaper is used by this module to remove cache entries that 1.1467 - have expired.""" 1.1468 + """A Reaper is used by this module to remove cache entries that 1.1469 + have expired.""" 1.1470 1.1471 - def __init__(self, zeroconf): 1.1472 - threading.Thread.__init__(self) 1.1473 - self.zeroconf = zeroconf 1.1474 - self.start() 1.1475 + def __init__(self, zeroconf): 1.1476 + threading.Thread.__init__(self) 1.1477 + self.zeroconf = zeroconf 1.1478 + self.start() 1.1479 1.1480 - def run(self): 1.1481 - while 1: 1.1482 - self.zeroconf.wait(10 * 1000) 1.1483 - if globals()['_GLOBAL_DONE']: 1.1484 - return 1.1485 - now = currentTimeMillis() 1.1486 - for record in self.zeroconf.cache.entries(): 1.1487 - if record.isExpired(now): 1.1488 - self.zeroconf.updateRecord(now, record) 1.1489 - self.zeroconf.cache.remove(record) 1.1490 + def run(self): 1.1491 + while 1: 1.1492 + self.zeroconf.wait(10 * 1000) 1.1493 + if globals()['_GLOBAL_DONE']: 1.1494 + return 1.1495 + now = currentTimeMillis() 1.1496 + for record in self.zeroconf.cache.entries(): 1.1497 + if record.isExpired(now): 1.1498 + self.zeroconf.updateRecord(now, record) 1.1499 + self.zeroconf.cache.remove(record) 1.1500 1.1501 1.1502 class ServiceBrowser(threading.Thread): 1.1503 - """Used to browse for a service of a specific type. 1.1504 + """Used to browse for a service of a specific type. 1.1505 1.1506 - The listener object will have its addService() and 1.1507 - removeService() methods called when this browser 1.1508 - discovers changes in the services availability.""" 1.1509 + The listener object will have its addService() and 1.1510 + removeService() methods called when this browser 1.1511 + discovers changes in the services availability.""" 1.1512 1.1513 - def __init__(self, zeroconf, type, listener): 1.1514 - """Creates a browser for a specific type""" 1.1515 - threading.Thread.__init__(self) 1.1516 - self.zeroconf = zeroconf 1.1517 - self.type = type 1.1518 - self.listener = listener 1.1519 - self.services = {} 1.1520 - self.nextTime = currentTimeMillis() 1.1521 - self.delay = _BROWSER_TIME 1.1522 - self.list = [] 1.1523 + def __init__(self, zeroconf, type, listener): 1.1524 + """Creates a browser for a specific type""" 1.1525 + threading.Thread.__init__(self) 1.1526 + self.zeroconf = zeroconf 1.1527 + self.type = type 1.1528 + self.listener = listener 1.1529 + self.services = {} 1.1530 + self.nextTime = currentTimeMillis() 1.1531 + self.delay = _BROWSER_TIME 1.1532 + self.list = [] 1.1533 1.1534 - self.done = 0 1.1535 + self.done = 0 1.1536 1.1537 - self.zeroconf.addListener(self, DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN)) 1.1538 - self.start() 1.1539 + self.zeroconf.addListener(self, DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN)) 1.1540 + self.start() 1.1541 1.1542 - def updateRecord(self, zeroconf, now, record): 1.1543 - """Callback invoked by Zeroconf when new information arrives. 1.1544 + def updateRecord(self, zeroconf, now, record): 1.1545 + """Callback invoked by Zeroconf when new information arrives. 1.1546 1.1547 - Updates information required by browser in the Zeroconf cache.""" 1.1548 - if record.type == _TYPE_PTR and record.name == self.type: 1.1549 - expired = record.isExpired(now) 1.1550 - try: 1.1551 - oldrecord = self.services[record.alias.lower()] 1.1552 - if not expired: 1.1553 - oldrecord.resetTTL(record) 1.1554 - else: 1.1555 - del(self.services[record.alias.lower()]) 1.1556 - callback = lambda x: self.listener.removeService(x, self.type, record.alias) 1.1557 - self.list.append(callback) 1.1558 - return 1.1559 - except: 1.1560 - if not expired: 1.1561 - self.services[record.alias.lower()] = record 1.1562 - callback = lambda x: self.listener.addService(x, self.type, record.alias) 1.1563 - self.list.append(callback) 1.1564 + Updates information required by browser in the Zeroconf cache.""" 1.1565 + if record.type == _TYPE_PTR and record.name == self.type: 1.1566 + expired = record.isExpired(now) 1.1567 + try: 1.1568 + oldrecord = self.services[record.alias.lower()] 1.1569 + if not expired: 1.1570 + oldrecord.resetTTL(record) 1.1571 + else: 1.1572 + del(self.services[record.alias.lower()]) 1.1573 + callback = lambda x: self.listener.removeService(x, self.type, record.alias) 1.1574 + self.list.append(callback) 1.1575 + return 1.1576 + except: 1.1577 + if not expired: 1.1578 + self.services[record.alias.lower()] = record 1.1579 + callback = lambda x: self.listener.addService(x, self.type, record.alias) 1.1580 + self.list.append(callback) 1.1581 1.1582 - expires = record.getExpirationTime(75) 1.1583 - if expires < self.nextTime: 1.1584 - self.nextTime = expires 1.1585 + expires = record.getExpirationTime(75) 1.1586 + if expires < self.nextTime: 1.1587 + self.nextTime = expires 1.1588 1.1589 - def cancel(self): 1.1590 - self.done = 1 1.1591 - self.zeroconf.notifyAll() 1.1592 + def cancel(self): 1.1593 + self.done = 1 1.1594 + self.zeroconf.notifyAll() 1.1595 1.1596 - def run(self): 1.1597 - while 1: 1.1598 - event = None 1.1599 - now = currentTimeMillis() 1.1600 - if len(self.list) == 0 and self.nextTime > now: 1.1601 - self.zeroconf.wait(self.nextTime - now) 1.1602 - if globals()['_GLOBAL_DONE'] or self.done: 1.1603 - return 1.1604 - now = currentTimeMillis() 1.1605 + def run(self): 1.1606 + while 1: 1.1607 + event = None 1.1608 + now = currentTimeMillis() 1.1609 + if len(self.list) == 0 and self.nextTime > now: 1.1610 + self.zeroconf.wait(self.nextTime - now) 1.1611 + if globals()['_GLOBAL_DONE'] or self.done: 1.1612 + return 1.1613 + now = currentTimeMillis() 1.1614 1.1615 - if self.nextTime <= now: 1.1616 - out = DNSOutgoing(_FLAGS_QR_QUERY) 1.1617 - out.addQuestion(DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN)) 1.1618 - for record in self.services.values(): 1.1619 - if not record.isExpired(now): 1.1620 - out.addAnswerAtTime(record, now) 1.1621 - self.zeroconf.send(out) 1.1622 - self.nextTime = now + self.delay 1.1623 - self.delay = min(20 * 1000, self.delay * 2) 1.1624 + if self.nextTime <= now: 1.1625 + out = DNSOutgoing(_FLAGS_QR_QUERY) 1.1626 + out.addQuestion(DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN)) 1.1627 + for record in self.services.values(): 1.1628 + if not record.isExpired(now): 1.1629 + out.addAnswerAtTime(record, now) 1.1630 + self.zeroconf.send(out) 1.1631 + self.nextTime = now + self.delay 1.1632 + self.delay = min(20 * 1000, self.delay * 2) 1.1633 1.1634 - if len(self.list) > 0: 1.1635 - event = self.list.pop(0) 1.1636 + if len(self.list) > 0: 1.1637 + event = self.list.pop(0) 1.1638 1.1639 - if event is not None: 1.1640 - event(self.zeroconf) 1.1641 + if event is not None: 1.1642 + event(self.zeroconf) 1.1643 1.1644 1.1645 class ServiceInfo(object): 1.1646 - """Service information""" 1.1647 + """Service information""" 1.1648 1.1649 - def __init__(self, type, name, address=None, port=None, weight=0, priority=0, properties=None, server=None): 1.1650 - """Create a service description. 1.1651 + def __init__(self, type, name, address=None, port=None, weight=0, priority=0, properties=None, server=None): 1.1652 + """Create a service description. 1.1653 1.1654 - type: fully qualified service type name 1.1655 - name: fully qualified service name 1.1656 - address: IP address as unsigned short, network byte order 1.1657 - port: port that the service runs on 1.1658 - weight: weight of the service 1.1659 - priority: priority of the service 1.1660 - properties: dictionary of properties (or a string holding the bytes for the text field) 1.1661 - server: fully qualified name for service host (defaults to name)""" 1.1662 + type: fully qualified service type name 1.1663 + name: fully qualified service name 1.1664 + address: IP address as unsigned short, network byte order 1.1665 + port: port that the service runs on 1.1666 + weight: weight of the service 1.1667 + priority: priority of the service 1.1668 + properties: dictionary of properties (or a string holding the bytes for the text field) 1.1669 + server: fully qualified name for service host (defaults to name)""" 1.1670 1.1671 - if not name.endswith(type): 1.1672 - raise BadTypeInNameException 1.1673 - self.type = type 1.1674 - self.name = name 1.1675 - self.address = address 1.1676 - self.port = port 1.1677 - self.weight = weight 1.1678 - self.priority = priority 1.1679 - if server: 1.1680 - self.server = server 1.1681 - else: 1.1682 - self.server = name 1.1683 - self.setProperties(properties) 1.1684 + if not name.endswith(type): 1.1685 + raise BadTypeInNameException 1.1686 + self.type = type 1.1687 + self.name = name 1.1688 + self.address = address 1.1689 + self.port = port 1.1690 + self.weight = weight 1.1691 + self.priority = priority 1.1692 + if server: 1.1693 + self.server = server 1.1694 + else: 1.1695 + self.server = name 1.1696 + self.setProperties(properties) 1.1697 1.1698 - def setProperties(self, properties): 1.1699 - """Sets properties and text of this info from a dictionary""" 1.1700 - if isinstance(properties, dict): 1.1701 - self.properties = properties 1.1702 - list = [] 1.1703 - result = '' 1.1704 - for key in properties: 1.1705 - value = properties[key] 1.1706 - if value is None: 1.1707 - suffix = ''.encode('utf-8') 1.1708 - elif isinstance(value, str): 1.1709 - suffix = value.encode('utf-8') 1.1710 - elif isinstance(value, int): 1.1711 - if value: 1.1712 - suffix = 'true' 1.1713 - else: 1.1714 - suffix = 'false' 1.1715 - else: 1.1716 - suffix = ''.encode('utf-8') 1.1717 - list.append('='.join((key, suffix))) 1.1718 - for item in list: 1.1719 - result = ''.join((result, struct.pack('!c', chr(len(item))), item)) 1.1720 - self.text = result 1.1721 - else: 1.1722 - self.text = properties 1.1723 + def setProperties(self, properties): 1.1724 + """Sets properties and text of this info from a dictionary""" 1.1725 + if isinstance(properties, dict): 1.1726 + self.properties = properties 1.1727 + list = [] 1.1728 + result = '' 1.1729 + for key in properties: 1.1730 + value = properties[key] 1.1731 + if value is None: 1.1732 + suffix = ''.encode('utf-8') 1.1733 + elif isinstance(value, str): 1.1734 + suffix = value.encode('utf-8') 1.1735 + elif isinstance(value, int): 1.1736 + if value: 1.1737 + suffix = 'true' 1.1738 + else: 1.1739 + suffix = 'false' 1.1740 + else: 1.1741 + suffix = ''.encode('utf-8') 1.1742 + list.append('='.join((key, suffix))) 1.1743 + for item in list: 1.1744 + result = ''.join((result, struct.pack('!c', chr(len(item))), item)) 1.1745 + self.text = result 1.1746 + else: 1.1747 + self.text = properties 1.1748 1.1749 - def setText(self, text): 1.1750 - """Sets properties and text given a text field""" 1.1751 - self.text = text 1.1752 - try: 1.1753 - result = {} 1.1754 - end = len(text) 1.1755 - index = 0 1.1756 - strs = [] 1.1757 - while index < end: 1.1758 - length = ord(text[index]) 1.1759 - index += 1 1.1760 - strs.append(text[index:index+length]) 1.1761 - index += length 1.1762 + def setText(self, text): 1.1763 + """Sets properties and text given a text field""" 1.1764 + self.text = text 1.1765 + try: 1.1766 + result = {} 1.1767 + end = len(text) 1.1768 + index = 0 1.1769 + strs = [] 1.1770 + while index < end: 1.1771 + length = ord(text[index]) 1.1772 + index += 1 1.1773 + strs.append(text[index:index+length]) 1.1774 + index += length 1.1775 1.1776 - for s in strs: 1.1777 - eindex = s.find('=') 1.1778 - if eindex == -1: 1.1779 - # No equals sign at all 1.1780 - key = s 1.1781 - value = 0 1.1782 - else: 1.1783 - key = s[:eindex] 1.1784 - value = s[eindex+1:] 1.1785 - if value == 'true': 1.1786 - value = 1 1.1787 - elif value == 'false' or not value: 1.1788 - value = 0 1.1789 + for s in strs: 1.1790 + eindex = s.find('=') 1.1791 + if eindex == -1: 1.1792 + # No equals sign at all 1.1793 + key = s 1.1794 + value = 0 1.1795 + else: 1.1796 + key = s[:eindex] 1.1797 + value = s[eindex+1:] 1.1798 + if value == 'true': 1.1799 + value = 1 1.1800 + elif value == 'false' or not value: 1.1801 + value = 0 1.1802 1.1803 - # Only update non-existent properties 1.1804 - if key and result.get(key) == None: 1.1805 - result[key] = value 1.1806 + # Only update non-existent properties 1.1807 + if key and result.get(key) == None: 1.1808 + result[key] = value 1.1809 1.1810 - self.properties = result 1.1811 - except: 1.1812 - traceback.print_exc() 1.1813 - self.properties = None 1.1814 + self.properties = result 1.1815 + except: 1.1816 + traceback.print_exc() 1.1817 + self.properties = None 1.1818 1.1819 - def getType(self): 1.1820 - """Type accessor""" 1.1821 - return self.type 1.1822 + def getType(self): 1.1823 + """Type accessor""" 1.1824 + return self.type 1.1825 1.1826 - def getName(self): 1.1827 - """Name accessor""" 1.1828 - if self.type is not None and self.name.endswith("." + self.type): 1.1829 - return self.name[:len(self.name) - len(self.type) - 1] 1.1830 - return self.name 1.1831 + def getName(self): 1.1832 + """Name accessor""" 1.1833 + if self.type is not None and self.name.endswith("." + self.type): 1.1834 + return self.name[:len(self.name) - len(self.type) - 1] 1.1835 + return self.name 1.1836 1.1837 - def getAddress(self): 1.1838 - """Address accessor""" 1.1839 - return self.address 1.1840 + def getAddress(self): 1.1841 + """Address accessor""" 1.1842 + return self.address 1.1843 1.1844 - def getPort(self): 1.1845 - """Port accessor""" 1.1846 - return self.port 1.1847 + def getPort(self): 1.1848 + """Port accessor""" 1.1849 + return self.port 1.1850 1.1851 - def getPriority(self): 1.1852 - """Pirority accessor""" 1.1853 - return self.priority 1.1854 + def getPriority(self): 1.1855 + """Pirority accessor""" 1.1856 + return self.priority 1.1857 1.1858 - def getWeight(self): 1.1859 - """Weight accessor""" 1.1860 - return self.weight 1.1861 + def getWeight(self): 1.1862 + """Weight accessor""" 1.1863 + return self.weight 1.1864 1.1865 - def getProperties(self): 1.1866 - """Properties accessor""" 1.1867 - return self.properties 1.1868 + def getProperties(self): 1.1869 + """Properties accessor""" 1.1870 + return self.properties 1.1871 1.1872 - def getText(self): 1.1873 - """Text accessor""" 1.1874 - return self.text 1.1875 + def getText(self): 1.1876 + """Text accessor""" 1.1877 + return self.text 1.1878 1.1879 - def getServer(self): 1.1880 - """Server accessor""" 1.1881 - return self.server 1.1882 + def getServer(self): 1.1883 + """Server accessor""" 1.1884 + return self.server 1.1885 1.1886 - def updateRecord(self, zeroconf, now, record): 1.1887 - """Updates service information from a DNS record""" 1.1888 - if record is not None and not record.isExpired(now): 1.1889 - if record.type == _TYPE_A: 1.1890 - #if record.name == self.name: 1.1891 - if record.name == self.server: 1.1892 - self.address = record.address 1.1893 - elif record.type == _TYPE_SRV: 1.1894 - if record.name == self.name: 1.1895 - self.server = record.server 1.1896 - self.port = record.port 1.1897 - self.weight = record.weight 1.1898 - self.priority = record.priority 1.1899 - #self.address = None 1.1900 - self.updateRecord(zeroconf, now, zeroconf.cache.getByDetails(self.server, _TYPE_A, _CLASS_IN)) 1.1901 - elif record.type == _TYPE_TXT: 1.1902 - if record.name == self.name: 1.1903 - self.setText(record.text) 1.1904 + def updateRecord(self, zeroconf, now, record): 1.1905 + """Updates service information from a DNS record""" 1.1906 + if record is not None and not record.isExpired(now): 1.1907 + if record.type == _TYPE_A: 1.1908 + #if record.name == self.name: 1.1909 + if record.name == self.server: 1.1910 + self.address = record.address 1.1911 + elif record.type == _TYPE_SRV: 1.1912 + if record.name == self.name: 1.1913 + self.server = record.server 1.1914 + self.port = record.port 1.1915 + self.weight = record.weight 1.1916 + self.priority = record.priority 1.1917 + #self.address = None 1.1918 + self.updateRecord(zeroconf, now, zeroconf.cache.getByDetails(self.server, _TYPE_A, _CLASS_IN)) 1.1919 + elif record.type == _TYPE_TXT: 1.1920 + if record.name == self.name: 1.1921 + self.setText(record.text) 1.1922 1.1923 - def request(self, zeroconf, timeout): 1.1924 - """Returns true if the service could be discovered on the 1.1925 - network, and updates this object with details discovered. 1.1926 - """ 1.1927 - now = currentTimeMillis() 1.1928 - delay = _LISTENER_TIME 1.1929 - next = now + delay 1.1930 - last = now + timeout 1.1931 - result = 0 1.1932 - try: 1.1933 - zeroconf.addListener(self, DNSQuestion(self.name, _TYPE_ANY, _CLASS_IN)) 1.1934 - while self.server is None or self.address is None or self.text is None: 1.1935 - if last <= now: 1.1936 - return 0 1.1937 - if next <= now: 1.1938 - out = DNSOutgoing(_FLAGS_QR_QUERY) 1.1939 - out.addQuestion(DNSQuestion(self.name, _TYPE_SRV, _CLASS_IN)) 1.1940 - out.addAnswerAtTime(zeroconf.cache.getByDetails(self.name, _TYPE_SRV, _CLASS_IN), now) 1.1941 - out.addQuestion(DNSQuestion(self.name, _TYPE_TXT, _CLASS_IN)) 1.1942 - out.addAnswerAtTime(zeroconf.cache.getByDetails(self.name, _TYPE_TXT, _CLASS_IN), now) 1.1943 - if self.server is not None: 1.1944 - out.addQuestion(DNSQuestion(self.server, _TYPE_A, _CLASS_IN)) 1.1945 - out.addAnswerAtTime(zeroconf.cache.getByDetails(self.server, _TYPE_A, _CLASS_IN), now) 1.1946 - zeroconf.send(out) 1.1947 - next = now + delay 1.1948 - delay = delay * 2 1.1949 + def request(self, zeroconf, timeout): 1.1950 + """Returns true if the service could be discovered on the 1.1951 + network, and updates this object with details discovered. 1.1952 + """ 1.1953 + now = currentTimeMillis() 1.1954 + delay = _LISTENER_TIME 1.1955 + next = now + delay 1.1956 + last = now + timeout 1.1957 + result = 0 1.1958 + try: 1.1959 + zeroconf.addListener(self, DNSQuestion(self.name, _TYPE_ANY, _CLASS_IN)) 1.1960 + while self.server is None or self.address is None or self.text is None: 1.1961 + if last <= now: 1.1962 + return 0 1.1963 + if next <= now: 1.1964 + out = DNSOutgoing(_FLAGS_QR_QUERY) 1.1965 + out.addQuestion(DNSQuestion(self.name, _TYPE_SRV, _CLASS_IN)) 1.1966 + out.addAnswerAtTime(zeroconf.cache.getByDetails(self.name, _TYPE_SRV, _CLASS_IN), now) 1.1967 + out.addQuestion(DNSQuestion(self.name, _TYPE_TXT, _CLASS_IN)) 1.1968 + out.addAnswerAtTime(zeroconf.cache.getByDetails(self.name, _TYPE_TXT, _CLASS_IN), now) 1.1969 + if self.server is not None: 1.1970 + out.addQuestion(DNSQuestion(self.server, _TYPE_A, _CLASS_IN)) 1.1971 + out.addAnswerAtTime(zeroconf.cache.getByDetails(self.server, _TYPE_A, _CLASS_IN), now) 1.1972 + zeroconf.send(out) 1.1973 + next = now + delay 1.1974 + delay = delay * 2 1.1975 1.1976 - zeroconf.wait(min(next, last) - now) 1.1977 - now = currentTimeMillis() 1.1978 - result = 1 1.1979 - finally: 1.1980 - zeroconf.removeListener(self) 1.1981 + zeroconf.wait(min(next, last) - now) 1.1982 + now = currentTimeMillis() 1.1983 + result = 1 1.1984 + finally: 1.1985 + zeroconf.removeListener(self) 1.1986 1.1987 - return result 1.1988 + return result 1.1989 1.1990 - def __eq__(self, other): 1.1991 - """Tests equality of service name""" 1.1992 - if isinstance(other, ServiceInfo): 1.1993 - return other.name == self.name 1.1994 - return 0 1.1995 + def __eq__(self, other): 1.1996 + """Tests equality of service name""" 1.1997 + if isinstance(other, ServiceInfo): 1.1998 + return other.name == self.name 1.1999 + return 0 1.2000 1.2001 - def __ne__(self, other): 1.2002 - """Non-equality test""" 1.2003 - return not self.__eq__(other) 1.2004 + def __ne__(self, other): 1.2005 + """Non-equality test""" 1.2006 + return not self.__eq__(other) 1.2007 1.2008 - def __repr__(self): 1.2009 - """String representation""" 1.2010 - result = "service[%s,%s:%s," % (self.name, socket.inet_ntoa(self.getAddress()), self.port) 1.2011 - if self.text is None: 1.2012 - result += "None" 1.2013 - else: 1.2014 - if len(self.text) < 20: 1.2015 - result += self.text 1.2016 - else: 1.2017 - result += self.text[:17] + "..." 1.2018 - result += "]" 1.2019 - return result 1.2020 + def __repr__(self): 1.2021 + """String representation""" 1.2022 + result = "service[%s,%s:%s," % (self.name, socket.inet_ntoa(self.getAddress()), self.port) 1.2023 + if self.text is None: 1.2024 + result += "None" 1.2025 + else: 1.2026 + if len(self.text) < 20: 1.2027 + result += self.text 1.2028 + else: 1.2029 + result += self.text[:17] + "..." 1.2030 + result += "]" 1.2031 + return result 1.2032 1.2033 1.2034 class Zeroconf(object): 1.2035 - """Implementation of Zeroconf Multicast DNS Service Discovery 1.2036 + """Implementation of Zeroconf Multicast DNS Service Discovery 1.2037 1.2038 - Supports registration, unregistration, queries and browsing. 1.2039 - """ 1.2040 - def __init__(self, bindaddress=None): 1.2041 - """Creates an instance of the Zeroconf class, establishing 1.2042 - multicast communications, listening and reaping threads.""" 1.2043 - globals()['_GLOBAL_DONE'] = 0 1.2044 - if bindaddress is None: 1.2045 - self.intf = socket.gethostbyname(socket.gethostname()) 1.2046 - else: 1.2047 - self.intf = bindaddress 1.2048 - self.group = ('', _MDNS_PORT) 1.2049 - self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 1.2050 - try: 1.2051 - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 1.2052 - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) 1.2053 - except: 1.2054 - # SO_REUSEADDR should be equivalent to SO_REUSEPORT for 1.2055 - # multicast UDP sockets (p 731, "TCP/IP Illustrated, 1.2056 - # Volume 2"), but some BSD-derived systems require 1.2057 - # SO_REUSEPORT to be specified explicity. Also, not all 1.2058 - # versions of Python have SO_REUSEPORT available. So 1.2059 - # if you're on a BSD-based system, and haven't upgraded 1.2060 - # to Python 2.3 yet, you may find this library doesn't 1.2061 - # work as expected. 1.2062 - # 1.2063 - pass 1.2064 - self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) 1.2065 - self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) 1.2066 - try: 1.2067 - self.socket.bind(self.group) 1.2068 - except: 1.2069 - # Some versions of linux raise an exception even though 1.2070 - # the SO_REUSE* options have been set, so ignore it 1.2071 - # 1.2072 - pass 1.2073 - #self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(self.intf) + socket.inet_aton('0.0.0.0')) 1.2074 - self.socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0')) 1.2075 + Supports registration, unregistration, queries and browsing. 1.2076 + """ 1.2077 + def __init__(self, bindaddress=None): 1.2078 + """Creates an instance of the Zeroconf class, establishing 1.2079 + multicast communications, listening and reaping threads.""" 1.2080 + globals()['_GLOBAL_DONE'] = 0 1.2081 + if bindaddress is None: 1.2082 + self.intf = socket.gethostbyname(socket.gethostname()) 1.2083 + else: 1.2084 + self.intf = bindaddress 1.2085 + self.group = ('', _MDNS_PORT) 1.2086 + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 1.2087 + try: 1.2088 + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 1.2089 + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) 1.2090 + except: 1.2091 + # SO_REUSEADDR should be equivalent to SO_REUSEPORT for 1.2092 + # multicast UDP sockets (p 731, "TCP/IP Illustrated, 1.2093 + # Volume 2"), but some BSD-derived systems require 1.2094 + # SO_REUSEPORT to be specified explicity. Also, not all 1.2095 + # versions of Python have SO_REUSEPORT available. So 1.2096 + # if you're on a BSD-based system, and haven't upgraded 1.2097 + # to Python 2.3 yet, you may find this library doesn't 1.2098 + # work as expected. 1.2099 + # 1.2100 + pass 1.2101 + self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) 1.2102 + self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) 1.2103 + try: 1.2104 + self.socket.bind(self.group) 1.2105 + except: 1.2106 + # Some versions of linux raise an exception even though 1.2107 + # the SO_REUSE* options have been set, so ignore it 1.2108 + # 1.2109 + pass 1.2110 + #self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(self.intf) + socket.inet_aton('0.0.0.0')) 1.2111 + self.socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0')) 1.2112 1.2113 - self.listeners = [] 1.2114 - self.browsers = [] 1.2115 - self.services = {} 1.2116 - self.servicetypes = {} 1.2117 + self.listeners = [] 1.2118 + self.browsers = [] 1.2119 + self.services = {} 1.2120 + self.servicetypes = {} 1.2121 1.2122 - self.cache = DNSCache() 1.2123 + self.cache = DNSCache() 1.2124 1.2125 - self.condition = threading.Condition() 1.2126 + self.condition = threading.Condition() 1.2127 1.2128 - self.engine = Engine(self) 1.2129 - self.listener = Listener(self) 1.2130 - self.reaper = Reaper(self) 1.2131 + self.engine = Engine(self) 1.2132 + self.listener = Listener(self) 1.2133 + self.reaper = Reaper(self) 1.2134 1.2135 - def isLoopback(self): 1.2136 - return self.intf.startswith("127.0.0.1") 1.2137 + def isLoopback(self): 1.2138 + return self.intf.startswith("127.0.0.1") 1.2139 1.2140 - def isLinklocal(self): 1.2141 - return self.intf.startswith("169.254.") 1.2142 + def isLinklocal(self): 1.2143 + return self.intf.startswith("169.254.") 1.2144 1.2145 - def wait(self, timeout): 1.2146 - """Calling thread waits for a given number of milliseconds or 1.2147 - until notified.""" 1.2148 - self.condition.acquire() 1.2149 - self.condition.wait(timeout/1000) 1.2150 - self.condition.release() 1.2151 + def wait(self, timeout): 1.2152 + """Calling thread waits for a given number of milliseconds or 1.2153 + until notified.""" 1.2154 + self.condition.acquire() 1.2155 + self.condition.wait(timeout/1000) 1.2156 + self.condition.release() 1.2157 1.2158 - def notifyAll(self): 1.2159 - """Notifies all waiting threads""" 1.2160 - self.condition.acquire() 1.2161 - self.condition.notifyAll() 1.2162 - self.condition.release() 1.2163 + def notifyAll(self): 1.2164 + """Notifies all waiting threads""" 1.2165 + self.condition.acquire() 1.2166 + self.condition.notifyAll() 1.2167 + self.condition.release() 1.2168 1.2169 - def getServiceInfo(self, type, name, timeout=3000): 1.2170 - """Returns network's service information for a particular 1.2171 - name and type, or None if no service matches by the timeout, 1.2172 - which defaults to 3 seconds.""" 1.2173 - info = ServiceInfo(type, name) 1.2174 - if info.request(self, timeout): 1.2175 - return info 1.2176 - return None 1.2177 + def getServiceInfo(self, type, name, timeout=3000): 1.2178 + """Returns network's service information for a particular 1.2179 + name and type, or None if no service matches by the timeout, 1.2180 + which defaults to 3 seconds.""" 1.2181 + info = ServiceInfo(type, name) 1.2182 + if info.request(self, timeout): 1.2183 + return info 1.2184 + return None 1.2185 1.2186 - def addServiceListener(self, type, listener): 1.2187 - """Adds a listener for a particular service type. This object 1.2188 - will then have its updateRecord method called when information 1.2189 - arrives for that type.""" 1.2190 - self.removeServiceListener(listener) 1.2191 - self.browsers.append(ServiceBrowser(self, type, listener)) 1.2192 + def addServiceListener(self, type, listener): 1.2193 + """Adds a listener for a particular service type. This object 1.2194 + will then have its updateRecord method called when information 1.2195 + arrives for that type.""" 1.2196 + self.removeServiceListener(listener) 1.2197 + self.browsers.append(ServiceBrowser(self, type, listener)) 1.2198 1.2199 - def removeServiceListener(self, listener): 1.2200 - """Removes a listener from the set that is currently listening.""" 1.2201 - for browser in self.browsers: 1.2202 - if browser.listener == listener: 1.2203 - browser.cancel() 1.2204 - del(browser) 1.2205 + def removeServiceListener(self, listener): 1.2206 + """Removes a listener from the set that is currently listening.""" 1.2207 + for browser in self.browsers: 1.2208 + if browser.listener == listener: 1.2209 + browser.cancel() 1.2210 + del(browser) 1.2211 1.2212 - def registerService(self, info, ttl=_DNS_TTL): 1.2213 - """Registers service information to the network with a default TTL 1.2214 - of 60 seconds. Zeroconf will then respond to requests for 1.2215 - information for that service. The name of the service may be 1.2216 - changed if needed to make it unique on the network.""" 1.2217 - self.checkService(info) 1.2218 - self.services[info.name.lower()] = info 1.2219 - if self.servicetypes.has_key(info.type): 1.2220 - self.servicetypes[info.type]+=1 1.2221 - else: 1.2222 - self.servicetypes[info.type]=1 1.2223 - now = currentTimeMillis() 1.2224 - nextTime = now 1.2225 - i = 0 1.2226 - while i < 3: 1.2227 - if now < nextTime: 1.2228 - self.wait(nextTime - now) 1.2229 - now = currentTimeMillis() 1.2230 - continue 1.2231 - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2232 - out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, ttl, info.name), 0) 1.2233 - out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, ttl, info.priority, info.weight, info.port, info.server), 0) 1.2234 - out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, ttl, info.text), 0) 1.2235 - if info.address: 1.2236 - out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, ttl, info.address), 0) 1.2237 - self.send(out) 1.2238 - i += 1 1.2239 - nextTime += _REGISTER_TIME 1.2240 + def registerService(self, info, ttl=_DNS_TTL): 1.2241 + """Registers service information to the network with a default TTL 1.2242 + of 60 seconds. Zeroconf will then respond to requests for 1.2243 + information for that service. The name of the service may be 1.2244 + changed if needed to make it unique on the network.""" 1.2245 + self.checkService(info) 1.2246 + self.services[info.name.lower()] = info 1.2247 + if self.servicetypes.has_key(info.type): 1.2248 + self.servicetypes[info.type]+=1 1.2249 + else: 1.2250 + self.servicetypes[info.type]=1 1.2251 + now = currentTimeMillis() 1.2252 + nextTime = now 1.2253 + i = 0 1.2254 + while i < 3: 1.2255 + if now < nextTime: 1.2256 + self.wait(nextTime - now) 1.2257 + now = currentTimeMillis() 1.2258 + continue 1.2259 + out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2260 + out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, ttl, info.name), 0) 1.2261 + out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, ttl, info.priority, info.weight, info.port, info.server), 0) 1.2262 + out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, ttl, info.text), 0) 1.2263 + if info.address: 1.2264 + out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, ttl, info.address), 0) 1.2265 + self.send(out) 1.2266 + i += 1 1.2267 + nextTime += _REGISTER_TIME 1.2268 1.2269 - def unregisterService(self, info): 1.2270 - """Unregister a service.""" 1.2271 - try: 1.2272 - del(self.services[info.name.lower()]) 1.2273 - if self.servicetypes[info.type]>1: 1.2274 - self.servicetypes[info.type]-=1 1.2275 - else: 1.2276 - del self.servicetypes[info.type] 1.2277 - except: 1.2278 - pass 1.2279 - now = currentTimeMillis() 1.2280 - nextTime = now 1.2281 - i = 0 1.2282 - while i < 3: 1.2283 - if now < nextTime: 1.2284 - self.wait(nextTime - now) 1.2285 - now = currentTimeMillis() 1.2286 - continue 1.2287 - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2288 - out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) 1.2289 - out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, 0, info.priority, info.weight, info.port, info.name), 0) 1.2290 - out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) 1.2291 - if info.address: 1.2292 - out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, 0, info.address), 0) 1.2293 - self.send(out) 1.2294 - i += 1 1.2295 - nextTime += _UNREGISTER_TIME 1.2296 + def unregisterService(self, info): 1.2297 + """Unregister a service.""" 1.2298 + try: 1.2299 + del(self.services[info.name.lower()]) 1.2300 + if self.servicetypes[info.type]>1: 1.2301 + self.servicetypes[info.type]-=1 1.2302 + else: 1.2303 + del self.servicetypes[info.type] 1.2304 + except: 1.2305 + pass 1.2306 + now = currentTimeMillis() 1.2307 + nextTime = now 1.2308 + i = 0 1.2309 + while i < 3: 1.2310 + if now < nextTime: 1.2311 + self.wait(nextTime - now) 1.2312 + now = currentTimeMillis() 1.2313 + continue 1.2314 + out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2315 + out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) 1.2316 + out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, 0, info.priority, info.weight, info.port, info.name), 0) 1.2317 + out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) 1.2318 + if info.address: 1.2319 + out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, 0, info.address), 0) 1.2320 + self.send(out) 1.2321 + i += 1 1.2322 + nextTime += _UNREGISTER_TIME 1.2323 1.2324 - def unregisterAllServices(self): 1.2325 - """Unregister all registered services.""" 1.2326 - if len(self.services) > 0: 1.2327 - now = currentTimeMillis() 1.2328 - nextTime = now 1.2329 - i = 0 1.2330 - while i < 3: 1.2331 - if now < nextTime: 1.2332 - self.wait(nextTime - now) 1.2333 - now = currentTimeMillis() 1.2334 - continue 1.2335 - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2336 - for info in self.services.values(): 1.2337 - out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) 1.2338 - out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, 0, info.priority, info.weight, info.port, info.server), 0) 1.2339 - out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) 1.2340 - if info.address: 1.2341 - out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, 0, info.address), 0) 1.2342 - self.send(out) 1.2343 - i += 1 1.2344 - nextTime += _UNREGISTER_TIME 1.2345 + def unregisterAllServices(self): 1.2346 + """Unregister all registered services.""" 1.2347 + if len(self.services) > 0: 1.2348 + now = currentTimeMillis() 1.2349 + nextTime = now 1.2350 + i = 0 1.2351 + while i < 3: 1.2352 + if now < nextTime: 1.2353 + self.wait(nextTime - now) 1.2354 + now = currentTimeMillis() 1.2355 + continue 1.2356 + out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2357 + for info in self.services.values(): 1.2358 + out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) 1.2359 + out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, 0, info.priority, info.weight, info.port, info.server), 0) 1.2360 + out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) 1.2361 + if info.address: 1.2362 + out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, 0, info.address), 0) 1.2363 + self.send(out) 1.2364 + i += 1 1.2365 + nextTime += _UNREGISTER_TIME 1.2366 1.2367 - def checkService(self, info): 1.2368 - """Checks the network for a unique service name, modifying the 1.2369 - ServiceInfo passed in if it is not unique.""" 1.2370 - now = currentTimeMillis() 1.2371 - nextTime = now 1.2372 - i = 0 1.2373 - while i < 3: 1.2374 - for record in self.cache.entriesWithName(info.type): 1.2375 - if record.type == _TYPE_PTR and not record.isExpired(now) and record.alias == info.name: 1.2376 - if (info.name.find('.') < 0): 1.2377 - info.name = info.name + ".[" + info.address + ":" + info.port + "]." + info.type 1.2378 - self.checkService(info) 1.2379 - return 1.2380 - raise NonUniqueNameException 1.2381 - if now < nextTime: 1.2382 - self.wait(nextTime - now) 1.2383 - now = currentTimeMillis() 1.2384 - continue 1.2385 - out = DNSOutgoing(_FLAGS_QR_QUERY | _FLAGS_AA) 1.2386 - self.debug = out 1.2387 - out.addQuestion(DNSQuestion(info.type, _TYPE_PTR, _CLASS_IN)) 1.2388 - out.addAuthorativeAnswer(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, info.name)) 1.2389 - self.send(out) 1.2390 - i += 1 1.2391 - nextTime += _CHECK_TIME 1.2392 + def checkService(self, info): 1.2393 + """Checks the network for a unique service name, modifying the 1.2394 + ServiceInfo passed in if it is not unique.""" 1.2395 + now = currentTimeMillis() 1.2396 + nextTime = now 1.2397 + i = 0 1.2398 + while i < 3: 1.2399 + for record in self.cache.entriesWithName(info.type): 1.2400 + if record.type == _TYPE_PTR and not record.isExpired(now) and record.alias == info.name: 1.2401 + if (info.name.find('.') < 0): 1.2402 + info.name = info.name + ".[" + info.address + ":" + info.port + "]." + info.type 1.2403 + self.checkService(info) 1.2404 + return 1.2405 + raise NonUniqueNameException 1.2406 + if now < nextTime: 1.2407 + self.wait(nextTime - now) 1.2408 + now = currentTimeMillis() 1.2409 + continue 1.2410 + out = DNSOutgoing(_FLAGS_QR_QUERY | _FLAGS_AA) 1.2411 + self.debug = out 1.2412 + out.addQuestion(DNSQuestion(info.type, _TYPE_PTR, _CLASS_IN)) 1.2413 + out.addAuthorativeAnswer(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, info.name)) 1.2414 + self.send(out) 1.2415 + i += 1 1.2416 + nextTime += _CHECK_TIME 1.2417 1.2418 - def addListener(self, listener, question): 1.2419 - """Adds a listener for a given question. The listener will have 1.2420 - its updateRecord method called when information is available to 1.2421 - answer the question.""" 1.2422 - now = currentTimeMillis() 1.2423 - self.listeners.append(listener) 1.2424 - if question is not None: 1.2425 - for record in self.cache.entriesWithName(question.name): 1.2426 - if question.answeredBy(record) and not record.isExpired(now): 1.2427 - listener.updateRecord(self, now, record) 1.2428 - self.notifyAll() 1.2429 + def addListener(self, listener, question): 1.2430 + """Adds a listener for a given question. The listener will have 1.2431 + its updateRecord method called when information is available to 1.2432 + answer the question.""" 1.2433 + now = currentTimeMillis() 1.2434 + self.listeners.append(listener) 1.2435 + if question is not None: 1.2436 + for record in self.cache.entriesWithName(question.name): 1.2437 + if question.answeredBy(record) and not record.isExpired(now): 1.2438 + listener.updateRecord(self, now, record) 1.2439 + self.notifyAll() 1.2440 1.2441 - def removeListener(self, listener): 1.2442 - """Removes a listener.""" 1.2443 - try: 1.2444 - self.listeners.remove(listener) 1.2445 - self.notifyAll() 1.2446 - except: 1.2447 - pass 1.2448 + def removeListener(self, listener): 1.2449 + """Removes a listener.""" 1.2450 + try: 1.2451 + self.listeners.remove(listener) 1.2452 + self.notifyAll() 1.2453 + except: 1.2454 + pass 1.2455 1.2456 - def updateRecord(self, now, rec): 1.2457 - """Used to notify listeners of new information that has updated 1.2458 - a record.""" 1.2459 - for listener in self.listeners: 1.2460 - listener.updateRecord(self, now, rec) 1.2461 - self.notifyAll() 1.2462 + def updateRecord(self, now, rec): 1.2463 + """Used to notify listeners of new information that has updated 1.2464 + a record.""" 1.2465 + for listener in self.listeners: 1.2466 + listener.updateRecord(self, now, rec) 1.2467 + self.notifyAll() 1.2468 1.2469 - def handleResponse(self, msg): 1.2470 - """Deal with incoming response packets. All answers 1.2471 - are held in the cache, and listeners are notified.""" 1.2472 - now = currentTimeMillis() 1.2473 - for record in msg.answers: 1.2474 - expired = record.isExpired(now) 1.2475 - if record in self.cache.entries(): 1.2476 - if expired: 1.2477 - self.cache.remove(record) 1.2478 - else: 1.2479 - entry = self.cache.get(record) 1.2480 - if entry is not None: 1.2481 - entry.resetTTL(record) 1.2482 - record = entry 1.2483 - else: 1.2484 - self.cache.add(record) 1.2485 + def handleResponse(self, msg): 1.2486 + """Deal with incoming response packets. All answers 1.2487 + are held in the cache, and listeners are notified.""" 1.2488 + now = currentTimeMillis() 1.2489 + for record in msg.answers: 1.2490 + expired = record.isExpired(now) 1.2491 + if record in self.cache.entries(): 1.2492 + if expired: 1.2493 + self.cache.remove(record) 1.2494 + else: 1.2495 + entry = self.cache.get(record) 1.2496 + if entry is not None: 1.2497 + entry.resetTTL(record) 1.2498 + record = entry 1.2499 + else: 1.2500 + self.cache.add(record) 1.2501 1.2502 - self.updateRecord(now, record) 1.2503 + self.updateRecord(now, record) 1.2504 1.2505 - def handleQuery(self, msg, addr, port): 1.2506 - """Deal with incoming query packets. Provides a response if 1.2507 - possible.""" 1.2508 - out = None 1.2509 + def handleQuery(self, msg, addr, port): 1.2510 + """Deal with incoming query packets. Provides a response if 1.2511 + possible.""" 1.2512 + out = None 1.2513 1.2514 - # Support unicast client responses 1.2515 - # 1.2516 - if port != _MDNS_PORT: 1.2517 - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA, 0) 1.2518 - for question in msg.questions: 1.2519 - out.addQuestion(question) 1.2520 + # Support unicast client responses 1.2521 + # 1.2522 + if port != _MDNS_PORT: 1.2523 + out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA, 0) 1.2524 + for question in msg.questions: 1.2525 + out.addQuestion(question) 1.2526 1.2527 - for question in msg.questions: 1.2528 - if question.type == _TYPE_PTR: 1.2529 - if question.name == "_services._dns-sd._udp.local.": 1.2530 - for stype in self.servicetypes.keys(): 1.2531 - if out is None: 1.2532 - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2533 - out.addAnswer(msg, DNSPointer("_services._dns-sd._udp.local.", _TYPE_PTR, _CLASS_IN, _DNS_TTL, stype)) 1.2534 - for service in self.services.values(): 1.2535 - if question.name == service.type: 1.2536 - if out is None: 1.2537 - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2538 - out.addAnswer(msg, DNSPointer(service.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, service.name)) 1.2539 - else: 1.2540 - try: 1.2541 - if out is None: 1.2542 - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2543 + for question in msg.questions: 1.2544 + if question.type == _TYPE_PTR: 1.2545 + if question.name == "_services._dns-sd._udp.local.": 1.2546 + for stype in self.servicetypes.keys(): 1.2547 + if out is None: 1.2548 + out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2549 + out.addAnswer(msg, DNSPointer("_services._dns-sd._udp.local.", _TYPE_PTR, _CLASS_IN, _DNS_TTL, stype)) 1.2550 + for service in self.services.values(): 1.2551 + if question.name == service.type: 1.2552 + if out is None: 1.2553 + out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2554 + out.addAnswer(msg, DNSPointer(service.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, service.name)) 1.2555 + else: 1.2556 + try: 1.2557 + if out is None: 1.2558 + out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) 1.2559 1.2560 - # Answer A record queries for any service addresses we know 1.2561 - if question.type == _TYPE_A or question.type == _TYPE_ANY: 1.2562 - for service in self.services.values(): 1.2563 - if service.server == question.name.lower(): 1.2564 - out.addAnswer(msg, DNSAddress(question.name, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) 1.2565 + # Answer A record queries for any service addresses we know 1.2566 + if question.type == _TYPE_A or question.type == _TYPE_ANY: 1.2567 + for service in self.services.values(): 1.2568 + if service.server == question.name.lower(): 1.2569 + out.addAnswer(msg, DNSAddress(question.name, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) 1.2570 1.2571 - service = self.services.get(question.name.lower(), None) 1.2572 - if not service: continue 1.2573 + service = self.services.get(question.name.lower(), None) 1.2574 + if not service: continue 1.2575 1.2576 - if question.type == _TYPE_SRV or question.type == _TYPE_ANY: 1.2577 - out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.priority, service.weight, service.port, service.server)) 1.2578 - if question.type == _TYPE_TXT or question.type == _TYPE_ANY: 1.2579 - out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text)) 1.2580 - if question.type == _TYPE_SRV: 1.2581 - out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) 1.2582 - except: 1.2583 - traceback.print_exc() 1.2584 + if question.type == _TYPE_SRV or question.type == _TYPE_ANY: 1.2585 + out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.priority, service.weight, service.port, service.server)) 1.2586 + if question.type == _TYPE_TXT or question.type == _TYPE_ANY: 1.2587 + out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text)) 1.2588 + if question.type == _TYPE_SRV: 1.2589 + out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) 1.2590 + except: 1.2591 + traceback.print_exc() 1.2592 1.2593 - if out is not None and out.answers: 1.2594 - out.id = msg.id 1.2595 - self.send(out, addr, port) 1.2596 + if out is not None and out.answers: 1.2597 + out.id = msg.id 1.2598 + self.send(out, addr, port) 1.2599 1.2600 - def send(self, out, addr = _MDNS_ADDR, port = _MDNS_PORT): 1.2601 - """Sends an outgoing packet.""" 1.2602 - # This is a quick test to see if we can parse the packets we generate 1.2603 - #temp = DNSIncoming(out.packet()) 1.2604 - try: 1.2605 - self.socket.sendto(out.packet(), 0, (addr, port)) 1.2606 - except: 1.2607 - # Ignore this, it may be a temporary loss of network connection 1.2608 - pass 1.2609 + def send(self, out, addr = _MDNS_ADDR, port = _MDNS_PORT): 1.2610 + """Sends an outgoing packet.""" 1.2611 + # This is a quick test to see if we can parse the packets we generate 1.2612 + #temp = DNSIncoming(out.packet()) 1.2613 + try: 1.2614 + self.socket.sendto(out.packet(), 0, (addr, port)) 1.2615 + except: 1.2616 + # Ignore this, it may be a temporary loss of network connection 1.2617 + pass 1.2618 1.2619 - def close(self): 1.2620 - """Ends the background threads, and prevent this instance from 1.2621 - servicing further queries.""" 1.2622 - if globals()['_GLOBAL_DONE'] == 0: 1.2623 - globals()['_GLOBAL_DONE'] = 1 1.2624 - self.notifyAll() 1.2625 - self.engine.notify() 1.2626 - self.unregisterAllServices() 1.2627 - self.socket.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0')) 1.2628 - self.socket.close() 1.2629 + def close(self): 1.2630 + """Ends the background threads, and prevent this instance from 1.2631 + servicing further queries.""" 1.2632 + if globals()['_GLOBAL_DONE'] == 0: 1.2633 + globals()['_GLOBAL_DONE'] = 1 1.2634 + self.notifyAll() 1.2635 + self.engine.notify() 1.2636 + self.unregisterAllServices() 1.2637 + self.socket.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0')) 1.2638 + self.socket.close() 1.2639 1.2640 # Test a few module features, including service registration, service 1.2641 # query (for Zoe), and service unregistration. 1.2642 1.2643 if __name__ == '__main__': 1.2644 - print "Multicast DNS Service Discovery for Python, version", __version__ 1.2645 - r = Zeroconf() 1.2646 - print "1. Testing registration of a service..." 1.2647 - desc = {'version':'0.10','a':'test value', 'b':'another value'} 1.2648 - info = ServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.", socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc) 1.2649 - print " Registering service..." 1.2650 - r.registerService(info) 1.2651 - print " Registration done." 1.2652 - print "2. Testing query of service information..." 1.2653 - print " Getting ZOE service:", str(r.getServiceInfo("_http._tcp.local.", "ZOE._http._tcp.local.")) 1.2654 - print " Query done." 1.2655 - print "3. Testing query of own service..." 1.2656 - print " Getting self:", str(r.getServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.")) 1.2657 - print " Query done." 1.2658 - print "4. Testing unregister of service information..." 1.2659 - r.unregisterService(info) 1.2660 - print " Unregister done." 1.2661 - r.close() 1.2662 + print "Multicast DNS Service Discovery for Python, version", __version__ 1.2663 + r = Zeroconf() 1.2664 + print "1. Testing registration of a service..." 1.2665 + desc = {'version':'0.10','a':'test value', 'b':'another value'} 1.2666 + info = ServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.", socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc) 1.2667 + print " Registering service..." 1.2668 + r.registerService(info) 1.2669 + print " Registration done." 1.2670 + print "2. Testing query of service information..." 1.2671 + print " Getting ZOE service:", str(r.getServiceInfo("_http._tcp.local.", "ZOE._http._tcp.local.")) 1.2672 + print " Query done." 1.2673 + print "3. Testing query of own service..." 1.2674 + print " Getting self:", str(r.getServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.")) 1.2675 + print " Query done." 1.2676 + print "4. Testing unregister of service information..." 1.2677 + r.unregisterService(info) 1.2678 + print " Unregister done." 1.2679 + r.close()
