164 lines
4.5 KiB
Python
164 lines
4.5 KiB
Python
|
""" Multicast DNS Service Discovery for Python, v0.14-wmcbrine
|
||
|
Copyright 2003 Paul Scott-Murphy, 2014 William McBrine
|
||
|
|
||
|
This module provides a framework for the use of DNS Service Discovery
|
||
|
using IP multicast.
|
||
|
|
||
|
This library is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU Lesser General Public
|
||
|
License as published by the Free Software Foundation; either
|
||
|
version 2.1 of the License, or (at your option) any later version.
|
||
|
|
||
|
This library is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
Lesser General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Lesser General Public
|
||
|
License along with this library; if not, write to the Free Software
|
||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||
|
USA
|
||
|
"""
|
||
|
|
||
|
import re
|
||
|
import socket
|
||
|
|
||
|
# Some timing constants
|
||
|
|
||
|
_UNREGISTER_TIME = 125 # ms
|
||
|
_CHECK_TIME = 175 # ms
|
||
|
_REGISTER_TIME = 225 # ms
|
||
|
_LISTENER_TIME = 200 # ms
|
||
|
_BROWSER_TIME = 10000 # ms
|
||
|
_DUPLICATE_PACKET_SUPPRESSION_INTERVAL = 1000 # ms
|
||
|
_DUPLICATE_QUESTION_INTERVAL = 999 # ms # Must be 1ms less than _DUPLICATE_PACKET_SUPPRESSION_INTERVAL
|
||
|
_CACHE_CLEANUP_INTERVAL = 10 # s
|
||
|
_LOADED_SYSTEM_TIMEOUT = 10 # s
|
||
|
_STARTUP_TIMEOUT = 9 # s must be lower than _LOADED_SYSTEM_TIMEOUT
|
||
|
_ONE_SECOND = 1000 # ms
|
||
|
|
||
|
# If the system is loaded or the event
|
||
|
# loop was blocked by another task that was doing I/O in the loop
|
||
|
# (shouldn't happen but it does in practice) we need to give
|
||
|
# a buffer timeout to ensure a coroutine can finish before
|
||
|
# the future times out
|
||
|
|
||
|
# Some DNS constants
|
||
|
|
||
|
_MDNS_ADDR = '224.0.0.251'
|
||
|
_MDNS_ADDR6 = 'ff02::fb'
|
||
|
_MDNS_PORT = 5353
|
||
|
_DNS_PORT = 53
|
||
|
_DNS_HOST_TTL = 120 # two minute for host records (A, SRV etc) as-per RFC6762
|
||
|
_DNS_OTHER_TTL = 4500 # 75 minutes for non-host records (PTR, TXT etc) as-per RFC6762
|
||
|
# Currently we enforce a minimum TTL for PTR records to avoid
|
||
|
# ServiceBrowsers generating excessive queries refresh queries.
|
||
|
# Apple uses a 15s minimum TTL, however we do not have the same
|
||
|
# level of rate limit and safe guards so we use 1/4 of the recommended value
|
||
|
_DNS_PTR_MIN_TTL = _DNS_OTHER_TTL / 4
|
||
|
|
||
|
_DNS_PACKET_HEADER_LEN = 12
|
||
|
|
||
|
_MAX_MSG_TYPICAL = 1460 # unused
|
||
|
_MAX_MSG_ABSOLUTE = 8966
|
||
|
|
||
|
_FLAGS_QR_MASK = 0x8000 # query response mask
|
||
|
_FLAGS_QR_QUERY = 0x0000 # query
|
||
|
_FLAGS_QR_RESPONSE = 0x8000 # response
|
||
|
|
||
|
_FLAGS_AA = 0x0400 # Authoritative answer
|
||
|
_FLAGS_TC = 0x0200 # Truncated
|
||
|
_FLAGS_RD = 0x0100 # Recursion desired
|
||
|
_FLAGS_RA = 0x8000 # Recursion available
|
||
|
|
||
|
_FLAGS_Z = 0x0040 # Zero
|
||
|
_FLAGS_AD = 0x0020 # Authentic data
|
||
|
_FLAGS_CD = 0x0010 # Checking disabled
|
||
|
|
||
|
_CLASS_IN = 1
|
||
|
_CLASS_CS = 2
|
||
|
_CLASS_CH = 3
|
||
|
_CLASS_HS = 4
|
||
|
_CLASS_NONE = 254
|
||
|
_CLASS_ANY = 255
|
||
|
_CLASS_MASK = 0x7FFF
|
||
|
_CLASS_UNIQUE = 0x8000
|
||
|
_CLASS_IN_UNIQUE = _CLASS_IN | _CLASS_UNIQUE
|
||
|
|
||
|
_TYPE_A = 1
|
||
|
_TYPE_NS = 2
|
||
|
_TYPE_MD = 3
|
||
|
_TYPE_MF = 4
|
||
|
_TYPE_CNAME = 5
|
||
|
_TYPE_SOA = 6
|
||
|
_TYPE_MB = 7
|
||
|
_TYPE_MG = 8
|
||
|
_TYPE_MR = 9
|
||
|
_TYPE_NULL = 10
|
||
|
_TYPE_WKS = 11
|
||
|
_TYPE_PTR = 12
|
||
|
_TYPE_HINFO = 13
|
||
|
_TYPE_MINFO = 14
|
||
|
_TYPE_MX = 15
|
||
|
_TYPE_TXT = 16
|
||
|
_TYPE_AAAA = 28
|
||
|
_TYPE_SRV = 33
|
||
|
_TYPE_NSEC = 47
|
||
|
_TYPE_ANY = 255
|
||
|
|
||
|
# Mapping constants to names
|
||
|
|
||
|
_CLASSES = {
|
||
|
_CLASS_IN: "in",
|
||
|
_CLASS_CS: "cs",
|
||
|
_CLASS_CH: "ch",
|
||
|
_CLASS_HS: "hs",
|
||
|
_CLASS_NONE: "none",
|
||
|
_CLASS_ANY: "any",
|
||
|
}
|
||
|
|
||
|
_TYPES = {
|
||
|
_TYPE_A: "a",
|
||
|
_TYPE_NS: "ns",
|
||
|
_TYPE_MD: "md",
|
||
|
_TYPE_MF: "mf",
|
||
|
_TYPE_CNAME: "cname",
|
||
|
_TYPE_SOA: "soa",
|
||
|
_TYPE_MB: "mb",
|
||
|
_TYPE_MG: "mg",
|
||
|
_TYPE_MR: "mr",
|
||
|
_TYPE_NULL: "null",
|
||
|
_TYPE_WKS: "wks",
|
||
|
_TYPE_PTR: "ptr",
|
||
|
_TYPE_HINFO: "hinfo",
|
||
|
_TYPE_MINFO: "minfo",
|
||
|
_TYPE_MX: "mx",
|
||
|
_TYPE_TXT: "txt",
|
||
|
_TYPE_AAAA: "quada",
|
||
|
_TYPE_SRV: "srv",
|
||
|
_TYPE_ANY: "any",
|
||
|
_TYPE_NSEC: "nsec",
|
||
|
}
|
||
|
|
||
|
_ADDRESS_RECORD_TYPES = {_TYPE_A, _TYPE_AAAA}
|
||
|
|
||
|
_HAS_A_TO_Z = re.compile(r'[A-Za-z]')
|
||
|
_HAS_ONLY_A_TO_Z_NUM_HYPHEN = re.compile(r'^[A-Za-z0-9\-]+$')
|
||
|
_HAS_ONLY_A_TO_Z_NUM_HYPHEN_UNDERSCORE = re.compile(r'^[A-Za-z0-9\-\_]+$')
|
||
|
_HAS_ASCII_CONTROL_CHARS = re.compile(r'[\x00-\x1f\x7f]')
|
||
|
|
||
|
_EXPIRE_REFRESH_TIME_PERCENT = 75
|
||
|
|
||
|
_LOCAL_TRAILER = '.local.'
|
||
|
_TCP_PROTOCOL_LOCAL_TRAILER = '._tcp.local.'
|
||
|
_NONTCP_PROTOCOL_LOCAL_TRAILER = '._udp.local.'
|
||
|
|
||
|
# https://datatracker.ietf.org/doc/html/rfc6763#section-9
|
||
|
_SERVICE_TYPE_ENUMERATION_NAME = "_services._dns-sd._udp.local."
|
||
|
|
||
|
try:
|
||
|
_IPPROTO_IPV6 = socket.IPPROTO_IPV6
|
||
|
except AttributeError:
|
||
|
# Sigh: https://bugs.python.org/issue29515
|
||
|
_IPPROTO_IPV6 = 41
|