Automaatne varustamine Yealink T19 + dünaamiline aadressiraamat

Kui ma sellesse ettevõttesse tööle tulin, oli mul juba IP-seadmete andmebaas, mitu tärniga serverit ja plaaster FreeBPX kujul. Lisaks töötas paralleelselt analoog-PBX Samsung IDCS500, mis oli üldiselt ettevõtte peamine sidesüsteem, IP-telefon töötas ainult müügiosakonnas. Ja kõik oleks niimoodi edasi küpsetatud, kuid ühel ilusal päeval anti käsk kõik IP-telefonile üle viia, lepiti kokku tähtajad, osteti seadmed ja hakati ellu viima plaani viia ettevõte 21. sajandisse.
Esimese asjana hakkab sellises olukorras muret tegema kiiresti kasvav telefoniaparaatide hulk, mida tuleb kuidagi majandada, teiseks tegi väga murelikuks telefoniraamat. Kui Endpoint Manager sai meid aidata esimesega (mis muide oli FreePBX-i viimastest versioonidest välja lõigatud), siis raamatuga tekkisid mõned küsimused:

  • Esiteks, kuidas tagada selle täpsus, kui kasutajate asukoht/voolavus pidevalt muutub?
  • Teiseks, kuidas telefone täielikult depersonaliseerida. Ja mitte iga kord kontakti nime täita?

Probleem oli huvitav, lahendus ei võtnud kaua aega. Nüüd annan täieliku nimekirja ja siis vaatame seda järjekorras.

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

Programm töötab kasutaja arvutis ja töötab eeldusel, et arvuti on telefoni kaudu võrku ühendatud, kuna Yealink T19 ei saa lüüsina töötada.

Esiteks peame mõistma, kas see on ühendatud? ja mis mac ja ip meie telefonil on.

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

Siin kasutame scapy raamistiku funktsiooni sniff, mille abil saame etteantud udp paketi, ootame 70 sekundit ja kui midagi ei taba, siis väljume.

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

Järgmisena veendume, et seade on tõepoolest Yealink, ja tagastame vajalikud väärtused (ip ja mac).

Erisoovi kasutades selgitame välja arvelduskonto telefonis. Selleks laaditakse praegune konfiguratsioon telefonist alla ja sõelutakse.

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

Uurige selle konto parooli. Selleks pöördume tabeli asterisk.sip ja selles oleva andmevälja poole.

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

Noh, viimases etapis loome ühenduse ldap AD-ga ja kasutades funktsiooni kaudu saadud sAMAccountName getpass.getuser() võtke praeguse kasutaja cn (mis sisaldab tavaliselt kasutaja täisnime).

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

Ühendame andmebaasis eelnevalt loodud tabeliga (mina lõin selle seal) ja sisestame kõik, mida õppisime, nimelt: ip, mac, kasutajanimi.

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

Võiksime siinkohal peatuda, kuna oleme juba loonud dünaamilise aadressiraamatu, võite küsida, kuid ma läksin kaugemale ja lisasin siia seadmete automaatse varustamise.

Selleks laaditakse eelkonfigureeritud tftp serverist alla malli konfiguratsioon, millesse teeme oma muudatused ja salvestame selle mac.cfg nime all. See tähendab, et Yealinki jaoks on kahte tüüpi konfiguratsioone, üks on globaalne ja teine ​​kehtib konkreetse telefoni kohta ja peaks olema kujul mac_phone.cfg

Pärast kõiki failis tehtud muudatusi ja selle tftp-serverisse tagasi salvestamist anname telefonile käsu seadme varundamiseks ja taaskäivitamiseks.

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

Peale seadme taaskäivitamist saame telefoni ekraanile oma täisnime + alati korrektselt täidetud aadressiraamatu andmebaasi kujul, siis jääb üle vaid lisada XML ja natuke PHP-d, et sisu dünaamiliselt kuvada. Selliseid näiteid on palju, isegi YEALINKil endal on need olemas.

PS: Suurema skaleeritavuse huvides saate põhiseaded (muutujad) teisaldada eraldi faili.

Allikas: www.habr.com

Lisa kommentaar