Expressão regular para corresponder ao nome do host ou endereço IP do DNS?

369

Alguém tem uma expressão regular acessível que corresponda a qualquer nome de host ou endereço IP legal do DNS?

É fácil escrever um que funcione 95% do tempo, mas espero conseguir algo que seja bem testado para corresponder exatamente às especificações RFC mais recentes para nomes de host DNS.

DonGar
fonte

Respostas:

535

Você pode usar as seguintes expressões regulares separadamente ou combinando-as em uma expressão OR conjunta.

ValidIpAddressRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$";

ValidHostnameRegex = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$";

ValidIpAddressRegex corresponde a endereços IP válidos e a nomes de host válidos ValidHostnameRegex . Dependendo do idioma que você usa, \ pode ter que ser escapado com \.


ValidHostnameRegex é válido conforme RFC 1123 . Originalmente, o RFC 952 especificava que os segmentos de nome de host não podiam iniciar com um dígito.

http://en.wikipedia.org/wiki/Hostname

A especificação original de nomes de host no RFC 952 exigia que os rótulos não pudessem começar com um dígito ou hífen e não deveriam terminar com um hífen. No entanto, uma especificação subsequente ( RFC 1123 ) permitiu que os rótulos de nome de host iniciassem com dígitos.

Valid952HostnameRegex = "^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$";
Jorge Ferreira
fonte
3
Aqui: stackoverflow.com/questions/4645126/… - Explico que os nomes que começam com um dígito também são considerados válidos. Além disso, apenas um ponto é questão questionável. Seria ótimo ter mais feedback sobre isso.
precisa saber é o seguinte
16
Você pode querer adicionar o IPv6. O OP não especificou que tipo de endereço. (By the way, ele pode ser encontrado aqui ) #
224456 new123456
32
Antes de as pessoas usarem cegamente isso em seu código, observe que não é completamente preciso. Ele ignora o RFC2181: "O próprio DNS coloca apenas uma restrição nos rótulos específicos que podem ser usados ​​para identificar registros de recursos. Essa restrição está relacionada ao comprimento do rótulo e ao nome completo. O comprimento de qualquer rótulo é limitado a entre 1 e 63 octetos. Um nome de domínio completo é limitado a 255 octetos (incluindo os separadores). "
rublo
7
@UserControl: nomes de host não latinos (codificados por punição) devem ser convertidos para o formato ASCII primeiro ( éxämplè.com= xn--xmpl-loa1ab.com) e depois validados.
Alix Axel
6
Sua expressão de nome de host está combinando com alguns valores inválidos: tentei 123.456.789.0e diz que é um nome de host válido.
Lbarreira 23/09
62

A regex do nome do host do smink não observa a limitação no comprimento dos rótulos individuais em um nome do host. Cada rótulo em um nome de host válido pode ter no máximo 63 octetos.

ValidHostnameRegex = "^ ([a-zA-Z0-9] | [a-zA-Z0-9] [a-zA-Z0-9 \ -] {0,61} [a-zA-Z0-9]) \
(\. ([a-zA-Z0-9] | [a-zA-Z0-9] [a-zA-Z0-9 \ -] {0,61} [a-zA-Z0-9])) * $ "

Observe que a barra invertida no final da primeira linha (acima) é a sintaxe do shell Unix para dividir a linha longa. Não faz parte da própria expressão regular.

Aqui está apenas a expressão regular sozinha em uma única linha:

