Auto foarsjenning Yealink T19 + dynamysk adresboek

Doe't ik foar dit bedriuw kaam te wurkjen, hie ik al wat database fan IP-apparaten, ferskate servers mei asterisk en in patch yn 'e foarm fan FreeBPX. Derneist wurke in analoge PBX Samsung IDCS500 parallel en wie yn 't algemien it haadkommunikaasjesysteem yn it bedriuw; IP-telefony wurke allinich foar de ferkeapôfdieling. En alles soe sa trochgean mei koken, mar op in moaie dei waard in dekreet jûn om elkenien oer te setten nei IP-telefony, deadlines waarden ôfpraat, apparatuer waard kocht, en it plan om it bedriuw yn 'e 21e ieu oer te setten begon te wurde útfierd.
It earste ding dat yn sa'n situaasje begjint te soargen is it hurd groeiend oantal tillefoans dy't op ien of oare manier beheard wurde moatte, it twadde ding dat tige soargen wie wie it tillefoanboek. As Endpoint Manager ús koe helpe mei de earste (dy't trouwens út 'e lêste ferzjes fan FreePBX wie ôfsnien), dan ûntstiene guon fragen mei it boek:

  • As earste, hoe de krektens te garandearjen as de lokaasje / fluiditeit fan brûkers konstant feroaret?
  • Twadder, hoe tillefoans folslein depersonalisearje. En net elke kear de kontaktnamme ynfolje?

It probleem wie nijsgjirrich, de oplossing duorre net lang om te kommen. No sil ik de folsleine list jaan, en dan sille wy it yn oarder besjen.

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("~") + "" + "AppDataLocal" + 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')

It programma rint op de brûker syn kompjûter en wurket op betingst dat de kompjûter is ferbûn mei it netwurk fia in telefoan, sûnt Yealink T19 kin net wurkje as in poarte.

Earst moatte wy begripe oft it ferbûn is? en wat mac en ip ús telefoan hat.

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

Hjirmei brûke wy de snifffunksje fan it skapy-ramt, mei har help krije wy in foarbeskaaide udp-pakket, wachtsje 70 sekonden en as wy neat fange, geane wy ​​út.

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

Dêrnei soargje wy derfoar dat it apparaat yndie Yealink is en jouwe de nedige wearden werom (ip en mac).

Mei help fan in spesjaal fersyk fine wy ​​it hjoeddeistige akkount op 'e tillefoan. Om dit te dwaan, wurdt de hjoeddeistige konfiguraasje downloade fan 'e tillefoan en parseard.

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

Sykje it wachtwurd foar dit akkount. Om dit te dwaan, wikselje wy nei de tabel asterisk.sip en it gegevensfjild dêryn.

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

No, foar de lêste etappe ferbine wy ​​mei ldap AD en brûke sAMAccountName krigen fia de funksje getpass.getuser() nim de cn fan 'e hjoeddeistige brûker (dy't normaal de folsleine namme fan 'e brûker befettet).

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

Wy ferbine mei in foarôf oanmakke tabel yn 'e databank (ik makke it dêr) en fier alles yn wat wy leard hawwe, nammentlik: ip, mac, brûkersnamme.

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

Wy koenen hjir stopje, om't wy al in dynamysk adresboek hawwe makke, kinne jo freegje, mar ik gie fierder en tafoege hjir auto-provisioning fan apparaten.

Om dit te dwaan, wurdt in sjabloankonfiguraasje downloade fan in foarôf ynstelde tftp-tsjinner, wêryn wy ús wizigingen meitsje en it bewarje as mac.cfg. Dat is, foar Yealink binne d'r twa soarten konfiguraasje, ien is globaal, en de twadde jildt foar in spesifike tillefoan en moat fan 'e foarm wêze mac_phone.cfg

Nei alle wizigingen yn it bestân en it bewarjen fan it werom nei de tftp-tsjinner, jouwe wy it kommando oan 'e tillefoan om it apparaat te leverjen en opnij te starten.

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

Nei it opnij opstarten fan it apparaat krije wy ús folsleine namme op it tillefoanskerm + in altyd korrekt ynfolle adresboek yn 'e foarm fan in databank, dan bliuwt it allinich om XML en in bytsje PHP ta te foegjen om de ynhâld dynamysk wer te jaan. D'r binne in protte fan sokke foarbylden, sels YEALINK sels hat se.

PS: Foar gruttere skalberens kinne jo de haadynstellingen (fariabelen) ferpleatse nei in apart bestân.

Boarne: www.habr.com

Add a comment