Name Module¶
The Name module provides a class for DNS names.
The Name class¶
A name is initialized from a tuple of label.Label:
from dike import Label, Name, make_name
python_sitename = Name((Label(b'www'),
Label(b'python'),
Label(b'org'),))
We can also create a name from a string using
Name.fromstr(), which makes a label for each dot-separated
piece. Each label is converted to
Punycode <https://tools.ietf.org/html/rfc3492>`_:
linux_sitename = Name.fromstr('www.kernel.org')
idn_city = Name.fromstr('Gießen.de')
idn_country = Name.fromstr('België.be')
A bytes value can also be used via
Name.frombytes(), which make a label for each dot-separated
piece. It does no further conversion:
bytes_name = Name.frombytes(b'non_unicode.name\0x80')
Finally, a name can be initialized from a tuple of bytes, which is useful when creating names from a packet, for example:
tuple_name = Name.fromtuple((b'ftp', b'isc', b'org'))
If you are uncertain of which type you will be using to initialize, or
just want to make life easy, the make_name() function will
create a name from any supported type:
example_name = make_name('a.good.example')
Note
All names are treated as Fully-Qualified Domain Names (FQDN), also called
absolute names. Names are parsed the same if they end with a
dot, ".", whether they do or not. For example,
"foo.example." is the same as "foo.example" when used to
initialize a Name instance.
As with label.Label instances, Name instances
are immutable - once initialized the value never changes. This has the
advantage of allowing labels to be used as keys in dict
and various other places, but does mean that updating a name is not
possible.
Name comparisons use the canonical ordering defined in RFC 4034. This sorts the names by the right-most label:
assert Name.fromstr('example') < Name.fromstr('Z.a.example')
assert Name.fromstr('Z.a.example') > Name.fromstr('a.example')
assert Name.fromstr('Z.a.eXaMPLe') == Name.fromstr('z.A.ExAmplE')
In general, names can be treated as sequences:
k_root = Name.fromstr('k.root-servers.net')
for label_val in k_root:
print(label_val)
assert 'net.root-servers.k' == str(Name(tuple(reversed(k_root))))
assert 'f.root-servers.net' == 'f' + k_root[1:]
You can use the in operator to check for the presence of a given label in a name. Note that this searches for _labels_, not _substrings_:
foo = Name.fromstr('foo.example')
assert "example" in "foo.example" # we end with "example"
assert "exam" not in "foo.example" # "exam" is not a label
assert "exam" in str("foo.example") # "exam" is a substring
Similarly the built-in len() function will return the number of labels in a name, rather than the length of the name as a string. You can get the length of a name either as a str or bytes value by explicitly converting first:
assert len(foo) == 2
assert len(str(foo)) == 11
Converting to bytes works as expected, except in the case where
there is a dot . character in one of the labels. In that case, a
LabelHasDot exception will be raised. Converting to str
also works as expected, except when there is a dot . character in
one of the labels (when a LabelHasDot exception will be
raised) or a non-Unicode byte sequence (when a
UnicodeError will be raised). To convert to a format in a
safe way, use the Name.to_presentation() method:
bar = Name.fromtuple((b'www',b'K\x80T',b'd.t'))
assert bar.to_presentation() == 'www.K\\128T.d\\.t.''
To check for parent/child relationships you can use the
Name.startswith() or Name.endswith() methods, or
slicing:
child = Name.fromstr('child.parent.zone')
parent = child[1:]
assert str(parent) == 'parent.zone'
assert child.endswith(parent)
assert child.endswith('zone')
These methods work very similar to the built-in
str.startswith() or str.endswith() methods,
including the ability to specify start & end values, as well as
comparing with multiple targets by passing a tuple:
zone_to_check = Name.fromstr('lots-o.info')
assert zone_to_check.endswith(('com', 'net', 'org', 'info'))
A couple of utility functions are also available to check for common types of names:
host_name = Name.fromstr('valid.hostname)
not_host_name = Name.fromstr('in_valid.hostname')
assert hostname.ishost() == True
assert not_host_name.ishost() == False
assert Name().isroot() == True
assert Name('any-other-name').isroot() == False
Name Objects¶
-
class
name.Name(labels: Tuple[dike.label.Label, ...] = (), *, canonicalize: bool = False)[source]¶ The
Nameconstructor requires a single argument, which is a tuple ofLabelobjects.Optionally canonicalize can be set to True, in which case the name will be set to the canonical (that is, lowercase) version.
If the name is more than 253 characters long, a
NameTooLongexception will be raised.Parameters: - val (tuple of Label) – Value to use when creating the name.
- canonicalize (bool) – Whether to canonicalize on creation.
Raises: -
endswith(suffix: Union[Name, dike.label.Label, str, bytes, Tuple[Union[Name, dike.label.Label, str, bytes], ...]], start: int = 0, end: Optional[int] = None) → bool[source]¶ Return True if the name ends with the specified suffix. With optional start, test the name beginning at that label. With optional end, stop comparing the name at that label.
The suffix can be a
Name,Label,str, orbytes.The suffix can also be a
tupleofNameor things that can be converted toName(str,bytes, orlabel.Label).Parameters: Returns: Whether the name ends with the specified prefix.
Return type: Raises: TypeErrorwhen suffix cannot be compared
-
classmethod
frombytes(val: bytes, *, canonicalize: bool = False) → name.Name[source]¶ Convert the specified value to a Name object, by splitting it into labels first.
Optionally canonicalize can be set to True, in which case the name will be set to the canonical (that is, lowercase) version.
A number of issues will cause an exception to be raised. Note that whitespace is not treated specially in any way, so if you want to trim it do so before passing to this method.
Parameters: Raises: EmptyLabelRaises: LabelTooLongRaises:
-
classmethod
fromstr(val: str, *, canonicalize: bool = False) → name.Name[source]¶ Convert the specified value to a Name object, by splitting it into labels first. It is converted to Punycode as necessary.
Optionally canonicalize can be set to True, in which case the name will be set to the canonical (that is, lowercase) version.
A number of issues will cause an exception to be raised. Note that whitespace is not treated specially in any way, so if you want to trim it do so before passing to this method.
Parameters: Raises: Raises: EmptyLabelRaises: LabelTooLongRaises:
-
classmethod
fromtuple(val: Tuple[bytes, ...], *, canonicalize: bool = False) → name.Name[source]¶ Convert the specified value to a Name object, by converting each element into a
Labelfirst.Optionally canonicalize can be set to True, in which case the name will be set to the canonical (that is, lowercase) version.
A number of issues will cause an exception to be raised.
Parameters: - val (typle of bytes) – Value to use when creating the name.
- canonicalize (bool) – Whether to canonicalize on creation.
Raises: EmptyLabelRaises: LabelTooLongRaises:
-
ishost() → bool[source]¶ Test if the name is valid as a host name. The rules for host names are defined in:
Return type: bool
-
startswith(prefix: Union[Name, dike.label.Label, str, bytes, Tuple[Union[Name, dike.label.Label, str, bytes], ...]], start: int = 0, end: Optional[int] = None) → bool[source]¶ Return True if the name starts with the specified prefix. With optional start, test the name beginning at that label. With optional end, stop comparing the name at that label.
The prefix can be a
Name,Label,str, orbytes.The prefix can also be a
tupleofNameor things that can be converted toName(str,bytes, orlabel.Label).Parameters: Returns: Whether the name ends with the specified prefix.
Return type: Raises: TypeErrorwhen prefix cannot be compared
Exceptions¶
-
class
name.NameTooLong[source]¶ Exception raised when trying to initialize a name of more than 253 octets.
This corresponds to a name longer than 255 octets when converted to wire format, since the wire format has a length indicator for the first label and a terminating length indicator of 0.
-
class
name.LabelHasDot[source]¶ Exception raised when trying to convert a name to a string or bytes value which has a dot,
'.', in a label. In that case, we cannot convert to a string or bytes value, since it would be impossible to tell which'.'are separators and which are part of a label.For example:
# very legal and very cool label_with_dot = Label('label.with.dot') le_name = Name.fromtuple((label_with_dot, 'example', 'com')) # but this will raise LabelHasDot print(le_name)
If you need to work with names that might possibly contain a dot in them (for example if processing packets from the Internet), you can treat labels separately, or use the wire or presentation format for names.