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