Mercurial > hg
annotate hgext/zeroconf/Zeroconf.py @ 36523:e7411fb7ba7f
wireprotoserver: ability to run an SSH server until an event is set
It seems useful to be able to start an SSH protocol server that
won't run forever and won't call sys.exit() when it stops. This
could be used to facilitate intra-process testing of the SSH
protocol, for example.
We teach the server function to loop until a threading.Event is set
and invent a new API to run the server until an event is set. It also
won't sys.exit() afterwards.
There aren't many callers of serve_forever(). So we could refactor
them relatively easily. But I was lazy.
threading.Event might be a bit heavyweight. An alternative would be
a list whose only elements is changed. We can't use a simple scalar
value like a bool or int because those types are immutable. Events
are what you use in systems programming for this use case, so the
use of threading.Event seems justified.
Differential Revision: https://phab.mercurial-scm.org/D2461
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Sat, 24 Feb 2018 12:07:21 -0800 |
parents | 31451f3f4b56 |
children | 876494fd967d |
rev | line source |
---|---|
28296
a73394e7b47c
zeroconf: use absolute_import
timeless <timeless@mozdev.org>
parents:
28295
diff
changeset
|
1 from __future__ import absolute_import, print_function |
28295
01815c159856
zeroconf: use print function
timeless <timeless@mozdev.org>
parents:
28249
diff
changeset
|
2 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
3 """ Multicast DNS Service Discovery for Python, v0.12 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
4 Copyright (C) 2003, Paul Scott-Murphy |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
5 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
6 This module provides a framework for the use of DNS Service Discovery |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
7 using IP multicast. It has been tested against the JRendezvous |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
8 implementation from <a href="http://strangeberry.com">StrangeBerry</a>, |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
9 and against the mDNSResponder from Mac OS X 10.3.8. |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
10 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
11 This library is free software; you can redistribute it and/or |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
12 modify it under the terms of the GNU Lesser General Public |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
13 License as published by the Free Software Foundation; either |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
14 version 2.1 of the License, or (at your option) any later version. |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
15 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
16 This library is distributed in the hope that it will be useful, |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
17 but WITHOUT ANY WARRANTY; without even the implied warranty of |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
19 Lesser General Public License for more details. |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
20 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
21 You should have received a copy of the GNU Lesser General Public |
15782
7de7630053cb
Remove FSF mailing address from GPL headers
Martin Geisler <mg@aragost.com>
parents:
14494
diff
changeset
|
22 License along with this library; if not, see |
7de7630053cb
Remove FSF mailing address from GPL headers
Martin Geisler <mg@aragost.com>
parents:
14494
diff
changeset
|
23 <http://www.gnu.org/licenses/>. |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
24 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
25 """ |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
26 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
27 """0.12 update - allow selection of binding interface |
28298 | 28 typo fix - Thanks A. M. Kuchlingi |
29 removed all use of word 'Rendezvous' - this is an API change""" | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
30 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
31 """0.11 update - correction to comments for addListener method |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
32 support for new record types seen from OS X |
28298 | 33 - IPv6 address |
34 - hostinfo | |
35 ignore unknown DNS record types | |
36 fixes to name decoding | |
28299 | 37 works alongside other processes using port 5353 (e.g. Mac OS X) |
28298 | 38 tested against Mac OS X 10.3.2's mDNSResponder |
39 corrections to removal of list entries for service browser""" | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
40 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
41 """0.10 update - Jonathon Paisley contributed these corrections: |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
42 always multicast replies, even when query is unicast |
28298 | 43 correct a pointer encoding problem |
44 can now write records in any order | |
45 traceback shown on failure | |
46 better TXT record parsing | |
47 server is now separate from name | |
48 can cancel a service browser | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
49 |
28298 | 50 modified some unit tests to accommodate these changes""" |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
51 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
52 """0.09 update - remove all records on service unregistration |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
53 fix DOS security problem with readName""" |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
54 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
55 """0.08 update - changed licensing to LGPL""" |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
56 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
57 """0.07 update - faster shutdown on engine |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
58 pointer encoding of outgoing names |
28298 | 59 ServiceBrowser now works |
60 new unit tests""" | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
61 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
62 """0.06 update - small improvements with unit tests |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
63 added defined exception types |
28298 | 64 new style objects |
65 fixed hostname/interface problem | |
66 fixed socket timeout problem | |
67 fixed addServiceListener() typo bug | |
68 using select() for socket reads | |
69 tested on Debian unstable with Python 2.2.2""" | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
70 |
17424
e7cfe3587ea4
fix trivial spelling errors
Mads Kiilerich <mads@kiilerich.com>
parents:
15782
diff
changeset
|
71 """0.05 update - ensure case insensitivity on domain names |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
72 support for unicast DNS queries""" |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
73 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
74 """0.04 update - added some unit tests |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
75 added __ne__ adjuncts where required |
28298 | 76 ensure names end in '.local.' |
77 timeout on receiving socket for clean shutdown""" | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
78 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
79 __author__ = "Paul Scott-Murphy" |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
80 __email__ = "paul at scott dash murphy dot com" |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
81 __version__ = "0.12" |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
82 |
34447
5385b76fd1fd
zeroconf: do not crash if socket being read is closed by another thread
Jun Wu <quark@fb.com>
parents:
28504
diff
changeset
|
83 import errno |
28422
e2c6092ad422
zeroconf: replace reduce+add with itertools.chain
timeless <timeless@mozdev.org>
parents:
28421
diff
changeset
|
84 import itertools |
28296
a73394e7b47c
zeroconf: use absolute_import
timeless <timeless@mozdev.org>
parents:
28295
diff
changeset
|
85 import select |
a73394e7b47c
zeroconf: use absolute_import
timeless <timeless@mozdev.org>
parents:
28295
diff
changeset
|
86 import socket |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
87 import string |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
88 import struct |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
89 import threading |
28296
a73394e7b47c
zeroconf: use absolute_import
timeless <timeless@mozdev.org>
parents:
28295
diff
changeset
|
90 import time |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
91 import traceback |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
92 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
93 __all__ = ["Zeroconf", "ServiceInfo", "ServiceBrowser"] |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
94 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
95 # hook for threads |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
96 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
97 globals()['_GLOBAL_DONE'] = 0 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
98 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
99 # Some timing constants |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
100 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
101 _UNREGISTER_TIME = 125 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
102 _CHECK_TIME = 175 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
103 _REGISTER_TIME = 225 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
104 _LISTENER_TIME = 200 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
105 _BROWSER_TIME = 500 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
106 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
107 # Some DNS constants |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
108 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
109 _MDNS_ADDR = '224.0.0.251' |
28297 | 110 _MDNS_PORT = 5353 |
111 _DNS_PORT = 53 | |
112 _DNS_TTL = 60 * 60 # one hour default TTL | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
113 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
114 _MAX_MSG_TYPICAL = 1460 # unused |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
115 _MAX_MSG_ABSOLUTE = 8972 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
116 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
117 _FLAGS_QR_MASK = 0x8000 # query response mask |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
118 _FLAGS_QR_QUERY = 0x0000 # query |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
119 _FLAGS_QR_RESPONSE = 0x8000 # response |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
120 |
17476 | 121 _FLAGS_AA = 0x0400 # Authoritative answer |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
122 _FLAGS_TC = 0x0200 # Truncated |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
123 _FLAGS_RD = 0x0100 # Recursion desired |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
124 _FLAGS_RA = 0x8000 # Recursion available |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
125 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
126 _FLAGS_Z = 0x0040 # Zero |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
127 _FLAGS_AD = 0x0020 # Authentic data |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
128 _FLAGS_CD = 0x0010 # Checking disabled |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
129 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
130 _CLASS_IN = 1 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
131 _CLASS_CS = 2 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
132 _CLASS_CH = 3 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
133 _CLASS_HS = 4 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
134 _CLASS_NONE = 254 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
135 _CLASS_ANY = 255 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
136 _CLASS_MASK = 0x7FFF |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
137 _CLASS_UNIQUE = 0x8000 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
138 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
139 _TYPE_A = 1 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
140 _TYPE_NS = 2 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
141 _TYPE_MD = 3 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
142 _TYPE_MF = 4 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
143 _TYPE_CNAME = 5 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
144 _TYPE_SOA = 6 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
145 _TYPE_MB = 7 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
146 _TYPE_MG = 8 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
147 _TYPE_MR = 9 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
148 _TYPE_NULL = 10 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
149 _TYPE_WKS = 11 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
150 _TYPE_PTR = 12 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
151 _TYPE_HINFO = 13 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
152 _TYPE_MINFO = 14 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
153 _TYPE_MX = 15 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
154 _TYPE_TXT = 16 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
155 _TYPE_AAAA = 28 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
156 _TYPE_SRV = 33 |
27637
b502138f5faa
cleanup: remove superfluous space after space after equals (python)
timeless <timeless@mozdev.org>
parents:
17537
diff
changeset
|
157 _TYPE_ANY = 255 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
158 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
159 # Mapping constants to names |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
160 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
161 _CLASSES = { _CLASS_IN : "in", |
28298 | 162 _CLASS_CS : "cs", |
163 _CLASS_CH : "ch", | |
164 _CLASS_HS : "hs", | |
165 _CLASS_NONE : "none", | |
166 _CLASS_ANY : "any" } | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
167 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
168 _TYPES = { _TYPE_A : "a", |
28298 | 169 _TYPE_NS : "ns", |
170 _TYPE_MD : "md", | |
171 _TYPE_MF : "mf", | |
172 _TYPE_CNAME : "cname", | |
173 _TYPE_SOA : "soa", | |
174 _TYPE_MB : "mb", | |
175 _TYPE_MG : "mg", | |
176 _TYPE_MR : "mr", | |
177 _TYPE_NULL : "null", | |
178 _TYPE_WKS : "wks", | |
179 _TYPE_PTR : "ptr", | |
180 _TYPE_HINFO : "hinfo", | |
181 _TYPE_MINFO : "minfo", | |
182 _TYPE_MX : "mx", | |
183 _TYPE_TXT : "txt", | |
184 _TYPE_AAAA : "quada", | |
185 _TYPE_SRV : "srv", | |
186 _TYPE_ANY : "any" } | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
187 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
188 # utility functions |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
189 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
190 def currentTimeMillis(): |
28298 | 191 """Current system time in milliseconds""" |
192 return time.time() * 1000 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
193 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
194 # Exceptions |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
195 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
196 class NonLocalNameException(Exception): |
28298 | 197 pass |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
198 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
199 class NonUniqueNameException(Exception): |
28298 | 200 pass |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
201 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
202 class NamePartTooLongException(Exception): |
28298 | 203 pass |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
204 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
205 class AbstractMethodException(Exception): |
28298 | 206 pass |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
207 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
208 class BadTypeInNameException(Exception): |
28298 | 209 pass |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
210 |
11435
7c58cde598fe
zeroconf: Use BadDomainName exception instead of string exceptions
Javi Merino <cibervicho@gmail.com>
parents:
10514
diff
changeset
|
211 class BadDomainName(Exception): |
28298 | 212 def __init__(self, pos): |
213 Exception.__init__(self, "at position %s" % pos) | |
11435
7c58cde598fe
zeroconf: Use BadDomainName exception instead of string exceptions
Javi Merino <cibervicho@gmail.com>
parents:
10514
diff
changeset
|
214 |
7c58cde598fe
zeroconf: Use BadDomainName exception instead of string exceptions
Javi Merino <cibervicho@gmail.com>
parents:
10514
diff
changeset
|
215 class BadDomainNameCircular(BadDomainName): |
28298 | 216 pass |
11435
7c58cde598fe
zeroconf: Use BadDomainName exception instead of string exceptions
Javi Merino <cibervicho@gmail.com>
parents:
10514
diff
changeset
|
217 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
218 # implementation classes |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
219 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
220 class DNSEntry(object): |
28298 | 221 """A DNS entry""" |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
222 |
28298 | 223 def __init__(self, name, type, clazz): |
224 self.key = string.lower(name) | |
225 self.name = name | |
226 self.type = type | |
227 self.clazz = clazz & _CLASS_MASK | |
228 self.unique = (clazz & _CLASS_UNIQUE) != 0 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
229 |
28298 | 230 def __eq__(self, other): |
231 """Equality test on name, type, and class""" | |
232 if isinstance(other, DNSEntry): | |
28299 | 233 return (self.name == other.name and self.type == other.type and |
234 self.clazz == other.clazz) | |
28298 | 235 return 0 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
236 |
28298 | 237 def __ne__(self, other): |
238 """Non-equality test""" | |
239 return not self.__eq__(other) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
240 |
28298 | 241 def getClazz(self, clazz): |
242 """Class accessor""" | |
243 try: | |
244 return _CLASSES[clazz] | |
245 except KeyError: | |
246 return "?(%s)" % (clazz) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
247 |
28298 | 248 def getType(self, type): |
249 """Type accessor""" | |
250 try: | |
251 return _TYPES[type] | |
252 except KeyError: | |
253 return "?(%s)" % (type) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
254 |
28298 | 255 def toString(self, hdr, other): |
256 """String representation with additional information""" | |
28299 | 257 result = ("%s[%s,%s" % |
258 (hdr, self.getType(self.type), self.getClazz(self.clazz))) | |
28298 | 259 if self.unique: |
260 result += "-unique," | |
261 else: | |
262 result += "," | |
263 result += self.name | |
264 if other is not None: | |
265 result += ",%s]" % (other) | |
266 else: | |
267 result += "]" | |
268 return result | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
269 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
270 class DNSQuestion(DNSEntry): |
28298 | 271 """A DNS question entry""" |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
272 |
28298 | 273 def __init__(self, name, type, clazz): |
274 if not name.endswith(".local."): | |
275 raise NonLocalNameException(name) | |
276 DNSEntry.__init__(self, name, type, clazz) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
277 |
28298 | 278 def answeredBy(self, rec): |
279 """Returns true if the question is answered by the record""" | |
28299 | 280 return (self.clazz == rec.clazz and |
281 (self.type == rec.type or self.type == _TYPE_ANY) and | |
282 self.name == rec.name) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
283 |
28298 | 284 def __repr__(self): |
285 """String representation""" | |
286 return DNSEntry.toString(self, "question", None) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
287 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
288 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
289 class DNSRecord(DNSEntry): |
28298 | 290 """A DNS record - like a DNS entry, but has a TTL""" |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
291 |
28298 | 292 def __init__(self, name, type, clazz, ttl): |
293 DNSEntry.__init__(self, name, type, clazz) | |
294 self.ttl = ttl | |
295 self.created = currentTimeMillis() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
296 |
28298 | 297 def __eq__(self, other): |
298 """Tests equality as per DNSRecord""" | |
299 if isinstance(other, DNSRecord): | |
300 return DNSEntry.__eq__(self, other) | |
301 return 0 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
302 |
28298 | 303 def suppressedBy(self, msg): |
304 """Returns true if any answer in a message can suffice for the | |
305 information held in this record.""" | |
306 for record in msg.answers: | |
307 if self.suppressedByAnswer(record): | |
308 return 1 | |
309 return 0 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
310 |
28298 | 311 def suppressedByAnswer(self, other): |
312 """Returns true if another record has same name, type and class, | |
313 and if its TTL is at least half of this record's.""" | |
314 if self == other and other.ttl > (self.ttl / 2): | |
315 return 1 | |
316 return 0 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
317 |
28298 | 318 def getExpirationTime(self, percent): |
319 """Returns the time at which this record will have expired | |
320 by a certain percentage.""" | |
321 return self.created + (percent * self.ttl * 10) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
322 |
28298 | 323 def getRemainingTTL(self, now): |
324 """Returns the remaining TTL in seconds.""" | |
325 return max(0, (self.getExpirationTime(100) - now) / 1000) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
326 |
28298 | 327 def isExpired(self, now): |
328 """Returns true if this record has expired.""" | |
329 return self.getExpirationTime(100) <= now | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
330 |
28298 | 331 def isStale(self, now): |
332 """Returns true if this record is at least half way expired.""" | |
333 return self.getExpirationTime(50) <= now | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
334 |
28298 | 335 def resetTTL(self, other): |
336 """Sets this record's TTL and created time to that of | |
337 another record.""" | |
338 self.created = other.created | |
339 self.ttl = other.ttl | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
340 |
28298 | 341 def write(self, out): |
342 """Abstract method""" | |
343 raise AbstractMethodException | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
344 |
28298 | 345 def toString(self, other): |
346 """String representation with additional information""" | |
28299 | 347 arg = ("%s/%s,%s" % |
348 (self.ttl, self.getRemainingTTL(currentTimeMillis()), other)) | |
28298 | 349 return DNSEntry.toString(self, "record", arg) |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
350 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
351 class DNSAddress(DNSRecord): |
28298 | 352 """A DNS address record""" |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
353 |
28298 | 354 def __init__(self, name, type, clazz, ttl, address): |
355 DNSRecord.__init__(self, name, type, clazz, ttl) | |
356 self.address = address | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
357 |
28298 | 358 def write(self, out): |
359 """Used in constructing an outgoing packet""" | |
360 out.writeString(self.address, len(self.address)) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
361 |
28298 | 362 def __eq__(self, other): |
363 """Tests equality on address""" | |
364 if isinstance(other, DNSAddress): | |
365 return self.address == other.address | |
366 return 0 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
367 |
28298 | 368 def __repr__(self): |
369 """String representation""" | |
370 try: | |
371 return socket.inet_ntoa(self.address) | |
372 except Exception: | |
373 return self.address | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
374 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
375 class DNSHinfo(DNSRecord): |
28298 | 376 """A DNS host information record""" |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
377 |
28298 | 378 def __init__(self, name, type, clazz, ttl, cpu, os): |
379 DNSRecord.__init__(self, name, type, clazz, ttl) | |
380 self.cpu = cpu | |
381 self.os = os | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
382 |
28298 | 383 def write(self, out): |
384 """Used in constructing an outgoing packet""" | |
385 out.writeString(self.cpu, len(self.cpu)) | |
386 out.writeString(self.os, len(self.os)) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
387 |
28298 | 388 def __eq__(self, other): |
389 """Tests equality on cpu and os""" | |
390 if isinstance(other, DNSHinfo): | |
391 return self.cpu == other.cpu and self.os == other.os | |
392 return 0 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
393 |
28298 | 394 def __repr__(self): |
395 """String representation""" | |
396 return self.cpu + " " + self.os | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
397 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
398 class DNSPointer(DNSRecord): |
28298 | 399 """A DNS pointer record""" |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
400 |
28298 | 401 def __init__(self, name, type, clazz, ttl, alias): |
402 DNSRecord.__init__(self, name, type, clazz, ttl) | |
403 self.alias = alias | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
404 |
28298 | 405 def write(self, out): |
406 """Used in constructing an outgoing packet""" | |
407 out.writeName(self.alias) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
408 |
28298 | 409 def __eq__(self, other): |
410 """Tests equality on alias""" | |
411 if isinstance(other, DNSPointer): | |
412 return self.alias == other.alias | |
413 return 0 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
414 |
28298 | 415 def __repr__(self): |
416 """String representation""" | |
417 return self.toString(self.alias) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
418 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
419 class DNSText(DNSRecord): |
28298 | 420 """A DNS text record""" |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
421 |
28298 | 422 def __init__(self, name, type, clazz, ttl, text): |
423 DNSRecord.__init__(self, name, type, clazz, ttl) | |
424 self.text = text | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
425 |
28298 | 426 def write(self, out): |
427 """Used in constructing an outgoing packet""" | |
428 out.writeString(self.text, len(self.text)) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
429 |
28298 | 430 def __eq__(self, other): |
431 """Tests equality on text""" | |
432 if isinstance(other, DNSText): | |
433 return self.text == other.text | |
434 return 0 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
435 |
28298 | 436 def __repr__(self): |
437 """String representation""" | |
438 if len(self.text) > 10: | |
439 return self.toString(self.text[:7] + "...") | |
440 else: | |
441 return self.toString(self.text) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
442 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
443 class DNSService(DNSRecord): |
28298 | 444 """A DNS service record""" |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
445 |
28298 | 446 def __init__(self, name, type, clazz, ttl, priority, weight, port, server): |
447 DNSRecord.__init__(self, name, type, clazz, ttl) | |
448 self.priority = priority | |
449 self.weight = weight | |
450 self.port = port | |
451 self.server = server | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
452 |
28298 | 453 def write(self, out): |
454 """Used in constructing an outgoing packet""" | |
455 out.writeShort(self.priority) | |
456 out.writeShort(self.weight) | |
457 out.writeShort(self.port) | |
458 out.writeName(self.server) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
459 |
28298 | 460 def __eq__(self, other): |
461 """Tests equality on priority, weight, port and server""" | |
462 if isinstance(other, DNSService): | |
28299 | 463 return (self.priority == other.priority and |
464 self.weight == other.weight and | |
465 self.port == other.port and | |
466 self.server == other.server) | |
28298 | 467 return 0 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
468 |
28298 | 469 def __repr__(self): |
470 """String representation""" | |
471 return self.toString("%s:%s" % (self.server, self.port)) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
472 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
473 class DNSIncoming(object): |
28298 | 474 """Object representation of an incoming DNS packet""" |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
475 |
28298 | 476 def __init__(self, data): |
477 """Constructor from string holding bytes of packet""" | |
478 self.offset = 0 | |
479 self.data = data | |
480 self.questions = [] | |
481 self.answers = [] | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
482 self.numquestions = 0 |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
483 self.numanswers = 0 |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
484 self.numauthorities = 0 |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
485 self.numadditionals = 0 |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
486 |
28298 | 487 self.readHeader() |
488 self.readQuestions() | |
489 self.readOthers() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
490 |
28298 | 491 def readHeader(self): |
492 """Reads header portion of packet""" | |
493 format = '!HHHHHH' | |
494 length = struct.calcsize(format) | |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
495 info = struct.unpack(format, |
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
496 self.data[self.offset:self.offset + length]) |
28298 | 497 self.offset += length |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
498 |
28298 | 499 self.id = info[0] |
500 self.flags = info[1] | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
501 self.numquestions = info[2] |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
502 self.numanswers = info[3] |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
503 self.numauthorities = info[4] |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
504 self.numadditionals = info[5] |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
505 |
28298 | 506 def readQuestions(self): |
507 """Reads questions section of packet""" | |
508 format = '!HH' | |
509 length = struct.calcsize(format) | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
510 for i in range(0, self.numquestions): |
28298 | 511 name = self.readName() |
28299 | 512 info = struct.unpack(format, |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
513 self.data[self.offset:self.offset + length]) |
28298 | 514 self.offset += length |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
515 |
28298 | 516 try: |
517 question = DNSQuestion(name, info[0], info[1]) | |
518 self.questions.append(question) | |
519 except NonLocalNameException: | |
520 pass | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
521 |
28298 | 522 def readInt(self): |
523 """Reads an integer from the packet""" | |
524 format = '!I' | |
525 length = struct.calcsize(format) | |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
526 info = struct.unpack(format, |
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
527 self.data[self.offset:self.offset + length]) |
28298 | 528 self.offset += length |
529 return info[0] | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
530 |
28298 | 531 def readCharacterString(self): |
532 """Reads a character string from the packet""" | |
533 length = ord(self.data[self.offset]) | |
534 self.offset += 1 | |
535 return self.readString(length) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
536 |
28298 | 537 def readString(self, len): |
538 """Reads a string of a given length from the packet""" | |
539 format = '!' + str(len) + 's' | |
540 length = struct.calcsize(format) | |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
541 info = struct.unpack(format, |
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
542 self.data[self.offset:self.offset + length]) |
28298 | 543 self.offset += length |
544 return info[0] | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
545 |
28298 | 546 def readUnsignedShort(self): |
547 """Reads an unsigned short from the packet""" | |
548 format = '!H' | |
549 length = struct.calcsize(format) | |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
550 info = struct.unpack(format, |
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
551 self.data[self.offset:self.offset + length]) |
28298 | 552 self.offset += length |
553 return info[0] | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
554 |
28298 | 555 def readOthers(self): |
28299 | 556 """Reads answers, authorities and additionals section of the packet""" |
28298 | 557 format = '!HHiH' |
558 length = struct.calcsize(format) | |
28504
3c90090320ad
zeroconf: remove leftover camelcase identifier
Martin von Zweigbergk <martinvonz@google.com>
parents:
28422
diff
changeset
|
559 n = self.numanswers + self.numauthorities + self.numadditionals |
28298 | 560 for i in range(0, n): |
561 domain = self.readName() | |
28299 | 562 info = struct.unpack(format, |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
563 self.data[self.offset:self.offset + length]) |
28298 | 564 self.offset += length |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
565 |
28298 | 566 rec = None |
567 if info[0] == _TYPE_A: | |
28299 | 568 rec = DNSAddress(domain, info[0], info[1], info[2], |
569 self.readString(4)) | |
28298 | 570 elif info[0] == _TYPE_CNAME or info[0] == _TYPE_PTR: |
28299 | 571 rec = DNSPointer(domain, info[0], info[1], info[2], |
572 self.readName()) | |
28298 | 573 elif info[0] == _TYPE_TXT: |
28299 | 574 rec = DNSText(domain, info[0], info[1], info[2], |
575 self.readString(info[3])) | |
28298 | 576 elif info[0] == _TYPE_SRV: |
28299 | 577 rec = DNSService(domain, info[0], info[1], info[2], |
578 self.readUnsignedShort(), | |
579 self.readUnsignedShort(), | |
580 self.readUnsignedShort(), | |
581 self.readName()) | |
28298 | 582 elif info[0] == _TYPE_HINFO: |
28299 | 583 rec = DNSHinfo(domain, info[0], info[1], info[2], |
584 self.readCharacterString(), | |
585 self.readCharacterString()) | |
28298 | 586 elif info[0] == _TYPE_AAAA: |
28299 | 587 rec = DNSAddress(domain, info[0], info[1], info[2], |
588 self.readString(16)) | |
28298 | 589 else: |
590 # Try to ignore types we don't know about | |
591 # this may mean the rest of the name is | |
592 # unable to be parsed, and may show errors | |
593 # so this is left for debugging. New types | |
594 # encountered need to be parsed properly. | |
595 # | |
596 #print "UNKNOWN TYPE = " + str(info[0]) | |
597 #raise BadTypeInNameException | |
598 self.offset += info[3] | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
599 |
28298 | 600 if rec is not None: |
601 self.answers.append(rec) | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
602 |
28298 | 603 def isQuery(self): |
604 """Returns true if this is a query""" | |
605 return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_QUERY | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
606 |
28298 | 607 def isResponse(self): |
608 """Returns true if this is a response""" | |
609 return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_RESPONSE | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
610 |
28298 | 611 def readUTF(self, offset, len): |
612 """Reads a UTF-8 string of a given length from the packet""" | |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
613 return self.data[offset:offset + len].decode('utf-8') |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
614 |
28298 | 615 def readName(self): |
616 """Reads a domain name from the packet""" | |
617 result = '' | |
618 off = self.offset | |
619 next = -1 | |
620 first = off | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
621 |
28298 | 622 while True: |
623 len = ord(self.data[off]) | |
624 off += 1 | |
625 if len == 0: | |
626 break | |
627 t = len & 0xC0 | |
628 if t == 0x00: | |
629 result = ''.join((result, self.readUTF(off, len) + '.')) | |
630 off += len | |
631 elif t == 0xC0: | |
632 if next < 0: | |
633 next = off + 1 | |
634 off = ((len & 0x3F) << 8) | ord(self.data[off]) | |
635 if off >= first: | |
636 raise BadDomainNameCircular(off) | |
637 first = off | |
638 else: | |
639 raise BadDomainName(off) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
640 |
28298 | 641 if next >= 0: |
642 self.offset = next | |
643 else: | |
644 self.offset = off | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
645 |
28298 | 646 return result |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
647 |
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
648 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
649 class DNSOutgoing(object): |
28298 | 650 """Object representation of an outgoing packet""" |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
651 |
28302
e96a3ae025ed
zeroconf: remove whitespace around = for named parameters
timeless <timeless@mozdev.org>
parents:
28301
diff
changeset
|
652 def __init__(self, flags, multicast=1): |
28298 | 653 self.finished = 0 |
654 self.id = 0 | |
655 self.multicast = multicast | |
656 self.flags = flags | |
657 self.names = {} | |
658 self.data = [] | |
659 self.size = 12 | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
660 |
28298 | 661 self.questions = [] |
662 self.answers = [] | |
663 self.authorities = [] | |
664 self.additionals = [] | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
665 |
28298 | 666 def addQuestion(self, record): |
667 """Adds a question""" | |
668 self.questions.append(record) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
669 |
28298 | 670 def addAnswer(self, inp, record): |
671 """Adds an answer""" | |
672 if not record.suppressedBy(inp): | |
673 self.addAnswerAtTime(record, 0) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
674 |
28298 | 675 def addAnswerAtTime(self, record, now): |
676 """Adds an answer if if does not expire by a certain time""" | |
677 if record is not None: | |
678 if now == 0 or not record.isExpired(now): | |
679 self.answers.append((record, now)) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
680 |
28298 | 681 def addAuthoritativeAnswer(self, record): |
682 """Adds an authoritative answer""" | |
683 self.authorities.append(record) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
684 |
28298 | 685 def addAdditionalAnswer(self, record): |
686 """Adds an additional answer""" | |
687 self.additionals.append(record) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
688 |
28298 | 689 def writeByte(self, value): |
690 """Writes a single byte to the packet""" | |
691 format = '!c' | |
692 self.data.append(struct.pack(format, chr(value))) | |
693 self.size += 1 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
694 |
28298 | 695 def insertShort(self, index, value): |
696 """Inserts an unsigned short in a certain position in the packet""" | |
697 format = '!H' | |
698 self.data.insert(index, struct.pack(format, value)) | |
699 self.size += 2 | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
700 |
28298 | 701 def writeShort(self, value): |
702 """Writes an unsigned short to the packet""" | |
703 format = '!H' | |
704 self.data.append(struct.pack(format, value)) | |
705 self.size += 2 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
706 |
28298 | 707 def writeInt(self, value): |
708 """Writes an unsigned integer to the packet""" | |
709 format = '!I' | |
710 self.data.append(struct.pack(format, int(value))) | |
711 self.size += 4 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
712 |
28298 | 713 def writeString(self, value, length): |
714 """Writes a string to the packet""" | |
715 format = '!' + str(length) + 's' | |
716 self.data.append(struct.pack(format, value)) | |
717 self.size += length | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
718 |
28298 | 719 def writeUTF(self, s): |
720 """Writes a UTF-8 string of a given length to the packet""" | |
721 utfstr = s.encode('utf-8') | |
722 length = len(utfstr) | |
723 if length > 64: | |
724 raise NamePartTooLongException | |
725 self.writeByte(length) | |
726 self.writeString(utfstr, length) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
727 |
28298 | 728 def writeName(self, name): |
729 """Writes a domain name to the packet""" | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
730 |
28298 | 731 try: |
732 # Find existing instance of this name in packet | |
733 # | |
734 index = self.names[name] | |
735 except KeyError: | |
736 # No record of this name already, so write it | |
737 # out as normal, recording the location of the name | |
738 # for future pointers to it. | |
739 # | |
740 self.names[name] = self.size | |
741 parts = name.split('.') | |
742 if parts[-1] == '': | |
743 parts = parts[:-1] | |
744 for part in parts: | |
745 self.writeUTF(part) | |
746 self.writeByte(0) | |
747 return | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
748 |
28298 | 749 # An index was found, so write a pointer to it |
750 # | |
751 self.writeByte((index >> 8) | 0xC0) | |
752 self.writeByte(index) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
753 |
28298 | 754 def writeQuestion(self, question): |
755 """Writes a question to the packet""" | |
756 self.writeName(question.name) | |
757 self.writeShort(question.type) | |
758 self.writeShort(question.clazz) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
759 |
28298 | 760 def writeRecord(self, record, now): |
761 """Writes a record (answer, authoritative answer, additional) to | |
762 the packet""" | |
763 self.writeName(record.name) | |
764 self.writeShort(record.type) | |
765 if record.unique and self.multicast: | |
766 self.writeShort(record.clazz | _CLASS_UNIQUE) | |
767 else: | |
768 self.writeShort(record.clazz) | |
769 if now == 0: | |
770 self.writeInt(record.ttl) | |
771 else: | |
772 self.writeInt(record.getRemainingTTL(now)) | |
773 index = len(self.data) | |
774 # Adjust size for the short we will write before this record | |
775 # | |
776 self.size += 2 | |
777 record.write(self) | |
778 self.size -= 2 | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
779 |
28298 | 780 length = len(''.join(self.data[index:])) |
781 self.insertShort(index, length) # Here is the short we adjusted for | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
782 |
28298 | 783 def packet(self): |
784 """Returns a string containing the packet's bytes | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
785 |
28298 | 786 No further parts should be added to the packet once this |
787 is done.""" | |
788 if not self.finished: | |
789 self.finished = 1 | |
790 for question in self.questions: | |
791 self.writeQuestion(question) | |
792 for answer, time_ in self.answers: | |
793 self.writeRecord(answer, time_) | |
794 for authority in self.authorities: | |
795 self.writeRecord(authority, 0) | |
796 for additional in self.additionals: | |
797 self.writeRecord(additional, 0) | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
798 |
28298 | 799 self.insertShort(0, len(self.additionals)) |
800 self.insertShort(0, len(self.authorities)) | |
801 self.insertShort(0, len(self.answers)) | |
802 self.insertShort(0, len(self.questions)) | |
803 self.insertShort(0, self.flags) | |
804 if self.multicast: | |
805 self.insertShort(0, 0) | |
806 else: | |
807 self.insertShort(0, self.id) | |
808 return ''.join(self.data) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
809 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
810 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
811 class DNSCache(object): |
28298 | 812 """A cache of DNS entries""" |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
813 |
28298 | 814 def __init__(self): |
815 self.cache = {} | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
816 |
28298 | 817 def add(self, entry): |
818 """Adds an entry""" | |
819 try: | |
820 list = self.cache[entry.key] | |
821 except KeyError: | |
822 list = self.cache[entry.key] = [] | |
823 list.append(entry) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
824 |
28298 | 825 def remove(self, entry): |
826 """Removes an entry""" | |
827 try: | |
828 list = self.cache[entry.key] | |
829 list.remove(entry) | |
830 except KeyError: | |
831 pass | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
832 |
28298 | 833 def get(self, entry): |
834 """Gets an entry by key. Will return None if there is no | |
835 matching entry.""" | |
836 try: | |
837 list = self.cache[entry.key] | |
838 return list[list.index(entry)] | |
839 except (KeyError, ValueError): | |
840 return None | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
841 |
28298 | 842 def getByDetails(self, name, type, clazz): |
843 """Gets an entry by details. Will return None if there is | |
844 no matching entry.""" | |
845 entry = DNSEntry(name, type, clazz) | |
846 return self.get(entry) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
847 |
28298 | 848 def entriesWithName(self, name): |
849 """Returns a list of entries whose key matches the name.""" | |
850 try: | |
851 return self.cache[name] | |
852 except KeyError: | |
853 return [] | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
854 |
28298 | 855 def entries(self): |
856 """Returns a list of all entries""" | |
857 try: | |
28422
e2c6092ad422
zeroconf: replace reduce+add with itertools.chain
timeless <timeless@mozdev.org>
parents:
28421
diff
changeset
|
858 return list(itertools.chain.from_iterable(self.cache.values())) |
28298 | 859 except Exception: |
860 return [] | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
861 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
862 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
863 class Engine(threading.Thread): |
28298 | 864 """An engine wraps read access to sockets, allowing objects that |
865 need to receive data from sockets to be called back when the | |
866 sockets are ready. | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
867 |
28298 | 868 A reader needs a handle_read() method, which is called when the socket |
869 it is interested in is ready for reading. | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
870 |
28298 | 871 Writers are not implemented here, because we only send short |
872 packets. | |
873 """ | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
874 |
28298 | 875 def __init__(self, zeroconf): |
876 threading.Thread.__init__(self) | |
877 self.zeroconf = zeroconf | |
878 self.readers = {} # maps socket to reader | |
879 self.timeout = 5 | |
880 self.condition = threading.Condition() | |
881 self.start() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
882 |
28298 | 883 def run(self): |
884 while not globals()['_GLOBAL_DONE']: | |
885 rs = self.getReaders() | |
886 if len(rs) == 0: | |
887 # No sockets to manage, but we wait for the timeout | |
888 # or addition of a socket | |
889 # | |
890 self.condition.acquire() | |
891 self.condition.wait(self.timeout) | |
892 self.condition.release() | |
893 else: | |
894 try: | |
895 rr, wr, er = select.select(rs, [], [], self.timeout) | |
896 for sock in rr: | |
897 try: | |
898 self.readers[sock].handle_read() | |
899 except Exception: | |
900 if not globals()['_GLOBAL_DONE']: | |
901 traceback.print_exc() | |
902 except Exception: | |
903 pass | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
904 |
28298 | 905 def getReaders(self): |
906 self.condition.acquire() | |
907 result = self.readers.keys() | |
908 self.condition.release() | |
909 return result | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
910 |
28298 | 911 def addReader(self, reader, socket): |
912 self.condition.acquire() | |
913 self.readers[socket] = reader | |
914 self.condition.notify() | |
915 self.condition.release() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
916 |
28298 | 917 def delReader(self, socket): |
918 self.condition.acquire() | |
28301
fd8a4d2d6541
zeroconf: del is not a function
timeless <timeless@mozdev.org>
parents:
28300
diff
changeset
|
919 del self.readers[socket] |
28298 | 920 self.condition.notify() |
921 self.condition.release() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
922 |
28298 | 923 def notify(self): |
924 self.condition.acquire() | |
925 self.condition.notify() | |
926 self.condition.release() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
927 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
928 class Listener(object): |
28298 | 929 """A Listener is used by this module to listen on the multicast |
930 group to which DNS messages are sent, allowing the implementation | |
931 to cache information as it arrives. | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
932 |
28298 | 933 It requires registration with an Engine object in order to have |
934 the read() method called when a socket is available for reading.""" | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
935 |
28298 | 936 def __init__(self, zeroconf): |
937 self.zeroconf = zeroconf | |
938 self.zeroconf.engine.addReader(self, self.zeroconf.socket) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
939 |
28298 | 940 def handle_read(self): |
34447
5385b76fd1fd
zeroconf: do not crash if socket being read is closed by another thread
Jun Wu <quark@fb.com>
parents:
28504
diff
changeset
|
941 data = addr = port = None |
5385b76fd1fd
zeroconf: do not crash if socket being read is closed by another thread
Jun Wu <quark@fb.com>
parents:
28504
diff
changeset
|
942 sock = self.zeroconf.socket |
5385b76fd1fd
zeroconf: do not crash if socket being read is closed by another thread
Jun Wu <quark@fb.com>
parents:
28504
diff
changeset
|
943 try: |
5385b76fd1fd
zeroconf: do not crash if socket being read is closed by another thread
Jun Wu <quark@fb.com>
parents:
28504
diff
changeset
|
944 data, (addr, port) = sock.recvfrom(_MAX_MSG_ABSOLUTE) |
5385b76fd1fd
zeroconf: do not crash if socket being read is closed by another thread
Jun Wu <quark@fb.com>
parents:
28504
diff
changeset
|
945 except socket.error as e: |
5385b76fd1fd
zeroconf: do not crash if socket being read is closed by another thread
Jun Wu <quark@fb.com>
parents:
28504
diff
changeset
|
946 if e.errno == errno.EBADF: |
5385b76fd1fd
zeroconf: do not crash if socket being read is closed by another thread
Jun Wu <quark@fb.com>
parents:
28504
diff
changeset
|
947 # some other thread may close the socket |
5385b76fd1fd
zeroconf: do not crash if socket being read is closed by another thread
Jun Wu <quark@fb.com>
parents:
28504
diff
changeset
|
948 return |
5385b76fd1fd
zeroconf: do not crash if socket being read is closed by another thread
Jun Wu <quark@fb.com>
parents:
28504
diff
changeset
|
949 else: |
5385b76fd1fd
zeroconf: do not crash if socket being read is closed by another thread
Jun Wu <quark@fb.com>
parents:
28504
diff
changeset
|
950 raise |
28298 | 951 self.data = data |
952 msg = DNSIncoming(data) | |
953 if msg.isQuery(): | |
954 # Always multicast responses | |
955 # | |
956 if port == _MDNS_PORT: | |
957 self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT) | |
958 # If it's not a multicast query, reply via unicast | |
959 # and multicast | |
960 # | |
961 elif port == _DNS_PORT: | |
962 self.zeroconf.handleQuery(msg, addr, port) | |
963 self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT) | |
964 else: | |
965 self.zeroconf.handleResponse(msg) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
966 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
967 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
968 class Reaper(threading.Thread): |
28298 | 969 """A Reaper is used by this module to remove cache entries that |
970 have expired.""" | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
971 |
28298 | 972 def __init__(self, zeroconf): |
973 threading.Thread.__init__(self) | |
974 self.zeroconf = zeroconf | |
975 self.start() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
976 |
28298 | 977 def run(self): |
978 while True: | |
979 self.zeroconf.wait(10 * 1000) | |
980 if globals()['_GLOBAL_DONE']: | |
981 return | |
982 now = currentTimeMillis() | |
983 for record in self.zeroconf.cache.entries(): | |
984 if record.isExpired(now): | |
985 self.zeroconf.updateRecord(now, record) | |
986 self.zeroconf.cache.remove(record) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
987 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
988 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
989 class ServiceBrowser(threading.Thread): |
28298 | 990 """Used to browse for a service of a specific type. |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
991 |
28298 | 992 The listener object will have its addService() and |
993 removeService() methods called when this browser | |
994 discovers changes in the services availability.""" | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
995 |
28298 | 996 def __init__(self, zeroconf, type, listener): |
997 """Creates a browser for a specific type""" | |
998 threading.Thread.__init__(self) | |
999 self.zeroconf = zeroconf | |
1000 self.type = type | |
1001 self.listener = listener | |
1002 self.services = {} | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1003 self.nexttime = currentTimeMillis() |
28298 | 1004 self.delay = _BROWSER_TIME |
1005 self.list = [] | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
1006 |
28298 | 1007 self.done = 0 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1008 |
28299 | 1009 self.zeroconf.addListener(self, DNSQuestion(self.type, _TYPE_PTR, |
1010 _CLASS_IN)) | |
28298 | 1011 self.start() |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1012 |
28298 | 1013 def updateRecord(self, zeroconf, now, record): |
1014 """Callback invoked by Zeroconf when new information arrives. | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1015 |
28298 | 1016 Updates information required by browser in the Zeroconf cache.""" |
1017 if record.type == _TYPE_PTR and record.name == self.type: | |
1018 expired = record.isExpired(now) | |
1019 try: | |
1020 oldrecord = self.services[record.alias.lower()] | |
1021 if not expired: | |
1022 oldrecord.resetTTL(record) | |
1023 else: | |
28301
fd8a4d2d6541
zeroconf: del is not a function
timeless <timeless@mozdev.org>
parents:
28300
diff
changeset
|
1024 del self.services[record.alias.lower()] |
28299 | 1025 callback = (lambda x: |
1026 self.listener.removeService(x, self.type, record.alias)) | |
28298 | 1027 self.list.append(callback) |
1028 return | |
1029 except Exception: | |
1030 if not expired: | |
1031 self.services[record.alias.lower()] = record | |
28299 | 1032 callback = (lambda x: |
1033 self.listener.addService(x, self.type, record.alias)) | |
28298 | 1034 self.list.append(callback) |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1035 |
28298 | 1036 expires = record.getExpirationTime(75) |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1037 if expires < self.nexttime: |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1038 self.nexttime = expires |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1039 |
28298 | 1040 def cancel(self): |
1041 self.done = 1 | |
1042 self.zeroconf.notifyAll() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1043 |
28298 | 1044 def run(self): |
1045 while True: | |
1046 event = None | |
1047 now = currentTimeMillis() | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1048 if len(self.list) == 0 and self.nexttime > now: |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1049 self.zeroconf.wait(self.nexttime - now) |
28298 | 1050 if globals()['_GLOBAL_DONE'] or self.done: |
1051 return | |
1052 now = currentTimeMillis() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1053 |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1054 if self.nexttime <= now: |
28298 | 1055 out = DNSOutgoing(_FLAGS_QR_QUERY) |
1056 out.addQuestion(DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN)) | |
1057 for record in self.services.values(): | |
1058 if not record.isExpired(now): | |
1059 out.addAnswerAtTime(record, now) | |
1060 self.zeroconf.send(out) | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1061 self.nexttime = now + self.delay |
28298 | 1062 self.delay = min(20 * 1000, self.delay * 2) |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1063 |
28298 | 1064 if len(self.list) > 0: |
1065 event = self.list.pop(0) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1066 |
28298 | 1067 if event is not None: |
1068 event(self.zeroconf) | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
1069 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1070 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1071 class ServiceInfo(object): |
28298 | 1072 """Service information""" |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
1073 |
28299 | 1074 def __init__(self, type, name, address=None, port=None, weight=0, |
1075 priority=0, properties=None, server=None): | |
28298 | 1076 """Create a service description. |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1077 |
28298 | 1078 type: fully qualified service type name |
1079 name: fully qualified service name | |
1080 address: IP address as unsigned short, network byte order | |
1081 port: port that the service runs on | |
1082 weight: weight of the service | |
1083 priority: priority of the service | |
28299 | 1084 properties: dictionary of properties (or a string holding the bytes for |
1085 the text field) | |
28298 | 1086 server: fully qualified name for service host (defaults to name)""" |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1087 |
28298 | 1088 if not name.endswith(type): |
1089 raise BadTypeInNameException | |
1090 self.type = type | |
1091 self.name = name | |
1092 self.address = address | |
1093 self.port = port | |
1094 self.weight = weight | |
1095 self.priority = priority | |
1096 if server: | |
1097 self.server = server | |
1098 else: | |
1099 self.server = name | |
1100 self.setProperties(properties) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1101 |
28298 | 1102 def setProperties(self, properties): |
1103 """Sets properties and text of this info from a dictionary""" | |
1104 if isinstance(properties, dict): | |
1105 self.properties = properties | |
1106 list = [] | |
1107 result = '' | |
1108 for key in properties: | |
1109 value = properties[key] | |
1110 if value is None: | |
1111 suffix = '' | |
1112 elif isinstance(value, str): | |
1113 suffix = value | |
1114 elif isinstance(value, int): | |
1115 if value: | |
1116 suffix = 'true' | |
1117 else: | |
1118 suffix = 'false' | |
1119 else: | |
1120 suffix = '' | |
1121 list.append('='.join((key, suffix))) | |
1122 for item in list: | |
28299 | 1123 result = ''.join((result, struct.pack('!c', chr(len(item))), |
1124 item)) | |
28298 | 1125 self.text = result |
1126 else: | |
1127 self.text = properties | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1128 |
28298 | 1129 def setText(self, text): |
1130 """Sets properties and text given a text field""" | |
1131 self.text = text | |
1132 try: | |
1133 result = {} | |
1134 end = len(text) | |
1135 index = 0 | |
1136 strs = [] | |
1137 while index < end: | |
1138 length = ord(text[index]) | |
1139 index += 1 | |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
1140 strs.append(text[index:index + length]) |
28298 | 1141 index += length |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
1142 |
28298 | 1143 for s in strs: |
1144 eindex = s.find('=') | |
1145 if eindex == -1: | |
1146 # No equals sign at all | |
1147 key = s | |
1148 value = 0 | |
1149 else: | |
1150 key = s[:eindex] | |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
1151 value = s[eindex + 1:] |
28298 | 1152 if value == 'true': |
1153 value = 1 | |
1154 elif value == 'false' or not value: | |
1155 value = 0 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1156 |
28298 | 1157 # Only update non-existent properties |
28420
d03b7800672c
zeroconf: compare singleton using is
timeless <timeless@mozdev.org>
parents:
28419
diff
changeset
|
1158 if key and result.get(key) is None: |
28298 | 1159 result[key] = value |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1160 |
28298 | 1161 self.properties = result |
1162 except Exception: | |
1163 traceback.print_exc() | |
1164 self.properties = None | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
1165 |
28298 | 1166 def getType(self): |
1167 """Type accessor""" | |
1168 return self.type | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1169 |
28298 | 1170 def getName(self): |
1171 """Name accessor""" | |
1172 if self.type is not None and self.name.endswith("." + self.type): | |
1173 return self.name[:len(self.name) - len(self.type) - 1] | |
1174 return self.name | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1175 |
28298 | 1176 def getAddress(self): |
1177 """Address accessor""" | |
1178 return self.address | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1179 |
28298 | 1180 def getPort(self): |
1181 """Port accessor""" | |
1182 return self.port | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1183 |
28298 | 1184 def getPriority(self): |
1185 """Priority accessor""" | |
1186 return self.priority | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1187 |
28298 | 1188 def getWeight(self): |
1189 """Weight accessor""" | |
1190 return self.weight | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1191 |
28298 | 1192 def getProperties(self): |
1193 """Properties accessor""" | |
1194 return self.properties | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1195 |
28298 | 1196 def getText(self): |
1197 """Text accessor""" | |
1198 return self.text | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1199 |
28298 | 1200 def getServer(self): |
1201 """Server accessor""" | |
1202 return self.server | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1203 |
28298 | 1204 def updateRecord(self, zeroconf, now, record): |
1205 """Updates service information from a DNS record""" | |
1206 if record is not None and not record.isExpired(now): | |
1207 if record.type == _TYPE_A: | |
1208 #if record.name == self.name: | |
1209 if record.name == self.server: | |
1210 self.address = record.address | |
1211 elif record.type == _TYPE_SRV: | |
1212 if record.name == self.name: | |
1213 self.server = record.server | |
1214 self.port = record.port | |
1215 self.weight = record.weight | |
1216 self.priority = record.priority | |
1217 #self.address = None | |
28299 | 1218 self.updateRecord(zeroconf, now, |
1219 zeroconf.cache.getByDetails(self.server, | |
1220 _TYPE_A, _CLASS_IN)) | |
28298 | 1221 elif record.type == _TYPE_TXT: |
1222 if record.name == self.name: | |
1223 self.setText(record.text) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1224 |
28298 | 1225 def request(self, zeroconf, timeout): |
1226 """Returns true if the service could be discovered on the | |
1227 network, and updates this object with details discovered. | |
1228 """ | |
1229 now = currentTimeMillis() | |
1230 delay = _LISTENER_TIME | |
1231 next = now + delay | |
1232 last = now + timeout | |
1233 result = 0 | |
1234 try: | |
28299 | 1235 zeroconf.addListener(self, DNSQuestion(self.name, _TYPE_ANY, |
1236 _CLASS_IN)) | |
1237 while (self.server is None or self.address is None or | |
1238 self.text is None): | |
28298 | 1239 if last <= now: |
1240 return 0 | |
1241 if next <= now: | |
1242 out = DNSOutgoing(_FLAGS_QR_QUERY) | |
28299 | 1243 out.addQuestion(DNSQuestion(self.name, _TYPE_SRV, |
1244 _CLASS_IN)) | |
1245 out.addAnswerAtTime( | |
1246 zeroconf.cache.getByDetails(self.name, | |
1247 _TYPE_SRV, | |
1248 _CLASS_IN), | |
1249 now) | |
1250 out.addQuestion(DNSQuestion(self.name, _TYPE_TXT, | |
1251 _CLASS_IN)) | |
1252 out.addAnswerAtTime( | |
1253 zeroconf.cache.getByDetails(self.name, _TYPE_TXT, | |
1254 _CLASS_IN), | |
1255 now) | |
28298 | 1256 if self.server is not None: |
28299 | 1257 out.addQuestion( |
1258 DNSQuestion(self.server, _TYPE_A, _CLASS_IN)) | |
1259 out.addAnswerAtTime( | |
1260 zeroconf.cache.getByDetails(self.server, _TYPE_A, | |
1261 _CLASS_IN), | |
1262 now) | |
28298 | 1263 zeroconf.send(out) |
1264 next = now + delay | |
1265 delay = delay * 2 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1266 |
28298 | 1267 zeroconf.wait(min(next, last) - now) |
1268 now = currentTimeMillis() | |
1269 result = 1 | |
1270 finally: | |
1271 zeroconf.removeListener(self) | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
1272 |
28298 | 1273 return result |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1274 |
28298 | 1275 def __eq__(self, other): |
1276 """Tests equality of service name""" | |
1277 if isinstance(other, ServiceInfo): | |
1278 return other.name == self.name | |
1279 return 0 | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1280 |
28298 | 1281 def __ne__(self, other): |
1282 """Non-equality test""" | |
1283 return not self.__eq__(other) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1284 |
28298 | 1285 def __repr__(self): |
1286 """String representation""" | |
28299 | 1287 result = ("service[%s,%s:%s," % |
1288 (self.name, socket.inet_ntoa(self.getAddress()), self.port)) | |
28298 | 1289 if self.text is None: |
1290 result += "None" | |
1291 else: | |
1292 if len(self.text) < 20: | |
1293 result += self.text | |
1294 else: | |
1295 result += self.text[:17] + "..." | |
1296 result += "]" | |
1297 return result | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
1298 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1299 |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1300 class Zeroconf(object): |
28298 | 1301 """Implementation of Zeroconf Multicast DNS Service Discovery |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1302 |
28298 | 1303 Supports registration, unregistration, queries and browsing. |
1304 """ | |
1305 def __init__(self, bindaddress=None): | |
1306 """Creates an instance of the Zeroconf class, establishing | |
1307 multicast communications, listening and reaping threads.""" | |
1308 globals()['_GLOBAL_DONE'] = 0 | |
1309 if bindaddress is None: | |
1310 self.intf = socket.gethostbyname(socket.gethostname()) | |
1311 else: | |
1312 self.intf = bindaddress | |
1313 self.group = ('', _MDNS_PORT) | |
1314 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
1315 try: | |
1316 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
1317 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) | |
1318 except Exception: | |
1319 # SO_REUSEADDR should be equivalent to SO_REUSEPORT for | |
1320 # multicast UDP sockets (p 731, "TCP/IP Illustrated, | |
1321 # Volume 2"), but some BSD-derived systems require | |
1322 # SO_REUSEPORT to be specified explicitly. Also, not all | |
1323 # versions of Python have SO_REUSEPORT available. So | |
1324 # if you're on a BSD-based system, and haven't upgraded | |
1325 # to Python 2.3 yet, you may find this library doesn't | |
1326 # work as expected. | |
1327 # | |
1328 pass | |
1329 self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, "\xff") | |
1330 self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, "\x01") | |
1331 try: | |
1332 self.socket.bind(self.group) | |
1333 except Exception: | |
1334 # Some versions of linux raise an exception even though | |
1335 # SO_REUSEADDR and SO_REUSEPORT have been set, so ignore it | |
1336 pass | |
28299 | 1337 self.socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, |
1338 socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0')) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1339 |
28298 | 1340 self.listeners = [] |
1341 self.browsers = [] | |
1342 self.services = {} | |
1343 self.servicetypes = {} | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1344 |
28298 | 1345 self.cache = DNSCache() |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1346 |
28298 | 1347 self.condition = threading.Condition() |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
1348 |
28298 | 1349 self.engine = Engine(self) |
1350 self.listener = Listener(self) | |
1351 self.reaper = Reaper(self) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1352 |
28298 | 1353 def isLoopback(self): |
1354 return self.intf.startswith("127.0.0.1") | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1355 |
28298 | 1356 def isLinklocal(self): |
1357 return self.intf.startswith("169.254.") | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1358 |
28298 | 1359 def wait(self, timeout): |
1360 """Calling thread waits for a given number of milliseconds or | |
1361 until notified.""" | |
1362 self.condition.acquire() | |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
1363 self.condition.wait(timeout / 1000) |
28298 | 1364 self.condition.release() |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1365 |
28298 | 1366 def notifyAll(self): |
1367 """Notifies all waiting threads""" | |
1368 self.condition.acquire() | |
1369 self.condition.notifyAll() | |
1370 self.condition.release() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1371 |
28298 | 1372 def getServiceInfo(self, type, name, timeout=3000): |
1373 """Returns network's service information for a particular | |
1374 name and type, or None if no service matches by the timeout, | |
1375 which defaults to 3 seconds.""" | |
1376 info = ServiceInfo(type, name) | |
1377 if info.request(self, timeout): | |
1378 return info | |
1379 return None | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1380 |
28298 | 1381 def addServiceListener(self, type, listener): |
1382 """Adds a listener for a particular service type. This object | |
1383 will then have its updateRecord method called when information | |
1384 arrives for that type.""" | |
1385 self.removeServiceListener(listener) | |
1386 self.browsers.append(ServiceBrowser(self, type, listener)) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1387 |
28298 | 1388 def removeServiceListener(self, listener): |
1389 """Removes a listener from the set that is currently listening.""" | |
1390 for browser in self.browsers: | |
1391 if browser.listener == listener: | |
1392 browser.cancel() | |
28301
fd8a4d2d6541
zeroconf: del is not a function
timeless <timeless@mozdev.org>
parents:
28300
diff
changeset
|
1393 del browser |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1394 |
28298 | 1395 def registerService(self, info, ttl=_DNS_TTL): |
1396 """Registers service information to the network with a default TTL | |
1397 of 60 seconds. Zeroconf will then respond to requests for | |
1398 information for that service. The name of the service may be | |
1399 changed if needed to make it unique on the network.""" | |
1400 self.checkService(info) | |
1401 self.services[info.name.lower()] = info | |
28421
6d72cc613fc4
zeroconf: replace has_key with in
timeless <timeless@mozdev.org>
parents:
28420
diff
changeset
|
1402 if info.type in self.servicetypes: |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
1403 self.servicetypes[info.type] += 1 |
28298 | 1404 else: |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
1405 self.servicetypes[info.type] = 1 |
28298 | 1406 now = currentTimeMillis() |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1407 nexttime = now |
28298 | 1408 i = 0 |
1409 while i < 3: | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1410 if now < nexttime: |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1411 self.wait(nexttime - now) |
28298 | 1412 now = currentTimeMillis() |
1413 continue | |
1414 out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) | |
28299 | 1415 out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, |
1416 _CLASS_IN, ttl, info.name), 0) | |
1417 out.addAnswerAtTime( | |
1418 DNSService( | |
1419 info.name, _TYPE_SRV, | |
1420 _CLASS_IN, ttl, info.priority, info.weight, info.port, | |
1421 info.server), | |
1422 0) | |
1423 out.addAnswerAtTime( | |
1424 DNSText(info.name, _TYPE_TXT, _CLASS_IN, ttl, info.text), | |
1425 0) | |
28298 | 1426 if info.address: |
28299 | 1427 out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, |
1428 _CLASS_IN, ttl, info.address), 0) | |
28298 | 1429 self.send(out) |
1430 i += 1 | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1431 nexttime += _REGISTER_TIME |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1432 |
28298 | 1433 def unregisterService(self, info): |
1434 """Unregister a service.""" | |
1435 try: | |
28301
fd8a4d2d6541
zeroconf: del is not a function
timeless <timeless@mozdev.org>
parents:
28300
diff
changeset
|
1436 del self.services[info.name.lower()] |
28300
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
1437 if self.servicetypes[info.type] > 1: |
15c5f50e7e13
zeroconf: add whitespace around operator
timeless <timeless@mozdev.org>
parents:
28299
diff
changeset
|
1438 self.servicetypes[info.type] -= 1 |
28298 | 1439 else: |
1440 del self.servicetypes[info.type] | |
1441 except KeyError: | |
1442 pass | |
1443 now = currentTimeMillis() | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1444 nexttime = now |
28298 | 1445 i = 0 |
1446 while i < 3: | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1447 if now < nexttime: |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1448 self.wait(nexttime - now) |
28298 | 1449 now = currentTimeMillis() |
1450 continue | |
1451 out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) | |
28299 | 1452 out.addAnswerAtTime( |
1453 DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) | |
1454 out.addAnswerAtTime( | |
1455 DNSService(info.name, _TYPE_SRV, | |
1456 _CLASS_IN, 0, info.priority, info.weight, info.port, | |
1457 info.name), | |
1458 0) | |
1459 out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, | |
1460 _CLASS_IN, 0, info.text), 0) | |
28298 | 1461 if info.address: |
28299 | 1462 out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, |
1463 _CLASS_IN, 0, info.address), 0) | |
28298 | 1464 self.send(out) |
1465 i += 1 | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1466 nexttime += _UNREGISTER_TIME |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1467 |
28298 | 1468 def unregisterAllServices(self): |
1469 """Unregister all registered services.""" | |
1470 if len(self.services) > 0: | |
1471 now = currentTimeMillis() | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1472 nexttime = now |
28298 | 1473 i = 0 |
1474 while i < 3: | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1475 if now < nexttime: |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1476 self.wait(nexttime - now) |
28298 | 1477 now = currentTimeMillis() |
1478 continue | |
1479 out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) | |
1480 for info in self.services.values(): | |
28299 | 1481 out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, |
1482 _CLASS_IN, 0, info.name), 0) | |
1483 out.addAnswerAtTime( | |
1484 DNSService(info.name, _TYPE_SRV, | |
1485 _CLASS_IN, 0, info.priority, info.weight, | |
1486 info.port, info.server), | |
1487 0) | |
1488 out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, | |
1489 _CLASS_IN, 0, info.text), 0) | |
28298 | 1490 if info.address: |
28299 | 1491 out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, |
1492 _CLASS_IN, 0, info.address), 0) | |
28298 | 1493 self.send(out) |
1494 i += 1 | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1495 nexttime += _UNREGISTER_TIME |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1496 |
28298 | 1497 def checkService(self, info): |
1498 """Checks the network for a unique service name, modifying the | |
1499 ServiceInfo passed in if it is not unique.""" | |
1500 now = currentTimeMillis() | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1501 nexttime = now |
28298 | 1502 i = 0 |
1503 while i < 3: | |
1504 for record in self.cache.entriesWithName(info.type): | |
28299 | 1505 if (record.type == _TYPE_PTR and not record.isExpired(now) and |
1506 record.alias == info.name): | |
28298 | 1507 if (info.name.find('.') < 0): |
28299 | 1508 info.name = ("%w.[%s:%d].%s" % |
1509 (info.name, info.address, info.port, info.type)) | |
28298 | 1510 self.checkService(info) |
1511 return | |
1512 raise NonUniqueNameException | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1513 if now < nexttime: |
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1514 self.wait(nexttime - now) |
28298 | 1515 now = currentTimeMillis() |
1516 continue | |
1517 out = DNSOutgoing(_FLAGS_QR_QUERY | _FLAGS_AA) | |
1518 self.debug = out | |
1519 out.addQuestion(DNSQuestion(info.type, _TYPE_PTR, _CLASS_IN)) | |
28299 | 1520 out.addAuthoritativeAnswer(DNSPointer(info.type, _TYPE_PTR, |
1521 _CLASS_IN, _DNS_TTL, info.name)) | |
28298 | 1522 self.send(out) |
1523 i += 1 | |
28419
eb9d0e828c30
zeroconf: remove camelcase in identifiers
timeless <timeless@mozdev.org>
parents:
28302
diff
changeset
|
1524 nexttime += _CHECK_TIME |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1525 |
28298 | 1526 def addListener(self, listener, question): |
1527 """Adds a listener for a given question. The listener will have | |
1528 its updateRecord method called when information is available to | |
1529 answer the question.""" | |
1530 now = currentTimeMillis() | |
1531 self.listeners.append(listener) | |
1532 if question is not None: | |
1533 for record in self.cache.entriesWithName(question.name): | |
1534 if question.answeredBy(record) and not record.isExpired(now): | |
1535 listener.updateRecord(self, now, record) | |
1536 self.notifyAll() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1537 |
28298 | 1538 def removeListener(self, listener): |
1539 """Removes a listener.""" | |
1540 try: | |
1541 self.listeners.remove(listener) | |
1542 self.notifyAll() | |
1543 except Exception: | |
1544 pass | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1545 |
28298 | 1546 def updateRecord(self, now, rec): |
1547 """Used to notify listeners of new information that has updated | |
1548 a record.""" | |
1549 for listener in self.listeners: | |
1550 listener.updateRecord(self, now, rec) | |
1551 self.notifyAll() | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1552 |
28298 | 1553 def handleResponse(self, msg): |
1554 """Deal with incoming response packets. All answers | |
1555 are held in the cache, and listeners are notified.""" | |
1556 now = currentTimeMillis() | |
1557 for record in msg.answers: | |
1558 expired = record.isExpired(now) | |
1559 if record in self.cache.entries(): | |
1560 if expired: | |
1561 self.cache.remove(record) | |
1562 else: | |
1563 entry = self.cache.get(record) | |
1564 if entry is not None: | |
1565 entry.resetTTL(record) | |
1566 record = entry | |
1567 else: | |
1568 self.cache.add(record) | |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
1569 |
28298 | 1570 self.updateRecord(now, record) |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1571 |
28298 | 1572 def handleQuery(self, msg, addr, port): |
1573 """Deal with incoming query packets. Provides a response if | |
1574 possible.""" | |
1575 out = None | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1576 |
28298 | 1577 # Support unicast client responses |
1578 # | |
1579 if port != _MDNS_PORT: | |
1580 out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA, 0) | |
1581 for question in msg.questions: | |
1582 out.addQuestion(question) | |
7874
d812029cda85
cleanup: drop variables for unused return values
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7071
diff
changeset
|
1583 |
28298 | 1584 for question in msg.questions: |
1585 if question.type == _TYPE_PTR: | |
1586 if question.name == "_services._dns-sd._udp.local.": | |
1587 for stype in self.servicetypes.keys(): | |
1588 if out is None: | |
1589 out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) | |
28299 | 1590 out.addAnswer(msg, |
1591 DNSPointer( | |
1592 "_services._dns-sd._udp.local.", | |
1593 _TYPE_PTR, _CLASS_IN, | |
1594 _DNS_TTL, stype)) | |
28298 | 1595 for service in self.services.values(): |
1596 if question.name == service.type: | |
1597 if out is None: | |
1598 out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) | |
28299 | 1599 out.addAnswer(msg, DNSPointer(service.type, _TYPE_PTR, |
1600 _CLASS_IN, _DNS_TTL, service.name)) | |
28298 | 1601 else: |
1602 try: | |
1603 if out is None: | |
1604 out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) | |
7874
d812029cda85
cleanup: drop variables for unused return values
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7071
diff
changeset
|
1605 |
28298 | 1606 # Answer A record queries for any service addresses we know |
1607 if question.type == _TYPE_A or question.type == _TYPE_ANY: | |
1608 for service in self.services.values(): | |
1609 if service.server == question.name.lower(): | |
28299 | 1610 out.addAnswer(msg, |
1611 DNSAddress(question.name, _TYPE_A, | |
1612 _CLASS_IN | _CLASS_UNIQUE, | |
1613 _DNS_TTL, service.address)) | |
7874
d812029cda85
cleanup: drop variables for unused return values
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7071
diff
changeset
|
1614 |
28298 | 1615 service = self.services.get(question.name.lower(), None) |
35629
31451f3f4b56
style: remove multiple statement on a single line in zeroconf
Boris Feld <boris.feld@octobus.net>
parents:
34447
diff
changeset
|
1616 if not service: |
31451f3f4b56
style: remove multiple statement on a single line in zeroconf
Boris Feld <boris.feld@octobus.net>
parents:
34447
diff
changeset
|
1617 continue |
7874
d812029cda85
cleanup: drop variables for unused return values
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7071
diff
changeset
|
1618 |
28299 | 1619 if (question.type == _TYPE_SRV or |
1620 question.type == _TYPE_ANY): | |
1621 out.addAnswer(msg, | |
1622 DNSService(question.name, _TYPE_SRV, | |
1623 _CLASS_IN | _CLASS_UNIQUE, | |
1624 _DNS_TTL, service.priority, | |
1625 service.weight, service.port, | |
1626 service.server)) | |
1627 if (question.type == _TYPE_TXT or | |
1628 question.type == _TYPE_ANY): | |
1629 out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, | |
1630 _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text)) | |
28298 | 1631 if question.type == _TYPE_SRV: |
28299 | 1632 out.addAdditionalAnswer( |
1633 DNSAddress(service.server, _TYPE_A, | |
1634 _CLASS_IN | _CLASS_UNIQUE, | |
1635 _DNS_TTL, service.address)) | |
28298 | 1636 except Exception: |
1637 traceback.print_exc() | |
7874
d812029cda85
cleanup: drop variables for unused return values
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7071
diff
changeset
|
1638 |
28298 | 1639 if out is not None and out.answers: |
1640 out.id = msg.id | |
1641 self.send(out, addr, port) | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1642 |
28302
e96a3ae025ed
zeroconf: remove whitespace around = for named parameters
timeless <timeless@mozdev.org>
parents:
28301
diff
changeset
|
1643 def send(self, out, addr=_MDNS_ADDR, port=_MDNS_PORT): |
28298 | 1644 """Sends an outgoing packet.""" |
1645 # This is a quick test to see if we can parse the packets we generate | |
1646 #temp = DNSIncoming(out.packet()) | |
1647 try: | |
1648 self.socket.sendto(out.packet(), 0, (addr, port)) | |
1649 except Exception: | |
1650 # Ignore this, it may be a temporary loss of network connection | |
1651 pass | |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1652 |
28298 | 1653 def close(self): |
1654 """Ends the background threads, and prevent this instance from | |
1655 servicing further queries.""" | |
1656 if globals()['_GLOBAL_DONE'] == 0: | |
1657 globals()['_GLOBAL_DONE'] = 1 | |
1658 self.notifyAll() | |
1659 self.engine.notify() | |
1660 self.unregisterAllServices() | |
28299 | 1661 self.socket.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, |
1662 socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0')) | |
28298 | 1663 self.socket.close() |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
1664 |
7071
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1665 # Test a few module features, including service registration, service |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1666 # query (for Zoe), and service unregistration. |
643c751e60b2
zeroconf: initial implementation
Matt Mackall <mpm@selenic.com>
parents:
diff
changeset
|
1667 |
7877
eba7f12b0c51
cleanup: whitespace cleanup
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents:
7874
diff
changeset
|
1668 if __name__ == '__main__': |
28298 | 1669 print("Multicast DNS Service Discovery for Python, version", __version__) |
1670 r = Zeroconf() | |
1671 print("1. Testing registration of a service...") | |
1672 desc = {'version':'0.10','a':'test value', 'b':'another value'} | |
28299 | 1673 info = ServiceInfo("_http._tcp.local.", |
1674 "My Service Name._http._tcp.local.", | |
1675 socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc) | |
28298 | 1676 print(" Registering service...") |
1677 r.registerService(info) | |
1678 print(" Registration done.") | |
1679 print("2. Testing query of service information...") | |
28299 | 1680 print(" Getting ZOE service:", |
1681 str(r.getServiceInfo("_http._tcp.local.", "ZOE._http._tcp.local."))) | |
28298 | 1682 print(" Query done.") |
1683 print("3. Testing query of own service...") | |
28299 | 1684 print(" Getting self:", |
1685 str(r.getServiceInfo("_http._tcp.local.", | |
1686 "My Service Name._http._tcp.local."))) | |
28298 | 1687 print(" Query done.") |
1688 print("4. Testing unregister of service information...") | |
1689 r.unregisterService(info) | |
1690 print(" Unregister done.") | |
1691 r.close() |