Fechas, malditas fechas

Este flash viene con dedicatoria a mi socio Inge, el cual vive traumatizado con el manejo de las fechas.

Desde que la computación trascendió la barrera del idioma inglés han surgido nuevos retos para los que hacemos programas multilingües.  El fenómeno se conoce como internacionalización y localización (abreviado i18n).  Uno de sus aspectos es el tratamiento de las fechas de acuerdo a su representación en diferentes regiones; he visto programas colapsar dramáticamente debido a la asunción de un formato específico de fecha.  Aunque los motores actuales de bases de datos también cuentan con herramientas para i18n, es práctica común almacenar los datos en un formato específico y centrar los esfuerzos en la interfaz de usuario.  Pero ello requiere el dominio de las herramientas que proporcionan los lenguajes de programación para la conversión entre los formatos de datos en su recorrido bidireccional entre el punto de almacenamiento (memoria, base de datos, archivo de texto, etc) <-> procesamiento <-> interfaz de usuario.

Siguiendo con las fechas, C# cuenta con una función muy útil para convertir cadenas de caracteres que contienen fechas en un formato predeterminado a variables DateTime de .NET:  DateTime.ParseExact().  El gran problema con las fechas consiste en su variedad de representaciones con la consiguiente ambiguedad, ej.

  • Mes – Dia – Año
  • Mes / Día / Año
  • Dia / Mes / Año
  • Febrero 14, 2011
  • 3 / 4 / 2011  puede ser 3 de abril ó 4 de marzo en dependencia de si usamos formato español o norteamericano.
  • etc

Pues bien, ParseExact( ) permite que le indiquemos exactamente cual es el formato esperado, imagine Ud los problemas que pudiera traer confundir el mes con el día !!!  Su documentación completa se puede encontrar en http://msdn.microsoft.com/es-es/library/w2sa9yss.aspx.  A modo de resumen, acepta como parámetros una cadena que debe contener una fecha, una segunda cadena que especifica en qué formato esta esa fecha, y un objeto IFormatProvider.  Vista hace fe, veamos el siguiente ejemplo:

Obtenemos de una base de datos una cadena que contiene una fecha (fecha_original) en formato estándar Mes/Dia/Año y debemos calcular una fecha de expiración de 5 meses después y devolver el resultado nuevamente como cadena:

CultureInfo provider = CultureInfo.InvariantCulture;
DateTime fecha_original_datetime = DateTime.ParseExact(fecha_original, "M/d/yyyy", provider);
string fecha_expiracion = DateTime.Now.AddMonths(5).ToString("M/d/yyyy");

