Yealink T19 Auto-Provisioning + Dynamisches Adressbuch

Als ich zu dieser Firma kam, hatte ich bereits eine Basis für IP-Geräte, mehrere Server mit Sternchen und einen Spritzer in Form von FreeBPX. Darüber hinaus arbeitete die analoge Telefonvermittlung Samsung IDCS500 parallel und war im Allgemeinen das Hauptkommunikationssystem des Unternehmens. Die IP-Telefonie funktionierte nur für die Verkaufsabteilung. Und alles wäre so weitergegangen, aber eines schönen Tages wurde ein Dekret erlassen, um alle auf IP-Telefonie umzustellen, Termine wurden vereinbart, Ausrüstung wurde gekauft und ein Plan zur Übertragung des Unternehmens auf das 21. Jahrhundert wurde umgesetzt.
Das erste, was in dieser Situation zu stören beginnt, ist die schnell wachsende Anzahl von Telefonen, die irgendwie verwaltet werden müssen, und das zweite, was sehr besorgniserregend war, war das Telefonbuch. Wenn Endpoint Manager (der übrigens aus den neuesten Versionen von FreePBX herausgeschnitten wurde) uns bei der ersten helfen könnte, dann stellten sich mit dem Buch einige Fragen:

  • Erstens, wie kann die Genauigkeit bei einer ständigen Änderung der Versetzung / des Umsatzes der Benutzer sichergestellt werden?
  • Zweitens, wie man Telefone vollständig entpersönlicht. Und nicht jedes Mal den Kontaktnamen ausfüllen?

Die Aufgabe war interessant, die Lösung ließ nicht lange auf sich warten. Jetzt werde ich eine vollständige Auflistung geben, und dann werden wir in der Reihenfolge analysieren.

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') 

Das Programm wird auf dem Computer des Benutzers ausgeführt und funktioniert unter der Bedingung, dass der Computer über das Telefon mit dem Netzwerk verbunden ist, da der Yealink T19 nicht als Gateway fungieren kann.

Zuerst müssen wir verstehen, ob es verbunden ist? und welcher Mac und welche IP hat unser Handy.

 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 

Hier verwenden wir die Sniff-Funktion aus dem Scapy-Framework. Mit ihr erhalten wir ein vordefiniertes udp-Paket. Warten Sie 70 Sekunden und beenden Sie das Programm, wenn wir nichts abfangen.

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

Als nächstes stellen wir sicher, dass das Gerät wirklich Yealink ist und geben die erforderlichen Werte (IP und Mac) zurück.

Auf besonderen Wunsch ermitteln wir das Girokonto am Telefon. Dazu wird die aktuelle Konfiguration vom Telefon heruntergeladen und analysiert.

 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 

Wir finden das Passwort für dieses Konto heraus. Dazu wenden wir uns der Tabelle asterisk.sip und darin dem Datenfeld zu.

 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 

Nun, für die letzte Phase stellen wir eine Verbindung zu ldap AD her und verwenden sAMAccountName, das über die Funktion getpass.getuser () erhalten wurde , um den cn des aktuellen Benutzers zu übernehmen (der normalerweise den Namen des Benutzers enthält).

 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 

Wir stellen eine Verbindung zu der zuvor erstellten Tabelle in der Datenbank her (ich habe sie an derselben Stelle erstellt) und fügen alles hinzu, was wir gelernt haben, nämlich: IP, Mac, Benutzername.

 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() 

Wir könnten damit aufhören, weil wir bereits ein dynamisches Adressbuch erstellt haben, fragen Sie, aber ich bin noch weiter gegangen und habe die Autoprovisioning-Geräte hier verschraubt.

Dazu wird eine Vorlagenkonfiguration von einem vorkonfigurierten TFTP-Server heruntergeladen, auf dem wir unsere Änderungen vornehmen und mit als mac.cfg speichern. Das heißt, für Yealink gibt es zwei Arten von Konfigurationen, eine globale und eine zweite, die auf ein bestimmtes Telefon angewendet werden und die die Form mac_phone.cfg haben sollten

Nach all den Änderungen in der Datei und dem Speichern auf dem TFTP-Server geben wir dem Telefon den Befehl zum Bereitstellen und Neustarten des Geräts.

 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') 

Nach dem Neustart des Geräts erhalten wir einen vollständigen Namen auf dem Telefonbildschirm + immer korrekt ausgefülltes Adressbuch im Angesicht der Datenbank, dann müssen wir nur XML und ein wenig PHP für die dynamische Anzeige von Inhalten befestigen. Es gibt viele solcher Beispiele, sogar YEALINK selbst.

PS: Für eine bessere Skalierbarkeit können Sie die Haupteinstellungen (Variablen) in eine separate Datei verschieben.

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


All Articles