martes, 27 de febrero de 2007

Un servidor dns en ruby (43 lineas)

El código que sigue representa un servidor dns mínimo, resuelve direcciones locales del dominio elegido y tiene otro servidor (DNS) configurado para resolver las direcciones que no corresponden al dominio local, lo he probado en windows con la versión 1.8.5 [i386-mswin32], si funciona en windows, difícil es que no funcione en otros sistemas operativos...
Una vez que mandamos en la resolución, podríamos resolver distintas ips dependiendo del cliente que nos hace la petición, la hora del día ó implementar un Round robin...
No obstante, esto es solo una maqueta, la motivación para crearlo ha sido solo la de jugar...

require 'socket'
DNS_REENVIO="10.0.0.115"
MIS_IPS = {"\003www\011ficciones\002es" => [10,0,0,1],
"\002mx\011ficciones\002es" => [10,0,0,2],
"\006correo\011ficciones\002es" => [10,0,0,3]}
#en octal número de caracteres que siguen:
#'ficciones'.length =9, en octal => 11
def resolver(peticion)
peticion[12,peticion.length-12] =~ /([^\0]+)\0/
consulta=$1
resolv=''
if MIS_IPS.keys.include?(consulta)#resolución en local
puts "\tresolución en local"
ip=MIS_IPS[consulta]
resolv=peticion
resolv[2] = 133.chr
resolv[3] = 128.chr
resolv[7] = 1.chr
resolv +="\xC0\x0C\000\001\000\001\000\000" +
"\007\010\000\004"
resolv +=ip[0].chr + ip[1].chr +
ip[2].chr + ip[3].chr
else #resultado de consulta a servidor dns de reenvio
puts "\tresolución a traves de dns"
re=UDPSocket.open
re.connect(DNS_REENVIO, 53)
re.send(peticion,0)
resp=re.recvfrom(256)
resolv = resp[0]
end
resolv
end
##
server = UDPSocket.open
server.bind('0.0.0.0', 53)
puts 'servidor iniciado'
while true
kk=server.recvfrom(256)
pet,info = kk
puts "peticion: " + pet.inspect
puts "info cliente: " + info.inspect
puerto_cliente=info[1];ip_cliente=info[3]
resolucion=resolver(pet)
server.send(resolucion,0,ip_cliente.to_s,
puerto_cliente.to_i)
end

Un servidor dns escucha en el puerto UDP 53,
MIS_IPS = {"\003www\011ficciones\002es" => [10,0,0,1],
"\002mx\011ficciones\002es" => [10,0,0,2],
"\006correo\011ficciones\002es" => [10,0,0,3]}

representa la resolución para el dominio local y el formato, aunque raro, es conveniente por la codificación de los mensajes en el protocolo DNS:
3 caracteres (\003 en octal):www
9 caracteres (\011 en octal):ficciones
2 caracteres (\002):es ...
Creo que el código se explica solo por lo demás, la resolución local es un poco liosa, pero así son los formatos de mensaje.
(rfc-1035, ver también http://www.freesoft.org/CIE/RFC/1035/38.htm)
Para probarlo:
1) Poner DNS_REENVIO apuntando a tu servidor dns.
2a) Cambiar servidor dns en la configuración de tú máquina y hacer ping a www.ficciones.es, www.google.es ...
2b)si no quieres cambiar el dns de tu máquina; ejecutar nslookup,
server computername, > www.google.es, > www.ficciones.es...