Проверьте строку имени хоста - PullRequest
24 голосов
/ 28 марта 2010

После до Регулярное выражение для совпадения имени хоста или IP-адреса? и используя ограничения на допустимые имена хостов в качестве ссылки, каков наиболее читаемый и краткий способ сопоставления / проверки имени хоста / fqdn (полное доменное имя) в Python? Я ответил с моей попыткой ниже, улучшения приветствуются.

Ответы [ 8 ]

44 голосов
/ 28 марта 2010
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 оканчивается более чем на одну точку.

4 голосов
/ 15 ноября 2015

Per The Old New Thing , максимальная длина имени 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('.'))

Кто-то может утверждать, что принимает пустые доменные имена, или нет, в зависимости от цели.

3 голосов
/ 19 октября 2015

Вот немного более строгая версия Ответ Тима Пицкера со следующими улучшениями:

  • Ограничить длину имени хоста 253 символами (после удаления дополнительной конечной точки).
  • Ограничить набор символов ASCII (т. Е. Использовать [0-9] вместо \d).
  • Убедитесь, что ДВУ не является полностью числовым.
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)
1 голос
/ 29 марта 2010

Мне нравится тщательность ответа Тима Пицкера, но я предпочитаю выгружать часть логики из регулярных выражений для удобства чтения. Честно говоря, мне пришлось искать значение этих (? «расширенных обозначений». Кроме того, я считаю, что «двойной отрицательный» подход более очевиден в том смысле, что он ограничивает ответственность регулярного выражения только нахождением любого недопустимого символа. Мне нравится, что 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("."))
0 голосов
/ 04 апреля 2017

Дополнительно к ответу @TimPietzcker. Underscore - допустимый символ имени хоста (но не для имени домена). В то время как двойная черта обычно встречается в домене IDN punycode (например, xn--). Номер порта должен быть удален. Это очистка кода.

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 )
0 голосов
/ 13 июня 2011
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))
0 голосов
/ 28 марта 2010

Если вы хотите проверить имя существующего хоста, лучше всего попытаться разрешить его. Вы никогда не напишите регулярное выражение для обеспечения этого уровня проверки.

0 голосов
/ 28 марта 2010

Обрабатывать каждую метку 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(".")))
...