Balanceo de troncales en Elastix (round robin)

21 Jun

Este mini tutorial aplica para FreePBX/Trixbox/Elastix.

La idea tras de esta guía es crear un balanceador de carga. Es decir, tener una sola troncal que automáticamente rote una serie de troncales posibles por las cuales pueden salir las llamadas. Dichas troncales pueden ser DAHDI, IAX2 o SIP, así que esto le agrega flexibilidad.

El código sería algo así (la sintaxis está en AEL para hacer la programación más simple)

[codesyntax lang=»c» tab_width=»3″ blockstate=»expanded»]

// Archivo extensions.ael
context roundrobin {
	_X. => {
		Set(max=10);
		Set(n=0);
		repetir:
		Set(n=${n}+1);
		Set(last=$[(${DB(rr/last)}+1)%${max});
		Set(DB(rr/last)=${last});
		Dial(${DB(rr/trunk${last})}/${EXTEN},30,g);
		if (${DIALSTATUS}!="ANSWERED") {
			if (${n}<${max}) {
				// Repetir ciclo
				goto repetir;
			};
		};
		Hangup;
	};
};
[/codesyntax]

Nos faltan dos pasos:

  1. Inicializar el AstDB con el valor de la primer troncal. Esto es sencillo ejecutando el comando database put rr last 1 dentro del CLI de Asterisk
  2. El paso final es crear una troncal ‘Custom’ dentro de FreePBX/Elastix y agregarla como Local/{OUTNUM}@roundrobin/n. Esto hará uso del canal Local y nos permitirá balancear la carga entre nuestras troncales.

Todas las llamadas que vayan hacia esta troncal ‘Custom’ harán un balanceo de carga entre las lineas contratadas. Util si tienes varias lineas de diferentes proveedores y quieres que el consumo se haga equitativamente.

Suerte,

 

Una posible solución al «No service» de los teléfonos Aastra (y como mejorar el rendimiento de los reportes de llamadas)

17 Jun

Para los que nunca han hecho uso de, Aastra es una marca de telefonía con base en Ontario, Canadá. Últimamente, su crecimiento se ha dado fuertemente gracias a la prevalencia de sistemas como Elastix que se integran muy bien con sus teléfonos. Mi percepción personal de la marca desde el punto de vista del valor del producto es intermedio: no es una marca tan barata como Grandstream o Yealink, pero tampoco es una tan cara como Polycom o Cisco. Es una marca que está a muy buenos medios términos en cuanto a calidad y funcionalidades se refiere.

Un «inconveniente» que tienen sus teléfonos es que son extremadamente sensibles al retraso de paquetes cuando están en modo de stand by. Esto quiere decir que constantemente los teléfonos están enviando paquetes a Asterisk para medir el estado del servicio, y si el servidor por un momento se retrasa con la respuesta, inevitablemente veremos el mensaje de «No service» en la pantalla de los teléfonos, que es como si no estuviéramos registrados.

La solución no siempre está bien establecida porque tenemos que encontrar (y solucionar) lo que sea que esté causando este retraso. Puede ser un problema de la red (no es muy común, pero puede ser) o puede ser que el servidor de Asterisk se encuentre en un proceso que consuma muchos procesos/CPU y ocasione que se retrase para emitir una respuesta. En este caso, vamos a analizar una posible cause de este segundo escenario.

El cliente que hoy me ha reportado que sus teléfonos están perdiendo el registro frecuentemente es una agencia automotriz, que tiene alrededor de unos 140 teléfonos conectados entre Linksys 921 y diversos modelos de la serie Aastra 675x (aunque el problema solo es con los Aastra, por la razón arriba mencionada).

Indagando un poco más, observe que de pronto sus procesos de MySQL subían alredor del 80% del CPU por unos segundos y luego, bajaban a 0%. Esto quiere decir que algo se come el procesador por un instante y luego lo suelta, pudiendo ser la causa del problema que tenemos. Indagué un poco más y me doy cuenta que por default Elastix no utiliza índices en las tablas de CDR, lo cual hace que todos los reportes del detalle de llamadas tengan que «barrer» todos los registros de la tabla para encontrar los que necesitamos. Para un PBX pequeño no hay mucho problema, pero para una tabla que tiene 3.6 millones de registros, buscar en todos ellos resulta algo de uso intensivo de CPU. Para corroborar mi teoria, activé el log de consultas lentas de MySQL, y al hacer un mysqldumpslow desde el Linux CLI, obtuve lo siguiente (no se fijen en el query, fíjense en el tiempo total para ejecutarlo):

[codesyntax lang=»bash»]

[root@100 asterisk]# mysqldumpslow -t 5 /var/log/mysql/mysql-slow.log

Reading mysql slow query log from /var/log/mysql/mysql-slow.log
Count: 14  Time=40.50s (567s)  Lock=0.00s (0s)  Rows=58.6 (820), asteriskuser[asteriskuser]@[XXX.XXX.XXX.XXX]
  SELECT  * from (SELECT  billsec as billsec ,calldate as calldate,clid as clid,src as src,dst as dst,dcontext as dcontext,channel as channel,dstchannel as dstchannel,lastapp as lastapp,lastdataas lastdata,duration as duration,disposition as disposition,amaflags as amaflags,accountcode as accountcode,uniqueid as uniqueid,userfield as userfield, calldate + INTERVAL duration SECOND as fecha_termino from cdr where calldate >= 'S') as resultado where fecha_termino >='S'order by calldate limit N, N

[/codesyntax]

Como se observa, cada consulta toma 40 segundos. ¡Esto es un mar de tiempo solo para ver las llamadas que ha hecho una extensión el día de hoy! El problema se confirma con un EXPLAIN del mismo query:

[codesyntax lang=»sql»]

mysql> EXPLAIN SELECT  * FROM ( SELECT  billsec AS billsec ,calldate AS calldate,clid AS clid,src AS src,dst AS dst,dcontext AS dcontext,channel  AS channel,dstchannel AS dstchannel,lastapp AS lastapp,lastdata AS lastdata,duration AS duration, disposition AS disposition,amaflags AS amaflags,accountcode AS accountcode,uniqueid AS uniqueid,userfield AS userfield,  calldate + INTERVAL duration SECOND AS fecha_termino FROM cdr  WHERE calldate >= ‘2011-01-01 00:00:01’)  AS resultado  WHERE fecha_termino >=’2011/06/17 16:44:23 ‘ORDER BY calldate LIMIT 0, 10000;

+----+-------------+------------+------+---------------+------+---------+------+---------+-----------------------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows    | Extra
                   |
+----+-------------+------------+------+---------------+------+---------+------+---------+-----------------------------+
|  1 | PRIMARY     |            | ALL  | NULL          | NULL | NULL    | NULL |  592484 | Using where; Using filesort |
|  2 | DERIVED     | cdr        | ALL  | NULL          | NULL | NULL    | NULL | 3617505 | Using where                 |
+----+-------------+------------+------+---------------+------+---------+------+---------+-----------------------------+
2 rows in set (27.90 sec)

[/codesyntax]

Allí se nota como la consulta NO usa índices y tiene que recorrer los 3.6M de registros. ¿La solución? Agregar un índice al campo calldate que es donde se hacen las consultas de manera principal. Esto se hace con el siquiente comando en MySQL (ojo: dependiendo del tamaño de la tabla de CDR este proceso puede demorar desde segundos hasta horas, por lo que planea muy bien que tu equipo esté disponible para ejecutar esta tarea)

[codesyntax lang=»sql»]

ALTER TABLE `asteriskcdrdb`.`cdr` ADD INDEX `calldate` (`calldate`);

[/codesyntax]

Y al terminar el proceso, podemos validar ejecutando el mismo EXPLAIN:

[codesyntax lang=»sql»]

+----+-------------+------------+-------+---------------+----------+---------+------+--------+-----------------------------+
| id | select_type | table      | type  | possible_keys | key      | key_len | ref  | rows   | Extra|
+----+-------------+------------+-------+---------------+----------+---------+------+--------+-----------------------------+
|  1 | PRIMARY     |            | ALL   | NULL          | NULL     | NULL    | NULL | 592516 | Using where; Using filesort |
|  2 | DERIVED     | cdr        | range | calldate      | calldate | 8       | NULL | 652745 | Using where                 |
+----+-------------+------------+-------+---------------+----------+---------+------+--------+-----------------------------+
2 rows in set (8.84 sec)

[/codesyntax]

Como se observa, la consulta ahora está reducida a 650K registros o bien un 82% de ahorro en cuanto al número de registros que se tienen que consultar.

Aunque esta puede bien no ser la única cause del problema, es un excelente primer paso. Ya ahorramos procesamiento en este ejercicio, ahora hay que indagar en que otros procesos del sistema podemos ahorrarnos unos ciclos para así dejar más recursos disponibles para nuestras llamadas.

Suerte,

Recuperar la contraseña de Elastix/FreePBX

13 Jun

En ocasiones me he enfrentado a la tarea de apoderarme del control de algún servidor Elastix/FreePBX porque el cliente no ha quedado satisfecho con el servicio que su proveedor anterior le ofrecía, así que dado que el cliente no conoce los accesos y los que si lo conocen no me lo van a dar «por la buena», entonces tengo que recuperarlo yo.

Obtener las contraseñas de admin tanto para Elastix como para FreePBX no es complicado, lo único que se requiere es tener acceso de root por SSH (si no tenemos este acceso, podemos obtenerlo siguiendo una de tantas guías para recuperar el password de root que hay en internet). Ya adentro del sistema, podemos hacer lo siguiente desde el CLI de Linux:

echo "UPDATE acl_user SET md5_password='827ccb0eea8a706c4c34a16891f84e7b' WHERE name='admin';" | sqlite3 /var/www/db/acl.db

Con esto cambiamos el password de Elastix a admin/12345. Ahora, para FreePBX:

echo "UPDATE asterisk.ampusers SET password_sha1 = SHA1('12345') WHERE username = 'admin';" | mysql -peLaStIx.2oo7

(estoy asumiendo que tu versión de FreePBX ya ocupa passwords en SHA1 y que el password de root de MySQL sigue siendo el default)

Y con esto cambiamos también el password de FreePBX a admin / 12345. Está de más decir que la primera acción a ejecutar debe ser cambiar estas contraseñas por algo más seguro, pero para ello ya podemos hacerlo accediendo a las interfaces gráficas correspondientes.

Suerte a todos,

Ingeniería social como ataque en Elastix/FreePBX

13 Jun

Hace unos días tuve oportunidad de dar un Curso Asterisk en Guadalajara, y los asistentes preguntaron por la manera en que podríamos hacer una intervención de llamada, con la cual pudiera escucharse la conversación de alguien más sin que esa persona se percatara que la estamos espiando. Esto en Asterisk puede hacerse fácilmente con la aplicación ChanSpy, y en Elastix/FreePBX viene pre-programada para la extensión 555.

Esto significa que cualquiera que esté registrado en nuestro equipo puede marcar 555 y escuchar cualquier conversación que ocurra en ese momento. Pero… ¿y qué hay de aquellos que NO deberían tener acceso a nuestro sistema? ¿Acaso hay algún truco para engañar a los internos y que nos concedan acceso a esta funcionalidad? La respuesta, es SI.

Gracias a la pregunta de @solin182 durante el curso, se me ocurrió algo tan sencillo como esto:

  1. Llamemos al número de teléfono de la víctima (debemos saber que utiliza Elastix/FreePBX)
  2. Al contestarnos su menu de voz, debemos pedir hablar con la operadora
  3. Cuando estemos hablando con alguien, debemos hacerle una solicitud como «Disculpe, mi teléfono está fallando. ¿Sería tan amable de comunicarme con la extensión 555 por favor?»

Si quien nos contestó no tiene ni idea de lo que dicha extensión hace, lo más probable es que nos comunique inmediatamente y sin verificar que es lo que le contesta, dejándonos comunicados con un ChanSpy con acceso completo al sistema y que solo se terminará cuando nosotros colguemos.

¿Existe alguna solución para evitar este tipo de ataques? Claro: desactivemos la opción 555 y/o protejámosla con una contraseña. Esto no evitará que la gente lo intente, pero si que logren acceder.

Este tipo de ataques no es común todavía, pero hace unos años tampoco lo eran aquellos que se aprovechaban de la inseguridad en algunos conmutadores para hacer llamadas internacionales a costa nuestra. Es cuestión de tiempo antes de que empiece a pasar y tengan acceso a información que podría ser de carácter confidencial.

Suerte a todos y recuerden: protejan sus equipos.

Versión mejorada del mensaje «all circuits are busy» de Elastix (v3)

31 May

Este código se encuentra desactualizado. Hay una versión más reciente disponible en http://asteriskmx.com/2013/04/version-mejorada-del-mensaje-all-circuits-are-busy-de-elastix-v4/

En muchas ocasiones hemos oido el mensaje de «all circuits are busy» («todos los circuitos se encuentran ocupados» si es que tenemos los sonidos en Español) de Elastix. El problema con este mensaje es que es reproducido ante cualquier falla de nuestro enlace. Esto incluye a números que no existen, números desconectados y cualquier otra causa de error que no siempre es culpa de nuestro conmutador, pero que el usuario final siempre le achaca al sistema que le configuramos.

Con la intención de abrir un poco el panorama y dar más información sobre la causa del error, escribí este código para ayudar a distinguir cual es la verdadera causa del problema. Para ello, es necesario modificar 2 archivos, dentro de /etc/asterisk

// Archivo extensions.ael
macro outmessage(HC) {
        switch(${HC}) {
                case 1:         // Unnalocated or unnasigned number
                                // El numero que marco no existe
                        Set(MSG=no-existe);
                        break;
                case 17:        // User is busy
                                // Tono ocupado
                        Busy;
                        break;
                case 20:        // Subscriber absent
                                // El número celular que usted marcó no está disponible o se encuentra fuera del area
                        Set(MSG=numero-celular-no-disponible);
                        break;
                case 28:        // Address incomplete
                                // Numero mal marcado
                        Set(MSG=cannot-complete-as-dialed&check-number-dial-again);
                        break;
                case 27:        // Destination out of order
                case 38:        // Network out of order
                        // El telefono que marco esta fuera de servicio
                        // El telefono que marco esta descolgado o en reparacion
                        Set(MSG=fuera-servicio);
                        break;
                case 41:        // Temporary failure
                                // Falla temporal en la red
                        Set(MSG=falla-red);
                        break;
                case 42:        // Switching equipment congestion
                                // Congestion por alta cantidad de trafico en la central
                        Set(MSG=alto-trafico);
                        break;
                case 102:       // Recovery timer expired
                                // No se obtuvo una respuesta de la central telefónica
                        Set(MSG=no-respuesta);
                        break;
                case 34:        // Todos los circuitos están ocupados
                default:
                        Set(MSG=all-circuits-busy-now&pls-try-call-later);
        };
        Playback(${MSG},noanswer);
        &hangupcall();
};
; Archivo extensions_override_freepbx.conf
[macro-outisbusy]
exten => s,1,Progress
exten => s,n,Macro(outmessage,${HANGUPCAUSE})

Con estas modificaciones, necesitamos ejecutar dos comandos dentro del *CLI:

ael reload
extensions reload

Y por último, necesitamos extraer el contenido del siguiente archivo de sonidos dentro de la carpeta /var/lib/asterisk/sounds

Con esto, Elastix nos dará un poco más de información indicándonos los siguientes casos:

  1. El número no existe
  2. El número está fuera de servicio o en reparación

Para todos los demás problemas, se entregará el default de «todos los circuitos se encuentran ocupados»

Conforme consiga más grabaciones para diferentes casos, incluiré más códigos de error dentro de este código, a manera de poder diferenciar mejor cada error. Desde la siguiente liga pueden descargar los Sonidos para mejorar el mensaje de error de Elastix

La emoción de convertirse en dCAP

27 May

Habemos quienes nos dedicamos a Asterisk día y noche: es nuestro hobby, nuestro sustento, nuestra herramienta, en algunos casos hasta nuestra ideología. Quizá el hecho de tomar una certificación basada en lo que vemos va más allá del conocimiento y entra un poco en lo personal: creemos que eso nos define como profesionales, sentimos alguna decepción si acaso no la obtenemos. Ya no lo hacemos por decir que sabemos, lo hacemos por nosotros.

Hace poco más de 6 meses tomé la certificación como parte de sentir que tenía derechos para decir que sé (al final de cuentas, vivo de enseñar a otras personas y me sentiría muy mal conmigo mismo si no tuviera la garantía que no solo sé, sino que la máxima autoridad al respecto también opina lo mismo). En aquel día, con todo y 6 años dedicándome a Asterisk, llegué a presentar el examen con los típicos nervios:

¿Y si me preguntan justo lo que no estudié?
¿Y si todo este tiempo me enfoqué en las cosas equivocadas?
¿Y si creo que sé pero no sé?

3 horas después salí con un examen teórico contestado con buenas expectativas, y un examen práctico contestado hasta en los puntos extras (por si acaso). Tenía la incertidumbre de que yo creyera que me fue bien cuando no hubiera sido así. Afortunadamente, 1 semana después obtuve la buena noticia de que mi trámite de dCAP ya había comenzando y solo era cosa que me entregaran el certificado. Así me convertí en el dCAP #1917.

Hoy estoy del otro lado: soy el instructor del curso y estoy aplicando el examen a los asistentes. De hecho, estoy escribiendo este post mientras contestan el examen práctico. Sé lo que están sintiendo, sé lo que son los nervios de que el tiempo se acaba, quizá hasta el aroma de oficina que no es la tuya te intimida y cualquier cosa que sale mal te preocupa porque crees que no vas a acabar. Pero al final de cuentas, el conocimiento está dentro de uno, y una vez que logras hacer más allá la duda de si lo vas a lograr o no, es que te das cuenta de que realmente ya lo conseguiste.

Faltan unos minutos para que llegue el momento de evaluarlos. Mi deseo es que todos los que se han esforzado lo pasen, y aquellos que estén leyendo esto solo puedo decirles que pierdan el miedo. Nunca sabrán que tan fácil o complicado es hasta que no lo intentan.

Cursos Asterisk para mayo y junio en Guadalajara y México

20 Abr

Hay nuevas fechas para nuestros cursos, en esta ocasión en Guadalajara y Ciudad de México. Aquí las fechas y modalidades:

Curso básico
– México, DF. 18 y 19 de mayo
– Guadalajara, Jal. 8 y 9 de junio
– México, DF. 22 y 23 de junio

Curso completo (básico, intermedio e IVR) intensivo
– México, DF. 18, 19, 20 y 21 de mayo
– Guadalajara, Jal. 8, 9, 10 y 11 de junio
– México, DF. 22, 23, 24 y 25 de junio

Para más detalles puedes consultar nuestra página de información general de los cursos. Si tienes alguna duda por favor, utiliza nuestro formulario de contacto.

Curso Asterisk Advanced® oficial, del 23 al 27 de mayo (México, DF)

1 Abr

Después de estarme dedicando por poco más de 4 años a dar cursos de Asterisk, finalmente el año pasado tomé la certificación dCAP. Tras tomarla, entré a la lista de profesionales en la materia autorizados a impartir el curso oficial de Digium, el Asterisk Advanced, así que en mayo se abren las fechas para tomar el curso del 23 al 27 (lunes a viernes). Este curso representa el material oficial para aplicar para el dCAP, de manera que aquellos interesados en aprender las cosas tal cual Digium lo hace deberían tomar este curso.

El costo del curso Asterisk Advanced es de $3,000 USD + IVA por persona (hay un descuento si pagan en abril, quedando en $2,100 USD + IVA). A los interesados en más informes de este curso, les pido utilicen la siguiente forma de contacto.

Recuerden que también imparto cursos que aunque no son el Asterisk Advanced, pero cubren el mismo material. El costo de dichos cursos es de $9,300 pesos + IVA por persona (tomen en cuenta que mis cursos no incluyen todo el equipo VoIP que se da con el Advanced). Si quieres más información al respecto de mis cursos, por favor, consulta mi página de información general.

¡Espero verte por allá!

Estadísticas de inseguridad en Elastix (México)

9 Mar

Hoy decidí realizar un estudio sobre las vulnerabilidades que sería capaz de encontrar en un entorno como Elastix en México. Me puse mi sombrero blanco y decidí escanear todas las posibles redes del país y buscar cuantos equipos utilizan la distribución antes mencionada (al menos las visibles en el puerto TCP 443), y los resultados fueron alarmantes. Aquí algunas estadísticas:

25.4 millones de hosts escaneados
69 mil tienen el puerto 443 abierto
467 equipos eran Elastix

Aclaro que esto no quiere decir que estos sean todos los equipos Elastix en México. Esto quiere decir que en una noche cualquiera fui capaz de encontrar 467 equipos visibles abiertamente en internet, los demás están tras algún firewall y son invisibles al exterior (¡bien!).

Ahora, para los números del terror:

  • 287 tienen algún tipo de dato «secreto» que se puede obtener fácilmente (contraseña de FreePBX, contraseña de Elastix, contraseñas de extensiones)
  • los mismos 287 tienen el puerto SIP abierto
  • 261 tenían algún tipo de contraseña default para FreePBX
  • 26 aún tenían la contraseña de «palosanto» para Elastix
  • 31 usan la misma contraseña en FreePBX que en Elastix
  • 42 aún usan FreePBX 2.5.x que muestra la contraseña de administrador en texto plano

Y quizá la más sobresaliente:

  • 197 usan alguna distribución de Elastix suficientemente vieja que permite descargar el archivo de batch extensions, entregándote el archivo que nombra todas las extensiones del sistema con sus respectivas contraseñas, dando un total de ¡8,300 extensiones disponibles para hacer llamadas en Elastix ajenos!

Esto quiere decir que aún con una tasa de fallo del 50%, tengo 4,150 de posibles extensiones desde las cuales se pueden sacar llamadas sin pagar un solo centavo, ocasionando tráfico VoIP que en pocas horas se convierte en facturas millonarias de teléfono.

Y si creen que exagero, échenle cuentas:

  • Supongan que de los 197 servidores visibles, al menos 170 tienen algún tipo de interfaz FXO o E1
  • Supongan que en una sesión de ataque promedio, puedo cursar llamadas por el mismo equipo (por la noche) durante unas 4 horas
  • Supongamos que promediando la cantidad de canales de E1 y de puertos FXO, puedo cursar 3 llamadas simultáneas por servidor

Esto me da un total de 4 horas * 60 minutos * 3 llamadas simultáneas * 170 servidores = 122,400 minutos de tráfico. Si el destino costara $7/min, hablamos de un total facturado de $856,800, ¡en unas horas!

Y claro que este es el espectro conservador: conozco casos de 2a persona que han tenido facturaciones de $24,000 en una sola pasada. Hagan números, y fácilmente comprenderán por que esto del robo de VoIP está tan de moda y resulta tan redituable.

Obviamente si no quieren convertirse en amigos de lo ajeno, sigan las ya super conocidas medidas de seguridad para cualquier entorno Asterisk:

  1. Siempre cambien las contraseñas default
  2. Tampoco usen palabras de diccionario como contraseña
  3. No den acceso desde el exterior si no se necesita. Esto aplica a los puertos UDP 5060 y TCP 22, 443 (los más comunes)
  4. User el permit deny dentro de Asterisk para limitar que IPs se registran en cada extensión.
  5. No usen contraseñas y passwords iguales (user:100 pass:100)
  6. Actualicen sus distribuciones a la más reciente versión
  7. Limiten la cantidad de llamadas simultáneas por extensión (¿Por qué razón Juan Pérez haría 20 llamadas a la vez?)
  8. Rechazen malas autenticaciones sin dar más detalles (alwaysauthreject=yes) en sip.conf
  9. Firewall firewall firewall
  10. Usen fail2ban para defenderse de ataques de fuerza bruta
  11. Autentiquen al usuario, no solo al teléfono (usen contraseñas post-marcado)
  12. Nieguen llamadas anónimas provenientes del exterior
  13. Definan planes de marcación limitados (ojo con el dialplan injection)

Asterisk/Elastix como tal son bastante seguros: es el administrador el que los hace inseguros al obviar cualquier de estos pasos sugeridos. Todos los puntos que aquí se mencionan también se estudian desde nuestros cursos Asterisk

Suerte, y…  ¡aseguren sus equipos!

Problema con nuevos registros en el foro

9 Mar

Hoy hicieron de mi notar que había problemas para registrarse en los foros de discusión. En efecto, al intentar registrar una cuenta nueva el módulo que tenía de reCaptcha no me dejaba hacerlo, por más veces que intentara de poner las palabras correctas.

Así que con la intención de no lastimar al forro en la guerra con el SPAM, descargué e instalé un nuevo Captcha basado en jQuery. La idea es que al registrarse arrastren el ícono solicitado sobre el círculo de la derecha. Resulta muy sencillo, pero no para los bots 🙂

Si alguien ha tratado de registrarse en los foros últimamente por favor intente este nuevo método. Si hay algún problema avísenme en los comentarios por favor.