Проверка строки имени хоста
-
22-09-2019 - |
Вопрос
Следуя за Регулярное выражение для соответствия имени хоста или IP-адресу?и использование Ограничения на действительные имена хостов В качестве справки, какой наиболее читабельный и краткий способ сопоставить/проверить имя хоста/fqdn (полное доменное имя) в Python?Я ответил своей попыткой ниже, улучшения приветствуются.
Решение
import re
def is_valid_hostname(hostname):
if len(hostname) > 255:
return False
if hostname[-1] == ".":
hostname = hostname[:-1] # strip exactly one dot from the right, if present
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
return all(allowed.match(x) for x in hostname.split("."))
гарантирует, что каждый сегмент
- содержит минимум один символ и максимум 63 символа
- состоит только из разрешенных символов
- не начинается и не заканчивается дефисом.
Это также позволяет избежать двойного отрицания (not disallowed
), и если hostname
заканчивается в .
, тоже норм.Это потерпит (и должно) потерпеть неудачу, если hostname
заканчивается более чем одной точкой.
Другие советы
Пер Старая новая вещь, максимальная длина DNS-имени составляет 253 символа.(В одном разрешено до 255 октетов, но 2 из них используются при кодировании.)
import re
def validate_fqdn(dn):
if dn.endswith('.'):
dn = dn[:-1]
if len(dn) < 1 or len(dn) > 253:
return False
ldh_re = re.compile('^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$',
re.IGNORECASE)
return all(ldh_re.match(x) for x in dn.split('.'))
Можно спорить о том, следует ли принимать пустые доменные имена или нет, в зависимости от цели.
Вот немного более строгая версия Ответ Тима Пицкера со следующими улучшениями:
- Ограничьте длину имени хоста 253 символами (после удаления необязательной конечной точки).
- Ограничьте набор символов ASCII (т.использовать
[0-9]
вместо\d
). - Убедитесь, что TLD не является полностью числовым.
import re
def is_valid_hostname(hostname):
if hostname[-1] == ".":
# strip exactly one dot from the right, if present
hostname = hostname[:-1]
if len(hostname) > 253:
return False
labels = hostname.split(".")
# the TLD must be not all-numeric
if re.match(r"[0-9]+$", labels[-1]):
return False
allowed = re.compile(r"(?!-)[a-z0-9-]{1,63}(?<!-)$", re.IGNORECASE)
return all(allowed.match(label) for label in labels)
Мне нравится тщательность ответа Тима Пицкера, но я предпочитаю выгрузить часть логики из регулярных выражений для удобства чтения.Честно говоря, мне пришлось поискать значение этих слов. (?
части «обозначения расширения».Кроме того, я считаю, что подход «двойного отрицания» более очевиден, поскольку он ограничивает ответственность регулярного выражения простым поиском любого недопустимого символа.Мне нравится, что re.IGNORECASE позволяет сокращать регулярное выражение.
Итак, вот еще один снимок;оно длиннее, но читается как проза.Я полагаю, что «читабельный» несколько расходится с «кратким».Я считаю, что все ограничения проверки, упомянутые в теме, покрыты:
def isValidHostname(hostname):
if len(hostname) > 255:
return False
if hostname.endswith("."): # A single trailing dot is legal
hostname = hostname[:-1] # strip exactly one dot from the right, if present
disallowed = re.compile("[^A-Z\d-]", re.IGNORECASE)
return all( # Split by labels and verify individually
(label and len(label) <= 63 # length is within proper range
and not label.startswith("-") and not label.endswith("-") # no bordering hyphens
and not disallowed.search(label)) # contains only legal characters
for label in hostname.split("."))
def is_valid_host(host):
'''IDN compatible domain validator'''
host = host.encode('idna').lower()
if not hasattr(is_valid_host, '_re'):
import re
is_valid_host._re = re.compile(r'^([0-9a-z][-\w]*[0-9a-z]\.)+[a-z0-9\-]{2,15}$')
return bool(is_valid_host._re.match(host))
В дополнение к ответу @TimPietzcker.Нижнее подчеркивание является допустимым символом имени хоста (но не для имени домена).Хотя двойное тире обычно встречается в домене IDN Punycode (например,хн--).Номер порта должен быть удален.Это очистка кода.
import re
def is_valid_hostname(hostname):
if len(hostname) > 255:
return False
hostname = hostname.rstrip(".")
allowed = re.compile("(?!-)[A-Z\d\-\_]{1,63}(?<!-)$", re.IGNORECASE)
return all(allowed.match(x) for x in hostname.split("."))
# convert your unicode hostname to punycode (python 3 )
# Remove the port number from hostname
normalise_host = hostname.encode("idna").decode().split(":")[0]
is_valid_hostanme(normalise_host )
Я думаю, что это регулярное выражение может помочь в Python:'^([a-zA-Z0-9]+(\.|\-))*[a-zA-Z0-9]+$'
Обрабатывайте каждую метку DNS индивидуально, исключая недопустимые символы и обеспечивая ненулевую длину.
def isValidHostname(hostname):
disallowed = re.compile("[^a-zA-Z\d\-]")
return all(map(lambda x: len(x) and not disallowed.search(x), hostname.split(".")))
Если вы хотите проверить имя существующего хоста, лучший способ — попытаться разрешить его.Вы никогда не напишете регулярное выражение для обеспечения такого уровня проверки.