3030import sys
3131import threading
3232import time
33+ import warnings
3334from functools import reduce
3435from typing import AnyStr , Dict , List , Optional , Union , cast
3536from typing import Callable , Set , Tuple # noqa # used in type hints
@@ -1429,38 +1430,59 @@ class ServiceInfo(RecordUpdateListener):
14291430
14301431 """Service information"""
14311432
1433+ # FIXME(dtantsur): black 19.3b0 produces code that is not valid syntax on
1434+ # Python 3.5: https://github.com/python/black/issues/759
1435+ # fmt: off
14321436 def __init__ (
14331437 self ,
14341438 type_ : str ,
14351439 name : str ,
1436- address : Optional [bytes ] = None ,
1440+ address : Optional [Union [ bytes , List [ bytes ]] ] = None ,
14371441 port : Optional [int ] = None ,
14381442 weight : int = 0 ,
14391443 priority : int = 0 ,
14401444 properties = b'' ,
14411445 server : Optional [str ] = None ,
14421446 host_ttl : int = _DNS_HOST_TTL ,
14431447 other_ttl : int = _DNS_OTHER_TTL ,
1448+ * ,
1449+ addresses : Optional [List [bytes ]] = None
14441450 ) -> None :
14451451 """Create a service description.
14461452
14471453 type_: fully qualified service type name
14481454 name: fully qualified service name
1449- address: IP address as unsigned short, network byte order
1455+ address: IP address as unsigned short, network byte order (deprecated, use addresses)
14501456 port: port that the service runs on
14511457 weight: weight of the service
14521458 priority: priority of the service
14531459 properties: dictionary of properties (or a string holding the
14541460 bytes for the text field)
14551461 server: fully qualified name for service host (defaults to name)
14561462 host_ttl: ttl used for A/SRV records
1457- other_ttl: ttl used for PTR/TXT records"""
1463+ other_ttl: ttl used for PTR/TXT records
1464+ addresses: List of IP addresses as unsigned short, network byte
1465+ order
1466+ """
1467+
1468+ # Accept both none, or one, but not both.
1469+ if address is not None and addresses is not None :
1470+ raise TypeError ("address and addresses cannot be provided together" )
14581471
14591472 if not type_ .endswith (service_type_name (name , allow_underscores = True )):
14601473 raise BadTypeInNameException
14611474 self .type = type_
14621475 self .name = name
1463- self .address = address
1476+ if addresses is not None :
1477+ self .addresses = addresses
1478+ elif address is not None :
1479+ warnings .warn ("address is deprecated, use addresses instead" , DeprecationWarning )
1480+ if isinstance (address , list ):
1481+ self .addresses = address
1482+ else :
1483+ self .addresses = [address ]
1484+ else :
1485+ self .addresses = []
14641486 self .port = port
14651487 self .weight = weight
14661488 self .priority = priority
@@ -1472,6 +1494,23 @@ def __init__(
14721494 self ._set_properties (properties )
14731495 self .host_ttl = host_ttl
14741496 self .other_ttl = other_ttl
1497+ # fmt: on
1498+
1499+ @property
1500+ def address (self ):
1501+ warnings .warn ("ServiceInfo.address is deprecated, use addresses instead" , DeprecationWarning )
1502+ try :
1503+ return self .addresses [0 ]
1504+ except IndexError :
1505+ return None
1506+
1507+ @address .setter
1508+ def address (self , value ):
1509+ warnings .warn ("ServiceInfo.address is deprecated, use addresses instead" , DeprecationWarning )
1510+ if value is None :
1511+ self .addresses = []
1512+ else :
1513+ self .addresses = [value ]
14751514
14761515 @property
14771516 def properties (self ) -> ServicePropertiesType :
@@ -1553,7 +1592,8 @@ def update_record(self, zc: 'Zeroconf', now: float, record: DNSRecord) -> None:
15531592 assert isinstance (record , DNSAddress )
15541593 # if record.name == self.name:
15551594 if record .name == self .server :
1556- self .address = record .address
1595+ if record .address not in self .addresses :
1596+ self .addresses .append (record .address )
15571597 elif record .type == _TYPE_SRV :
15581598 assert isinstance (record , DNSService )
15591599 if record .name == self .name :
@@ -1585,12 +1625,12 @@ def request(self, zc: 'Zeroconf', timeout: float) -> bool:
15851625 if cached :
15861626 self .update_record (zc , now , cached )
15871627
1588- if None not in ( self . server , self .address , self .text ) :
1628+ if self . server is not None and self .text is not None and self .addresses :
15891629 return True
15901630
15911631 try :
15921632 zc .add_listener (self , DNSQuestion (self .name , _TYPE_ANY , _CLASS_IN ))
1593- while None in ( self .server , self .address , self .text ) :
1633+ while self .server is None or self .text is None or not self .addresses :
15941634 if last <= now :
15951635 return False
15961636 if next_ <= now :
@@ -1629,7 +1669,16 @@ def __repr__(self) -> str:
16291669 type (self ).__name__ ,
16301670 ', ' .join (
16311671 '%s=%r' % (name , getattr (self , name ))
1632- for name in ('type' , 'name' , 'address' , 'port' , 'weight' , 'priority' , 'server' , 'properties' )
1672+ for name in (
1673+ 'type' ,
1674+ 'name' ,
1675+ 'addresses' ,
1676+ 'port' ,
1677+ 'weight' ,
1678+ 'priority' ,
1679+ 'server' ,
1680+ 'properties' ,
1681+ )
16331682 ),
16341683 )
16351684
@@ -1916,10 +1965,8 @@ def _broadcast_service(self, info: ServiceInfo) -> None:
19161965 )
19171966
19181967 out .add_answer_at_time (DNSText (info .name , _TYPE_TXT , _CLASS_IN , info .other_ttl , info .text ), 0 )
1919- if info .address :
1920- out .add_answer_at_time (
1921- DNSAddress (info .server , _TYPE_A , _CLASS_IN , info .host_ttl , info .address ), 0
1922- )
1968+ for address in info .addresses :
1969+ out .add_answer_at_time (DNSAddress (info .server , _TYPE_A , _CLASS_IN , info .host_ttl , address ), 0 )
19231970 self .send (out )
19241971 i += 1
19251972 next_time += _REGISTER_TIME
@@ -1952,8 +1999,8 @@ def unregister_service(self, info: ServiceInfo) -> None:
19521999 )
19532000 out .add_answer_at_time (DNSText (info .name , _TYPE_TXT , _CLASS_IN , 0 , info .text ), 0 )
19542001
1955- if info .address :
1956- out .add_answer_at_time (DNSAddress (info .server , _TYPE_A , _CLASS_IN , 0 , info . address ), 0 )
2002+ for address in info .addresses :
2003+ out .add_answer_at_time (DNSAddress (info .server , _TYPE_A , _CLASS_IN , 0 , address ), 0 )
19572004 self .send (out )
19582005 i += 1
19592006 next_time += _UNREGISTER_TIME
@@ -1986,10 +2033,8 @@ def unregister_all_services(self) -> None:
19862033 0 ,
19872034 )
19882035 out .add_answer_at_time (DNSText (info .name , _TYPE_TXT , _CLASS_IN , 0 , info .text ), 0 )
1989- if info .address :
1990- out .add_answer_at_time (
1991- DNSAddress (info .server , _TYPE_A , _CLASS_IN , 0 , info .address ), 0
1992- )
2036+ for address in info .addresses :
2037+ out .add_answer_at_time (DNSAddress (info .server , _TYPE_A , _CLASS_IN , 0 , address ), 0 )
19932038 self .send (out )
19942039 i += 1
19952040 next_time += _UNREGISTER_TIME
@@ -2126,16 +2171,17 @@ def handle_query(self, msg: DNSIncoming, addr: str, port: int) -> None:
21262171 if question .type in (_TYPE_A , _TYPE_ANY ):
21272172 for service in self .services .values ():
21282173 if service .server == question .name .lower ():
2129- out .add_answer (
2130- msg ,
2131- DNSAddress (
2132- question .name ,
2133- _TYPE_A ,
2134- _CLASS_IN | _CLASS_UNIQUE ,
2135- service .host_ttl ,
2136- service .address ,
2137- ),
2138- )
2174+ for address in service .addresses :
2175+ out .add_answer (
2176+ msg ,
2177+ DNSAddress (
2178+ question .name ,
2179+ _TYPE_A ,
2180+ _CLASS_IN | _CLASS_UNIQUE ,
2181+ service .host_ttl ,
2182+ address ,
2183+ ),
2184+ )
21392185
21402186 name_to_find = question .name .lower ()
21412187 if name_to_find not in self .services :
@@ -2168,15 +2214,16 @@ def handle_query(self, msg: DNSIncoming, addr: str, port: int) -> None:
21682214 ),
21692215 )
21702216 if question .type == _TYPE_SRV :
2171- out .add_additional_answer (
2172- DNSAddress (
2173- service .server ,
2174- _TYPE_A ,
2175- _CLASS_IN | _CLASS_UNIQUE ,
2176- service .host_ttl ,
2177- service .address ,
2217+ for address in service .addresses :
2218+ out .add_additional_answer (
2219+ DNSAddress (
2220+ service .server ,
2221+ _TYPE_A ,
2222+ _CLASS_IN | _CLASS_UNIQUE ,
2223+ service .host_ttl ,
2224+ address ,
2225+ )
21782226 )
2179- )
21802227 except Exception : # TODO stop catching all Exceptions
21812228 self .log_exception_warning ()
21822229
0 commit comments