web-dev-qa-db-fra.com

Valider une chaîne de nom d'hôte

Suivi de Expression régulière pour faire correspondre le nom d’hôte ou l’adresse IP? et en utilisant Restrictions sur les noms d'hôtes valides comme référence, quel est le moyen le plus lisible et le plus concis de faire correspondre/valider un nom d'hôte/fqdn (nom de domaine complet) en Python? J'ai répondu avec ma tentative ci-dessous, les améliorations sont les bienvenues.

22
kostmo
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("."))

assure que chaque segment

  • contient au moins un caractère et un maximum de 63 caractères
  • se compose uniquement de caractères autorisés
  • ne commence ni ne finit par un trait d'union.

Cela évite également les doubles négatifs (not disallowed), et si hostname se termine par un ., ce n'est pas grave. Il échouera (et devrait échouer) si hostname se termine par plus d'un point.

43
Tim Pietzcker

Voici une version un peu plus stricte de La réponse de Tim Pietzcker avec les améliorations suivantes:

  • Limitez la longueur du nom d'hôte à 253 caractères (après avoir supprimé le point final facultatif).
  • Limitez le jeu de caractères à ASCII (c'est-à-dire utilisez [0-9] au lieu de \d).
  • Vérifiez que le TLD n'est pas entièrement numérique.
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)
4
Eugene Yarmash

Per The Old New Thing , la longueur maximale d’un nom DNS est de 253 caractères. (Un seul est autorisé jusqu'à 255 octets, mais 2 d'entre eux sont consommés par le codage.)

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('.'))

On pourrait argumenter en faveur de l'acceptation de noms de domaine vides ou non, en fonction du but recherché.

3
solidsnack

Complément à la réponse @TimPietzcker. Le trait de soulignement est un nom d’hôte valide, le double tiret est commun pour IDN punycode. Le numéro de port doit être supprimé. C'est le nettoyage du code.

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 )
1
mootmoot

J'aime la minutie de la réponse de Tim Pietzcker, mais je préfère décharger une partie de la logique des expressions régulières pour des raisons de lisibilité. Honnêtement, je devais rechercher la signification de ces parties (? "extension notation". De plus, j’estime que l’approche «double négatif» est plus évidente en ce sens qu’elle limite la responsabilité de l’expression régulière à la simple recherche de tout caractère non valide. J'aime bien que re.IGNORECASE permette de raccourcir la regex.

Alors, voici un autre coup; c'est plus long mais ça se lit un peu comme de la prose. Je suppose que "lisible" est un peu en contradiction avec "concis". Je crois que toutes les contraintes de validation mentionnées dans le fil jusqu'à présent sont couvertes:


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("."))
1
kostmo

Cette expression rationnelle pure doit respecter tous les paramètres: ^(?=.{1,253}\.?$)(?!-)[A-Za-z0-9\-]{1,63}(\.[A-Za-z0-9\-]{1,63})*\.?(?<!-)$

0
Steve Goossens
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
imbolc