Label Module¶
The Label module provides classes for DNS labels.
The Label class¶
A label is initialized from bytes:
from dike.label import Label
monty = Label(b'flying-circus')
We can also create a label from a string using the
Label.fromstr() class method , which converts the string to
Punycode:
biter = Label.fromstr('møøsë')
print(biter) # møøsë (via implicit string conversion)
print(bytes(biter)) # b'xn--ms-ija4ca'
print(repr(biter)) # Label.fromstr('møøsë')
A utility function which will create a label from bytes, strings, or other labels is also available:
from dike.label import make_label
average_airspeed0 = make_label(b'African')
average_airspeed1 = make_label('European')
average_airspeed2 = make_label(average_airspeed1)
Labels 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 label is not possible. To make changes we can convert to either a
string or bytes value and use slicing or
concatenation to produce the value you want, then initialize a new
label:
country = Label.fromstr('holland')
same_country = Label.fromstr('nether' + str(country)[3:] + 's')
print(country) # holland
print(same_country) # netherlands
Label comparisons work as expected, and are case-insensitive (as per DNS specification):
print(Label(b'flying') == Label(b'circus')) # False
print(Label(b'flying') > Label(b'circus')) # True
print(Label(b'CIRCUS') == Label(b'circus')) # True
If you have a label you can also use bytes or strings in comparisons, and these work as if you had built a Label object for the comparison:
print(Label.fromstr('Norwegian') != 'blue') # True
print(Label(b'short') < b'shortness') # True
The module also includes a number of utility functions:
i_am_a_host_label = Label.fromstr('mailserver')
i_am_not_a_host_label = Label.fromstr('WITCH!!!')
print(i_am_a_host_label.ishost()) # True
print(i_am_a_host_label.canonical()) # b'mailserver'
print(i_am_not_a_host_label.ishost()) # False
print(i_am_not_a_host_label.canonical()) # b'witch!!!
Finally, be careful when converting arbitrary labels to strings, for
example when receiving labels in DNS packets from the Internet. This
can result in a UnicodeError being raised, if the bytes
cannot be represented in Punycode. You can use the
Label.to_presentation() method to convert the label to a
string the label in this case, for example when logging. This uses
escape sequences for any characters that might be interpreted as
special in a zone file, as defined in
RFC 1035:
bad_punycode = Label(b'scary-' + bytes([0x80]))
print(bad_punycode.to_presentation()) # scary-\128
The LabelFactory class¶
Since we often use the same label many times when dealing with DNS, it
can be more efficient to use the same instance for all occurrences of
a given label. For example, the label com is likely to appear in
many DNS names, and it can be more efficient to reuse the same label.
This is safe, because labels are immutable.
The LabelFactory class exists for this purpose:
from dike import LabelFactory, Label
zone_label_factory = LabelFactory()
foo = zone_label_factory.fromstr('foo')
bar = zone_label_factory.fromstr('foo')
baz = Label.fromstr('foo')
print(foo == bar) # True
print(foo is bar) # True
print(foo == baz) # True
print(foo is baz) # False
Notice that we can also create labels by normal object creation.
Comparisons (==, <, and so on) work as expected, but the
labels have different identities in this case (so the is
comparison is False).
The LabelFactory.frombytes() and
LabelFactory.fromlabel() methods are also available for
creating from bytes or other Label instances.
The labels in a LabelFactory are stored in a
weakref.WeakValueDictionary, so when a label is no longer
used memory used by the object will be released.
Label Objects¶
-
class
label.Label(label_val: bytes, *, canonicalize: bool = False)[source]¶ The
Labelconstructor requires a single value, which isbytes.If the optional canonicalize argument is used then the label will be converted to the canonical version (that is, ASCII lower-case).
If a label is more than 63 characters long, a
LabelTooLongexception will be raised. Attempting to create an empty lable (withb'') will raise aEmptyLabelexception.Parameters: label_val (bytes) – Value to use when creating the label. Raises: EmptyLabelRaises: LabelTooLong-
canonical() → bytes[source]¶ Return the
bytesrepresenting the canonical version of a label. This is the label converted to lowercase ASCII.Returns: canonical version of the label Return type: bytes
-
static
fromstr(label_val: str, *, canonicalize: bool = False) → label.Label[source]¶ Create an IDNA version of a string.
Certain Unicode values are not allowed in Punycode. A
UnicodeErrorexception will be raised in that case.Returns: a label Return type: Label Raises: UnicodeError – string cannot be converted to Punycode.
-
LabelFactory Objects¶
-
class
label.LabelFactory[source]¶ The
LabelFactoryconstructor takes no arguments.-
frombytes(label_bytes: bytes) → label.Label[source]¶ Get a
Labelinstance the same as one created from thebytespassed.Parameters: label_bytes (bytes) – bytes that we want a label of Return type: LabelRaises: EmptyLabelRaises: LabelTooLong
-
fromlabel(label: label.Label) → label.Label[source]¶ Get a
Labelinstance the same as the label passed.While you could use a simple assignment to also use the same label:
ipso = Label(b'facto') quid = ipso print(quid is ipso) # True
Using the
LabelFactoryfor this will store the reference in theLabelFactoryinstance, which might be useful when mixing creation from bothLabelandstr/bytes:factory = LabelFactory() ipso = Label(b'facto') quid = factory.fromlabel(ipso) pro = factory.fromstr('facto') print(quid is pro) # True
Parameters: label ( Label) – Label that we want to return an instance ofReturn type: Label
-
fromstr(label_str: str) → label.Label[source]¶ Get a
Labelinstance the same as one created from thestrpassed.Parameters: label_str (str) – string that we want a label of Return type: LabelRaises: EmptyLabelRaises: LabelTooLongRaises: UnicodeError – string cannot be converted to Punycode.
-