viernes, 27 de marzo de 2009

Validar una dirección IP privada en Java

Resulta que, para mi proyecto terminal me vi en la necesidad de verificar que una dirección IP sea una dirección local válida, como todo buen "huevón" me puse a googlear a ver si había algo hecho por ahí, escudándome en aquella muy frecuente (güacala) frase de "reinventar la rueda" no quería hacerlo por mi mismo (aparte de que tenía mucha flojera). A fin de cuentas, no encontré nada decente digno de agregarse a mi ya muy ofuscado código y me decidí a hacerlo, después de todo sólo necesitaba hacer uso de expresiones regulares, un lápiz y un papel.

Bueno, depués de este choro que seguro nadie leerá y no se porque escribí, muestro como lo hice:

Basados en el RFC 1918, una dirección IP privada puede pertenecer a cualquiera de los siguientes tres rangos de direcciones:


10.0.0.0 - 10.255.255.255
172.16.0.0 - 172.31.255.255
192.168.0.0 - 192.168.255.255
o bien:

172.16/12
192.168/16
10/8
A primera vista pareciera que es no es tan complicado hacerlo, pero el problema no es tan trivial (tampoco es difícil ¬¬); por ejemplo: ¿Cómo saber si un número está entre 0 y 255?. La respuesta puede ser algo similar a lo siguiente:

Números de un dígito:
  • Se permiten números en el rango [0-9].
Número de dos dígitos:
  • No pueden comenzar con 0, esto porque no se permiten números como 01, 02, 001, 010, etc.
  • Se permite cualquier número que comience con un dígito en el rango [1-9] y siga de un número en el rango [0-9].
Números de tres dígitos:
  • Si comienza con 1 puede seguir de 2 dígitos en el rango [0-9], es decir, se permiten números como 100, 101, 150, 199, etc.
  • Si comienza con 2:
    • Si después hay un número en el rango [0-4], enseguida puede haber un número en el rango [0-9], es decir, se permiten números en el rango [200-249].
    • Si después hay un 5, sólo se permiten números en el rango [0-6], es decir, los entre [250-255].
El texto de arriba "mapeado" a expresiones regulares puede quedar más o menos así:
Números de un dígito:

[0-9]
Números de dos dígitos:

[1-9][0-9]
Números de tres dígitos:

1[0-9][0-9]
2([0-4][0-9]|5[0-5])
entonces, la expresion regular para validar un número entero en el rango [0-255] es la union de los tres conjuntos de números anteriores:

[0-9] | [1-9][0-9] | 1[0-9][0-9] | 2([0-4][0-9] | 5[0-5])
podemos factorizar la expresion anterior, nótese que:

[0-9] | [1-9][0-9]
es lo mismo que:

[1-9]?[0-9]
? es un operador que indica que puede haber uno o ningún dígito en el rango [1-9]
factorizamos:

[0-9] | [1-9][0-9] | 1[0-9][0-9] | 2([0-4][0-9] | 5[0-5])
[1-9]?[0-9] | 1[0-9][0-9] | 2([0-4][0-9] | 5[0-5])
[1-9]?[0-9] | 1[0-9]{2} | 2([0-4][0-9] | 5[0-5])
para evitar cualquier confusión, separamos cada término usando paréntesis:

([1-9]?[0-9]) | (1[0-9]{2}) | (2([0-4][0-9] | 5[0-5]))
ahora bien, en java la siguiente expresión valida expresiones en el rango [.0-.255] (se agrega el punto antes de los dígitos):

"\\.(([1-9]?[0-9])|(1[0-9]{2})|(2([0-4][0-9]|5[0-5])))"

siguiendo los mismos conceptos, el siguiente método escrito en Java valida si una dirección IP es una dirección privada válida:

/**
* Verifica que la dirección IP es una dirección privada válida.
*
* @param ip Cadena de texto con la dirección que se quiere validar.
* @return true si es una dirección válida, false en otro caso.
*/
private bool verificaHost(String ip)
{
/* patrón para los números de .0 a .255 */
String n = "\\.(([1-9]?[0-9])|(1[0-9]{2})|(2([0-4][0-9]|5[0-5])))";

/* patrón para los números de .16 a .31 */
String m = "\\.((1[6-9])|(2[0-9])|(3[01]))";

/* 192.168.0.0 a 192.168.255.255 */
if(ip.matches("^192\\.168(" + n + "){2}$"))
return true;

/* 10.0.0.0 a 10.255.255.255 */
else if(ip.matches("^10(" + n + "){3}$"))
return true;

/* 172.16.0.0 a 172.31.255.255 */
else if(ip.matches("^172" + m + "(" + n + "){2}$"))
return true;

/* la dirección no coincidió con ninguna de las anteriores */
return false;
}

6 comentarios:

Memo dijo...

te odio cagado :D
eres un chingón

Anónimo dijo...

Gracias por tu aporte

Anónimo dijo...

Excelente aporte muy bueno

Unknown dijo...

No podía ser mejor! Es el primer post realmente útil :D Buena suerte!

inesar dijo...

Muy útil!!! Gracias!!

Anónimo dijo...

No está mal pero mi inexperiencia me dice que eso es una flipada...jeje

para los novatos como yo esto quizá se entienda mejor:
básicamente miro el número de dígitos de cada número de la ip
ej.(10.223.45.123), después de hacer el split cada número irá en un registro de aux y ahí con que se comprenda en los números deseados y no empiece por 0(lo miro con el substring) creo que ya es suficiente.

Aquí mi código:

String[] aux =FTP_HOST.split(Pattern.quote(".")); for(int i=0;aux.length>i;i++){
if(aux[i].length()==0){
logger.error("The data entered in FTP HOST is incorrect");
return null;
}else if(aux[i].length()==1){
if(Integer.valueOf(aux[i])<0 || Integer.valueOf(aux[0])>9){
logger.error("The data entered in FTP HOST is incorrect");
return null;
}
}else if(aux[i].length()==2){
if(Integer.valueOf(aux[i])<0 || Integer.valueOf(aux[i])>99 || Integer.valueOf(aux[i].substring(0,1))==0){
logger.error("The data entered in FTP HOST is incorrect");
return null;
}
}else if(aux[i].length()==3){
if(Integer.valueOf(aux[i])<0 || Integer.valueOf(aux[i])>255 || Integer.valueOf(aux[i].substring(0,1))==0){
logger.error("The data entered in FTP HOST is incorrect");
return null;
}
}
}