Note que la conversión inversa se realiza con la función ToString( ) y un parámetro que también especifica el formato deseado.  Los formatos pueden ser predeterminados de .NET (http://msdn.microsoft.com/es-es/library/az4se3k1.aspx) o definidos por nosotros según las reglas en http://msdn.microsoft.com/es-es/library/8kb3ddd4.aspx .  Ojo pues como su nombre lo indica, los formatos deben ser EXACTOS, no es lo mismo dia 01 que día 1.

Fácil cierto?  Feliz programación con fechas 🙂

Anuncios

OpenVPN y el codec g729

Para los que trabajamos con telefonía IP es bien conocido que el codec g729 es uno de los más populares debido principalmente a que comprime el audio a 8 kbps lo cual permite un uso eficiente del ancho de banda disponible.  Aunque existen otros que realizan la misma función y al mismo tiempo son gratis (ej. Speex), con mucha frecuencia la única opción es g729 ya que es el más soportado por los proveedores y es el que viene incluído en la gran mayoría de los equipos de hardware usados en voz sobre IP.

Pues bien, hace unos días le compré a Digium una licencia de g729 para Asterisk, la cual brinda un máximo de dos registros posibles.  La instalación fluyó perfectamente, hice las pruebas pertinentes y efectivamente pude usar el codec satisfactoriamente.  Luego dejé a un lado el asterisk y le apliqué una actualización al sistema CentOS y más adelante comencé a hacer pruebas de enrutamiento con OpenVPN instalado en el mismo servidor, lo cual condujo a varios reinicios del servicio OpenVPN y del propio servidor.  Finalmente, todas las rutas funcionaban como se esperaba, pero … las llamadas ya no funcionaban.

Al revisar las trazas noté un par de aspectos relevantes: primero, la interfaz de red eth0, aparecia renombrada como eth1; segundo, la traza de las llamadas mostraba error de codec y el comando “g729 show licenses” en el CLI de Asterisk informaba que no encontró licencias válidas en el sistema ya que la existente tenía “incorrect host-id”.  Me las agencié para resolver el primer problema de lograr que volviera a nombrar la interfaz como eth0, pero aún así continuó el error.  Luego de contactar al soporte técnico de Digium, me informaron que el host-id de la licencia se calcula basado en:

  • Las direcciones MAC y los nombres de todas las interfaces de red presentes en el sistema (incluídas las virtuales de OpenVPN).

Como ya tenía las interfaces con sus nombres originales, entonces solo quedaba por revisar las MAC, la interfaz física no podía cambiar la suya, asi que solo quedaba la tap0 correspondiente a la VPN.  Luego de repetir un par de veces “service openvpn restart”  e  “ifconfig”  me di cuenta que openvpn asigna una nueva dirección MAC aleatoria a la interfaz virtual cada vez que se levanta el servicio, así que la solución consistía en lograr que siempre asigne la misma MAC.

Revisando un poco la documentación encontré que existe una forma de lograr esto y consiste en incluir en server.conf, client.conf o como se haya nombrado el archivo de configuración la siguiente directiva:

lladdr 56:37:67:A0:47:BC

donde el parámetro es por supuesto la MAC que se desee asignar.  Lo mejor es copiarlo de la última configuración válida generada disponible a través de “ifconfig” (hwaddr).

Aquí se los dejo y creo que pudiera aplicarse en otros casos más generales de servicios dependientes de la dirección MAC donde al mismo tiempo esté involucrada OpenVPN. Importante: según la documentación, este parámetro solo funciona en Linux, pero supongo que en Windows existan otros métodos de fijar la MAC de una interfaz virtual.

OpenVPN en Centos

Durante los últimos dos meses me he encontrado repentinamente al menos en cuatro ocasiones ante la necesidad de instalar OpenVPN en Centos. Siempre ha sido un requerimiento de alguna tarea más grande o más “importante”, algo como para no perder el tiempo en ello. Sin embargo, el repositorio oficial de Centos no incluye el paquete de OpenVPN, lo cual nos obliga a ejecutar una serie de pasos adicionales. Aqui les dejo una guia de estos pasos que se les ahorrará tiempo a muchos y a mi mismo como referencia futura pues según las probabilidades pronto me tocará otra instalación de OpenVPN en Centos.

– Agregar estas líneas a /etc/yum.conf  para utilizar el repositorio Dag

[dag]
name=Repositorio Dag para Red Hat Enterprise Linux, Centos, Fedora
baseurl=http://apt.sw.be/redhat/el$releasever/en/$basearch/dag
gpgcheck=1
enabled=1

– Instalar las llaves GPG para el repositorio Dag, de otra forma mostrará un error cuando se intente instalar un paquete.

cd /var/tmp
wget [archivo-correspondiente-a-mi-instalacion]
rpm -Uhv [archivo-correspondiente-a-mi-instalacion]
cat /etc/redhat-release

En mi caso particular, el último de los sistemas instalados fue CentOS release 5.5 (Final), luego el comando quedó asi:

cd /var/tmp
wget http://packages.sw.be/rpmforge-release/rpmforge-release-0.5.2-2.el5.rf.i386.rpm
rpm -Uhv rpmforge-release-0.5.2-2.el5.rf.i386.rpm

Instalar las bibliotecas requeridas para OpenVPN:

yum install lzo lzo-devel zlib zlib-devel

– Y al final instalar OpenVPN:

yum install openvpn

Copiar para /etc/openvpn los archivos de configuración de muestra:

cp -r /usr/share/doc/openvpn-2.1.4/easy-rsa/ /etc/openvpn/
cp /usr/share/doc/openvpn-2.1.4/sample-config-files/server.conf /etc/openvpn/
cp -r /usr/share/doc/openvpn-2.1.4/easy-rsa/2.0/openssl /etc/openvpn

– Configurar como servidor o cliente: el programa es el mismo para ambas, la función es determinada por el contenido del archivo de configuración. Al final de los pasos anteriores OpenVPN se instaló como servicio, o sea, tiene ya un script en /etc/init.d  y en Centos se puede manipular asi:

service openvpn [start | stop | restart ]

Para arrancar el servicio cuando reinicie el servidor:

chkconfig openvpn on

El servicio lee cualquier archivo en /etc/openvpn con extensión .cnf e inicia el servicio basado en este.

– El próximo paso es crear los certificados y las llaves (en caso de ser servidor) y los archivos de configuración, lo cual no repetiré aquí pues está muy bien documentado en el sitio de openvpn .  Garantizar primero permiso de ejecución a los scripts en el directorio easy-rsa:

cd /etc/openvpn/easy-rsa/2.0
chmod +x clean-all
chmod +x build*
chmod +x vars
chmod +x whichopensslcnf
chmod +x pkitool

 

Configurar el servidor como un router a través de la VPN

– Primero activar el reenvio de paquetes: editar /etc/sysctl.conf y adicionar la linea:

net.ipv4.ip_forward = 1

El siguiente comando lo activa de forma inmediata pero no sobrevive a un reinicio:

 echo 1 > /proc/sys/net/ipv4/ip_forward
 

– Activar las reglas de NAT de iptables.  El siguiente ejemplo debe ajustarlo a su configuración específica, aqui se asume el siguiente escenario:

Subred OpenVPN:  10.8.1.1/24
IP local del servidor: 192.168.0.2
Subred local (LAN):  192.168.0.0/24
Gateway de la LAN: 192.168.0.1
DNS: 192.168.0.1

Desde el shell ejecutar:

iptables -t nat -A POSTROUTING -s 10.8.1.0/24 -j SNAT --to 192.168.0.2
iptables -A FORWARD -d 10.8.1.0/24 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -s 10.8.1.0/24 -j ACCEPT

Guardar las reglas:

service iptables save

y asegurar que el servicio iptables se arranque automático al iniciar el servidor:

chkconfig iptables on

Listo!