Dns sd en ubuntu y dnsjava

De ChuWiki

Me he estado entreteniendo en probar la librería dnsjava para probar a consultar y añadir servicios en un servidor dns-sd. Así que me ha tocado instalar y configurar uno de estos en linux (ubuntu) y luego jugar con java.

¿Qué es DNS SD?[editar]

Un servidor DNS es al que se le pregunta por el nombre de algún servidor (por ejemplo, chuidiang.com) y te devuelve la IP y más información adicional sobre el servidor. Si la añadimos la parte SD (Service Discovery), también se le puede preguntar por qué servidores ofrencen un determinado servicio. Por ejemplo, podemos preguntar por "_http._tcp.chuidiang.com" para obtener una lista de "servicios" http en el dominio chuidiang.com y saber el servidor concreto, puerto e incluso path de la URL con la que conectarnos.

Instalación de DNS SD para test[editar]

Para las pruebas de dnsjava, además de consultar cosas, queremos añadir servicios. Obviamente, no tenemos permisos para añadir entradas en un servidor DNS de internet, así que nos montamos uno local para poder jugar con él. En este caso, con ubuntu 18.04. Si sólo quisieramos hacer consultas, no hace falta esta instalación, podríamos usar directamente los DNS configurados en nuestro PC

El servidor DNS se llama bind9, así que lo instalamos con sudo apt install bind9.

La configuración de este servicio se situa más o menos automáticamente en /etc/bind

Para consulta todo correcto de momento, pero hay una cosa que tocar y una pega para añadir servicios.

La cosa a tocar es el fichero /etc/bind/named.conf.default-zones. Las zonas (dominios controlados por esta instalación) no son modificables. En este fichero están los de internet y además esta localhost. Podemos aprovechar este para hacerlo editable. Para ello basta añadir la línea allow-update

zone "localhost" {
	type master;
	file "/etc/bind/db.local";
        allow-update { any; };
};

También, en el fichero /etc/bind/db.local debemos añadir este par de líneas al final para habilitar la parte SD (service discovery) de DNS

b._dns-sd._udp          PTR @
lb._dns-sd._udp         PTR @

Y la pega, que en realidad es doble pega :

  • los ficheros en /etc/bind y el directorio mismo deben ser del usuario y grupo bind, por lo que en ese directorio debemos ejecutar sudo chown bind:bind . para el directorio y sudo chown bind:bind * para todos los ficheros.
  • La segunda pega, en mi caso, es que hay un servicio llamado apparmor que vigila dónde puede o no escribir los demás servicios, incluido bind. Y fíjate qué cosas, no le deja escribir en /etc/bind. Así que editamos el fichero de configuración correspondiente sudo gvim /etc/apparmor.d/usr.sbin.named y buscamos la línea
/etc/bind/** r,

para cmabiarla por

/etc/bind/** rw,

Por supuesto, hay que reiniciar ambos servicios después de estos cambios

sudo /etc/init.d/apparmor restart
sudo /etc/init.d/bind9 restart

Con esto ya no deberíamos tener problemas, solo empezar a jugar con java

Añadimos un servicio[editar]

En nuestro proyecto necesitamos antes que nada poner la dependencia de dnsjava. En gradle sería algo como

compile group: 'dnsjava', name: 'dnsjava', version: '2.1.8'

El código java para añadir un servicio nuevo puede ser como este

// El Resolver para conectarse al DNS nuestro, en localhost.
// Si no ponemos parámetro, se conecta al DNS por defecto del sistema     
SimpleResolver resolver = new SimpleResolver("127.0.0.1");
resolver.setTimeout(5);

// Para update hay que hacerlo por TCP. Las consultas no lo necesitan.
resolver.setTCP(true);

// En la zona "localhost" (que es en la que tenemos permiso) añadimos una entrada tipo PTR.
// La zona no tiene nada que ver con dónde corre el ejecutable que ofrece el servicio ni su
// IP/puerto de conexión.
// La entrada PTR dice para un sevicio concreto, qué servidor/es lo ofrece/n. 
// En este caso, hacemos una entrada diciendo que el servidor Dummy._test._tcp
// ofrece un servicio de  _test._tcp.
// 3600L (una hora) es el tiempo que esta información se puede guardar en caché.
Name zone = Name.fromString("localhost.");
Update update = new Update(zone);
update.replace(new Name("_test._tcp", zone), Type.PTR, 3600L, "Dummy._test._tcp");
Message response = resolver.send(update);
System.out.println("Response: " + response);

// Ahora añadimos los datos de como conectar a ese servicio Dummy.
// Para ello se hace una entrada SRV, en la que se pone el nombre concreto del
// servicio (Dummy._test._tcp), la zona en la que está, que es de tipo SRV,
// el tiempo en caché y la paramétrica de conexión al servicio
// 0 0 son la preferencia y peso del servicio. Si el mismo servicio concreto puede elegirse
// en varios servidores, se debe elegir el que tenga la preferencia más baja y a igualdad
// de preferencia, el de peso más alto.
// 80 es el puerto al que hay que conectarse
// localhost. es el servidor donde está el servicio. No tiene que ser la zona y debe acabar en punto.
// En este caso estamos diciendo que el servicio Dummy._test._tcp está en localhost:80
update.replace(new Name("Dummy._test._tcp",zone),Type.SRV, 3600L, "0 0 80 localhost.");
response = resolver.send(update);
System.out.println("Response: " + response);

// Quitamos lo de TCP para poder hacer consultas.
resolver.setTCP(false);

Para consultar todos los servicios/entradas que hay en el DNS, tenemos el siguiente código.

// Zona que nos interesa, localhost, en servidor en DNS de 127.0.0.1. TSIG es un certificado para permisos,
// como no hemos configurado nada de certificados, no es necesario.
ZoneTransferIn xfr = ZoneTransferIn.newAXFR(new Name("localhost"), "127.0.0.1", (TSIG)null);
xfr.setTimeout(30);

// Obtenemos todos los registros
List records = xfr.run();
Iterator it = records.iterator();

// Y los mostramos
while(it.hasNext()) {
   System.out.println(it.next());
}

Salen todos los registros en la zona localhost. La salida tiene varias cosas, pero aparece el servicio que acabamos de meter

_test._tcp.localhost.	3600	IN	PTR	Dummy._test._tcp.localhost.
Dummy._test._tcp.localhost.	3600	IN	SRV	0 0 80 localhost.