Yealink T19 Autoaprovisionamiento + Libreta de direcciones dinámica

Cuando llegué a trabajar en esta empresa, ya tenía una base en dispositivos IP, varios servidores con asterisco y un chapoteo en forma de FreeBPX. Además, la central telefónica analógica Samsung IDCS500 funcionaba en paralelo y, en general, era el principal sistema de comunicación de la empresa, la telefonía IP solo funcionaba para el departamento de ventas. Y todo habría seguido hirviendo, pero un buen día se dictó el decreto para transferir a todos a la telefonía IP, se acordaron las fechas, se compró el equipo y comenzó a implementarse un plan para transferir la empresa al siglo XXI.
Lo primero que comienza a molestar en esta situación es el número cada vez mayor de teléfonos que necesitan ser manejados de alguna manera, y lo segundo que fue muy preocupante fue la guía telefónica. Si Endpoint Manager (que, por cierto, se eliminó de las últimas versiones de FreePBX) podría ayudarnos con la primera, entonces surgieron algunas preguntas con el libro:

  • En primer lugar, ¿cómo garantizar su precisión con un cambio constante de la dislocación / rotación de los usuarios?
  • En segundo lugar, cómo despersonalizar completamente los teléfonos. ¿Y no completa el nombre del contacto cada vez?

La tarea fue interesante, la solución no se hizo esperar. Ahora daré una lista completa, y luego analizaremos en orden.