^ ([a-zA-Z0-9] | [a-zA-Z0-9] [a-zA-Z0-9 \ -] {0,61} [a-zA-Z0-9]) (\. ([a-zA-Z0-9] | [a-zA-Z0-9] [a-zA-Z0-9 \ -] {0,61} [a-zA-Z0-9]) * *

Você também deve verificar separadamente se o comprimento total do nome do host não deve exceder 255 caracteres . Para mais informações, consulte o RFC-952 e o RFC-1123.

Sakari A. Maaranen
fonte
6
Excelente padrão de host. Provavelmente depende da implementação regex de sua língua, mas para JS pode ser ajustada ligeiramente para ser mais breve sem perder nada:/^[a-z\d]([a-z\d\-]{0,61}[a-z\d])?(\.[a-z\d]([a-z\d\-]{0,61}[a-z\d])?)*$/i
Semicolon
31

Para corresponder a um endereço IP válido, use o seguinte regex:

(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}

ao invés de:

([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\.([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])){3}

Explicação

Muitos mecanismos de expressão regular correspondem à primeira possibilidade na ORsequência. Por exemplo, tente o seguinte regex:

10.48.0.200

Teste

Teste a diferença entre bom e ruim

Alban
fonte
5
Não se esqueça de start ^ e end $ ou algo como 0.0.0.999 ou 999.0.0.0 também corresponderá. ;)
andreas 28/11
11
yes para validar uma string start ^ e end $ são obrigatórios, mas se você estiver pesquisando um IP em um texto, não o use.
Alban
A 'não ganância' não intencional que você identifica se aplica também às outras soluções de nomes de host. Vale a pena adicionar isso à sua resposta, pois os outros não corresponderão ao nome completo do host. por exemplo ([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*versus([a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]|[a-zA-Z0-9])(\.([a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])|[a-zA-Z0-9]))*
ergohack
EDIT: acima, use +no final em vez de *ver a falha.
Ergohack
5

Parece que não consigo editar a postagem principal, então adicionarei minha resposta aqui.

Para nome do host - resposta fácil, no exemplo egrep aqui - http: //www.linuxinsight.com/how_to_grep_for_ip_addresses_using_the_gnu_egrep_utility.html

egrep '([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}'

Embora o caso não represente valores como 0 no primeiro octeto e valores maiores que 254 (endereço IP) ou 255 (máscara de rede). Talvez uma declaração if adicional ajudaria.

Quanto ao nome de host DNS legal, desde que você verifique apenas nomes de host da Internet (e não intranet), escrevi o seguinte trecho, uma mistura de shell / php, mas deve ser aplicável como qualquer expressão regular.

primeiro acesse o site da ietf, faça o download e analise uma lista de nomes de domínio legais de nível 1:

tld=$(curl -s http://data.iana.org/TLD/tlds-alpha-by-domain.txt |  sed 1d  | cut -f1 -d'-' | tr '\n' '|' | sed 's/\(.*\)./\1/')
echo "($tld)"

Isso deve fornecer uma boa parte do código re que verifica a legalidade dos principais nomes de domínio, como .com .org ou .ca

Em seguida, adicione a primeira parte da expressão de acordo com as diretrizes encontradas aqui - http: //www.domainit.com/support/faq.mhtml?category=Domain_FAQ&question=9 (qualquer combinação alfanumérica e símbolo '-', o traço não deve estar o começo ou o fim de um octeto.

(([a-z0-9]+|([a-z0-9]+[-]+[a-z0-9]+))[.])+

Em seguida, junte tudo (exemplo PHP preg_match):

$pattern = '/^(([a-z0-9]+|([a-z0-9]+[-]+[a-z0-9]+))[.])+(AC|AD|AE|AERO|AF|AG|AI|AL|AM|AN|AO|AQ|AR|ARPA|AS|ASIA|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BIZ|BJ|BM|BN|BO|BR|BS|BT|BV|BW|BY|BZ|CA|CAT|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|COM|COOP|CR|CU|CV|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EDU|EE|EG|ER|ES|ET|EU|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GOV|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|INFO|INT|IO|IQ|IR|IS|IT|JE|JM|JO|JOBS|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MG|MH|MIL|MK|ML|MM|MN|MO|MOBI|MP|MQ|MR|MS|MT|MU|MUSEUM|MV|MW|MX|MY|MZ|NA|NAME|NC|NE|NET|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|ORG|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PRO|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|ST|SU|SV|SY|SZ|TC|TD|TEL|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TRAVEL|TT|TV|TW|TZ|UA|UG|UK|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|XN|XN|XN|XN|XN|XN|XN|XN|XN|XN|XN|YE|YT|YU|ZA|ZM|ZW)[.]?$/i';

    if (preg_match, $pattern, $matching_string){
    ... do stuff
    }

Você também pode adicionar uma instrução if para verificar se a string que você está verificando tem menos de 256 caracteres - http://www.ops.ietf.org/lists/namedroppers/namedroppers.2003/msg00964.html

Alex Volkov
fonte
11
-1 porque corresponde a endereços IP falsos, como "999.999.999.999".
bdesham
11
"Embora o caso não represente valores como 0 no primeiro octeto e valores maiores que 254 (endereço IP) ou 255 (máscara de rede)."
Alex Volkov
Vi que você qualificou sua resposta, sim. Fiz uma votação negativa porque essa parte da sua resposta ainda não é útil.
bdesham
3

Vale a pena notar que existem bibliotecas para a maioria dos idiomas que fazem isso por você, geralmente incorporadas à biblioteca padrão. E é provável que essas bibliotecas sejam atualizadas com muito mais frequência do que o código que você copiou de uma resposta do Stack Overflow há quatro anos e esqueceu. E é claro que eles também geralmente analisam o endereço de alguma forma utilizável, em vez de apenas fazer uma correspondência com vários grupos.

Por exemplo, detectando e analisando IPv4 em (POSIX) C:

#include <arpa/inet.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
  for (int i=1; i!=argc; ++i) {
    struct in_addr addr = {0};
    printf("%s: ", argv[i]);
    if (inet_pton(AF_INET, argv[i], &addr) != 1)
      printf("invalid\n");
    else
      printf("%u\n", addr.s_addr);
  }
  return 0;
}

Obviamente, essas funções não funcionarão se você estiver tentando, por exemplo, encontrar todos os endereços válidos em uma mensagem de bate-papo - mas mesmo assim, pode ser mais fácil usar um regex simples, mas excessivamente zeloso, para encontrar possíveis correspondências e usar o biblioteca para analisá-los.

Por exemplo, em Python:

>>> import ipaddress
>>> import re
>>> msg = "My address is 192.168.0.42; 192.168.0.420 is not an address"
>>> for maybeip in re.findall(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', msg):
...     try:
...         print(ipaddress.ip_address(maybeip))
...     except ValueError:
...         pass
abarnert
fonte
2
def isValidHostname(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("."))
PythonDev
fonte
Você poderia explicar esse regex? Exatamente, o que (?! -), (? <! -) significa?
SCIT
11
@Scit, eles garantem que ele não comece ou termine com um caractere "-" se o seu mecanismo de expressão regular permitir seu uso. Por exemplo, do Python ou do Perl .
YLearn 19/02/16
1

Eu acho que este é o melhor regex de validação de IP. por favor, verifique uma vez !!!

^(([01]?[0-9]?[0-9]|2([0-4][0-9]|5[0-5]))\.){3}([01]?[0-9]?[0-9]|2([0-4][0-9]|5[0-5]))$
Prakash Thapa
fonte
1
"^((\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])\.){3}(\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])$"
zangw
fonte
1

Isso funciona para endereços IP válidos:

regex = '^([0-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-5][0-5])[.]([0-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-5][0-5])[.]([0-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-5][0-5])[.]([0-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-5][0-5])$'
aliasav
fonte
1
/^(?:[a-zA-Z0-9]+|[a-zA-Z0-9][-a-zA-Z0-9]+[a-zA-Z0-9])(?:\.[a-zA-Z0-9]+|[a-zA-Z0-9][-a-zA-Z0-9]+[a-zA-Z0-9])?$/
user2240578
fonte
0

Aqui está um regex que eu usei no Ant para obter um IP ou nome de host do proxy fora de ANT_OPTS. Isso foi usado para obter o IP do proxy para que eu pudesse executar um teste Ant "isreachable" antes de configurar um proxy para uma JVM bifurcada.

^.*-Dhttp\.proxyHost=(\w{1,}\.\w{1,}\.\w{1,}\.*\w{0,})\s.*$
Bill Stephens
fonte
É isso \waí, ele não captura o IP, apenas o nome do host em determinadas situações.
Yaron
0

Achei que isso funciona muito bem para endereços IP. Ele valida como a resposta principal, mas também garante que o ip seja isolado, para que nenhum texto ou mais números / decimais sejam anteriores ou anteriores ao ip.

(? <! \ S) (?: (?: \ D | [1-9] \ d | 1 \ d \ d | 2 [0-4] \ d | 25 [0-5]) \ b |. \ b) {7} (?! \ S)

Andrew
fonte
Eu tentei muito, mas não conseguia entender duas coisas aqui. 1. \ b especifica o limite da palavra Por que estamos usando \ b? qual é o limite? e 2. Por que funciona apenas para {7} Pelo que entendi, acho que deveria ser {4}, mas não está funcionando. Opcionalmente, você pode dizer por que está usando blocos que não capturam.
Srichakradhar
0

tente isto:

((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)

funciona no meu caso.

serafim
fonte
0

Em relação aos endereços IP, parece que há algum debate sobre a inclusão de zeros à esquerda. Era uma vez a prática comum e geralmente é aceita, então eu argumentaria que elas deveriam ser sinalizadas como válidas, independentemente da preferência atual. Há também alguma ambiguidade sobre se o texto antes e depois da string deve ser validado e, novamente, acho que deveria. 1.2.3.4 é um IP válido, mas 1.2.3.4.5 não é e nem a parte 1.2.3.4 nem a parte 2.3.4.5 devem resultar em uma correspondência. Algumas das preocupações podem ser tratadas com esta expressão:

grep -E '(^|[^[:alnum:]+)(([0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])([^[:alnum:]]|$)' 

A parte lamentável aqui é o fato de que a parte regex que valida um octeto é repetida como é verdade em muitas soluções oferecidas. Embora isso seja melhor do que para instâncias do padrão, a repetição pode ser totalmente eliminada se as sub-rotinas forem suportadas no regex que está sendo usado. O próximo exemplo habilita essas funções com a -Popção de grepe também tira proveito da funcionalidade lookahead e lookbehind. (O nome da função que selecionei é 'o' para o octeto. Eu poderia ter usado 'octeto' como o nome, mas queria ser conciso.)

grep -P '(?<![\d\w\.])(?<o>([0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5]))(\.\g<o>){3}(?![\d\w\.])'

A manipulação do ponto pode, na verdade, criar um falso negativo se os endereços IP estiverem em um arquivo com texto na forma de frases, pois o período pode ser seguido sem fazer parte da notação pontilhada. Uma variante do acima consertaria isso:

grep -P '(?<![\d\w\.])(?<x>([0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5]))(\.\g<x>){3}(?!([\d\w]|\.\d))'
Thom Anderson
fonte
0
>>> my_hostname = "testhostn.ame"
>>> print bool(re.match("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", my_hostname))
True
>>> my_hostname = "testhostn....ame"
>>> print bool(re.match("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", my_hostname))
False
>>> my_hostname = "testhostn.A.ame"
>>> print bool(re.match("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", my_hostname))
True
Mohammad Shahid Siddiqui
fonte
0

A nova estrutura de rede possui inicializadores disponíveis para struct IPv4Address e struct IPv6Address, que lidam com a parte do endereço IP com muita facilidade. Fazer isso no IPv6 com uma regex é difícil com todas as regras de encurtamento.

Infelizmente não tenho uma resposta elegante para o nome do host.

Observe que a estrutura de rede é recente, portanto, pode forçar a compilação de versões recentes do sistema operacional.

import Network
let tests = ["192.168.4.4","fkjhwojfw","192.168.4.4.4","2620:3","2620::33"]

for test in tests {
    if let _ = IPv4Address(test) {
        debugPrint("\(test) is valid ipv4 address")
    } else if let _ = IPv6Address(test) {
        debugPrint("\(test) is valid ipv6 address")
    } else {
        debugPrint("\(test) is not a valid IP address")
    }
}

output:
"192.168.4.4 is valid ipv4 address"
"fkjhwojfw is not a valid IP address"
"192.168.4.4.4 is not a valid IP address"
"2620:3 is not a valid IP address"
"2620::33 is valid ipv6 address"
Raiz de Darrell
fonte
-1

que tal agora?

([0-9]{1,3}\.){3}[0-9]{1,3}
Saikrishna Rao
fonte
E o mesmo acontece com 9999999999.0.0.9999999999 :) Mas para a maioria dos programadores, essa abordagem curta será suficiente.
28513 Andreas
3
-1 porque corresponde a endereços IP sem sentido (como observa @Shebuka).
bdesham
-1

no php: filter_var(gethostbyname($dns), FILTER_VALIDATE_IP) == true ? 'ip' : 'not ip'

sirjay
fonte
2
Embora esse código possa responder à pergunta, geralmente a explicação ao lado do código torna a resposta muito mais útil. Por favor edite sua resposta e fornecer algum contexto e explicação.
precisa saber é o seguinte
E, a menos que eu esteja enganado, FILTER_VALIDATE_IP é um valor somente para PHP.
DonGar
-2

Verificando nomes de host como ... mywebsite.co.in, thangaraj.name, 18thangaraj.in, thangaraj106.in etc.,

[a-z\d+].*?\\.\w{2,4}$
Thangaraj
fonte
3
-1. O OP pediu algo "bem testado para corresponder exatamente às especificações RFC mais recentes", mas isso não corresponde, por exemplo, * .museum, enquanto corresponderá a * .foo. Aqui está uma lista de TLDs válidos.
bdesham
Não sei se é uma boa ideia colocar o sinal de mais dentro da classe de caracteres (colchetes), além disso, existem TLDs com 5 letras ( .expert, por exemplo).
Yaron
A melhor maneira de realizar com o RFC é usar as funções de sistema / idioma. inet_atoné bom o suficiente.
M3nda
-2

Pensei nesse padrão simples de correspondência de expressões regulares para correspondência de endereços IP \ d + [.] \ D + [.] \ D + [.] \ D +

Dody
fonte
1111.1.1.1 não é um ip válido. Não há como realmente testar um formato IP se você não se importar com sub-redes. Você deve pelo menos tomar cuidado com o número de aparências com algo parecido ^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}e, claro, essa não será a maneira correta. Se você possui um idioma para escrever um script, com certeza terá acesso às funções de rede. A melhor maneira de verificar um IP REAL é dizer ao sistema para converter e fazer o ip no formato correto e, em seguida, verifique se é verdadeiro / falso. No caso de Python eu uso socket.inet_aton(ip). Caso de PHP vc precise inet_aton($ip).
M3nda
Os usuários do Python podem dar uma olhada aqui: gist.github.com/erm3nda/f25439bba66931d3ca9699b2816e796c
m3nda