from scapy.all import sniff from scapy.layers.inet import IP import mysql.connector import ldap import getpass import tftpy import requests import os import time from string import replace def conn_ldap(login): ad = ldap.initialize('ldap://***.local') ad.simple_bind_s('voip@***.local', 'password') basedn = 'OU=IT,DC=***,DC=LOCAL' basedn_user = 'OU=***,OU=***,DC=***,DC=LOCAL' scope = ldap.SCOPE_SUBTREE filterexp = "(&(sAMAccountName=" + login + ")(ObjectClass=person))" filterexp2 = "(&(ObjectClass=organizationUnit))" attrlist = ['cn'] attrlist2 = ['OU'] search = ad.search_s(basedn, scope, filterexp, attrlist) adname = search[0][1]['cn'][0].decode('utf-8') if adname == ' ': search = ad.search_s(basedn_user, scope, filterexp2, attrlist2) for i in range(1, len(search)+1): group = search[i][1]['ou'][0] basedn_user2 = 'OU='+group+','+basedn_user search = ad.search_s(basedn_user2, scope, filterexp, attrlist) adname = search[0][1]['cn'][0].decode('utf-8') if adname != ' ': return adname adname = search[0][1]['cn'][0].decode('utf-8') ad.unbind_s() return adname def tftp_file_change(config,place,adname,current_account,current_account_password): client = tftpy.TftpClient("192.168.0.3", 69) client.download('template.cfg', place) fileread = open(place, 'r') line = fileread.readlines() fileread.close() line[5] = (('account.1.label = ').encode('utf-8') + adname.encode('utf-8') + '\n') line[2] = (('account.1.auth_name = ').encode('utf-8') + current_account.encode('utf-8') + '\n') line[3] = (('account.1.display_name = ').encode('utf-8') + current_account.encode('utf-8') + '\n') line[6] = (('account.1.password = ').encode('utf-8') + current_account_password[0][0] + '\n') filewrite = open(place, 'w') for i in line: filewrite.write(i) filewrite.close() print place print config client.upload(config,place) def get_phone_inform(ipaddr): fileconf = requests.get('http://admin:admin@'+ipaddr+'/servlet?phonecfg=get[&accounts=1]') conf = fileconf.text.split('|') current_account = conf[2] return current_account def sniff_frame(): pcapf = sniff(count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060") if len(pcapf) == 0: exit() frame = pcapf[0] macaddr = frame.src print macaddr[:8] if macaddr[:8] != '80:5e:c0': exit() ipaddr = frame[0][IP].src return macaddr, ipaddr def conn_mysql(query,fquery,macaddr,qwery2): connect = mysql.connector.connect(host='192.168.0.3', database='voip', user='voip_wr', password='***') cursor = connect.cursor() cursor.execute(fquery) state = cursor.fetchall() state = bool(state[0][0]) if state == True: cursor.execute(qwery2) connect.commit() connect.close() else: cursor.execute(query) connect.commit() connect.close() def check_account(current_account): connect = mysql.connector.connect(host='192.168.0.3', database='asterisk', user='voip_wr', password='***') cursor = connect.cursor() qwery = 'select data from sip where id=' + current_account + ' and keyword="secret";' cursor.execute(qwery) password = cursor.fetchall() if password == ' ': exit() else: return password if __name__ == '__main__': macaddr, ipaddr = sniff_frame() current_account = get_phone_inform(ipaddr) current_account_password = check_account(current_account) macaddr = macaddr.replace(':', '') ipaddr = ipaddr.decode('utf-8') adname = conn_ldap(getpass.getuser()) query = 'INSERT INTO station (mac, ip, name, number) VALUES (' + '"' + macaddr + '",' + '"' + ipaddr + '",' + '"' + adname + '",' + '"' + get_phone_inform(ipaddr) + '"' + ')' qwery2 = 'UPDATE station SET ip=' + '"' + ipaddr + '"' + ', name=' + '"' + adname + '"' + ', number=' + '"' + get_phone_inform(ipaddr) + '"' + ' WHERE mac=' + '"' + macaddr + '"' fquery = 'SELECT EXISTS(SELECT mac FROM voip.station WHERE mac=' + '"' + macaddr + '")' query = query.encode('utf-8') fquery = fquery.encode('utf-8') config = macaddr + '.cfg' place = os.path.expanduser("~") + "\\" + "AppData\\Local\\" + config conn_mysql(query,fquery,macaddr,qwery2) tftp_file_change(config,place,adname,current_account,current_account_password) requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=AutoP') requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=Reboot') 

El programa se ejecuta en la computadora del usuario y funciona bajo la condición de que la computadora esté conectada a la red a través del teléfono, ya que el Yealink T19 no puede funcionar como una puerta de enlace.

Primero tenemos que entender si está conectado? y qué mac e ip tiene nuestro teléfono.

 def sniff_frame(): pcapf = sniff(count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060") if len(pcapf) == 0: exit() frame = pcapf[0] macaddr = frame.src print macaddr[:8] if macaddr[:8] != '80:5e:c0': exit() ipaddr = frame[0][IP].src return macaddr, ipaddr 

Aquí usamos la función sniff del marco de trabajo scapy, usándola obtenemos un paquete udp predefinido, esperamos 70 segundos y si no atrapamos nada, salimos.

 count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060" 

A continuación, nos aseguramos de que el dispositivo sea realmente Yealink y devolvemos los valores necesarios (ip y mac).

Mediante una solicitud especial, descubrimos la cuenta corriente en el teléfono. Para hacer esto, la configuración actual se descarga desde el teléfono y se analiza.

 def get_phone_inform(ipaddr): fileconf = requests.get('http://admin:admin@'+ipaddr+'/servlet?phonecfg=get[&accounts=1]') conf = fileconf.text.split('|') current_account = conf[2] return current_account 

Descubrimos la contraseña de esta cuenta. Para hacer esto, pasamos a la tabla asterisk.sip y en ella al campo de datos.

 def check_account(current_account): connect = mysql.connector.connect(host='192.168.0.3', database='asterisk', user='voip_wr', password='***') cursor = connect.cursor() qwery = 'select data from sip where id=' + current_account + ' and keyword="secret";' cursor.execute(qwery) password = cursor.fetchall() if password == ' ': exit() else: return password 

Bueno, para la etapa final, nos conectamos a ldap AD y usamos sAMAccountName obtenido a través de la función getpass.getuser () para tomar el cn del usuario actual (que generalmente contiene el nombre del usuario).

 def conn_ldap(login): ad = ldap.initialize('ldap://***.local') ad.simple_bind_s('voip@***.local', 'password') basedn = 'OU=***,DC=***,DC=LOCAL' basedn_user = 'OU=***,OU=***,DC=***,DC=LOCAL' scope = ldap.SCOPE_SUBTREE filterexp = "(&(sAMAccountName=" + login + ")(ObjectClass=person))" filterexp2 = "(&(ObjectClass=organizationUnit))" attrlist = ['cn'] attrlist2 = ['OU'] search = ad.search_s(basedn, scope, filterexp, attrlist) adname = search[0][1]['cn'][0].decode('utf-8') if adname == ' ': search = ad.search_s(basedn_user, scope, filterexp2, attrlist2) for i in range(1, len(search)+1): group = search[i][1]['ou'][0] basedn_user2 = 'OU='+group+','+basedn_user search = ad.search_s(basedn_user2, scope, filterexp, attrlist) adname = search[0][1]['cn'][0].decode('utf-8') if adname != ' ': return adname adname = search[0][1]['cn'][0].decode('utf-8') ad.unbind_s() return adname 

Nos conectamos a la tabla creada previamente en la base de datos (la creé en el mismo lugar) y agregamos todo lo que aprendimos, a saber: ip, mac, nombre de usuario.

 def conn_mysql(query,fquery,macaddr,qwery2): connect = mysql.connector.connect(host='192.168.0.3', database='voip', user='voip_wr', password='***') cursor = connect.cursor() cursor.execute(fquery) state = cursor.fetchall() state = bool(state[0][0]) if state == True: cursor.execute(qwery2) connect.commit() connect.close() else: cursor.execute(query) connect.commit() connect.close() 

Podríamos detenernos en esto, porque ya hemos creado una libreta de direcciones dinámica, pregunta, pero fui más allá y atornillé los dispositivos de autoprovisión aquí.

Para hacer esto, se descarga una configuración de plantilla desde un servidor tftp preconfigurado, en el que hacemos nuestros cambios y los guardamos con mac.cfg. Es decir, para Yealink hay dos tipos de configuración, una global y la segunda aplicada a un teléfono específico y debe tener la forma mac_phone.cfg

Después de todos los cambios en el archivo y guardarlo nuevamente en el servidor tftp, le damos el comando al teléfono para aprovisionar y reiniciar el dispositivo.

 def tftp_file_change(config,place,adname,current_account,current_account_password): client = tftpy.TftpClient("192.168.0.3", 69) client.download('template.cfg', place) fileread = open(place, 'r') line = fileread.readlines() fileread.close() line[5] = (('account.1.label = ').encode('utf-8') + adname.encode('utf-8') + '\n') line[2] = (('account.1.auth_name = ').encode('utf-8') + current_account.encode('utf-8') + '\n') line[3] = (('account.1.display_name = ').encode('utf-8') + current_account.encode('utf-8') + '\n') line[6] = (('account.1.password = ').encode('utf-8') + current_account_password[0][0] + '\n') filewrite = open(place, 'w') for i in line: filewrite.write(i) filewrite.close() print place print config client.upload(config,place) 

 requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=AutoP') requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=Reboot') 

Después de reiniciar el dispositivo, obtenemos un nombre completo en la pantalla del teléfono + siempre rellenamos correctamente la libreta de direcciones frente a la base de datos, luego solo necesitamos atornillar XML y un poco de PHP para mostrar el contenido dinámicamente. Hay muchos ejemplos de este tipo, incluso el propio YEALINK tiene.

PD: para una mayor escalabilidad, puede mover la configuración principal (variables) a un archivo separado.

Source: https://habr.com/ru/post/465779/


All Articles