Fornitura automatica Yealink T19 + libru d'indirizzu dinamicu

Quandu aghju vinutu à travaglià per questa cumpagnia, aghju digià avutu qualchì basa di dati di i dispositi IP, parechji servitori cù asteriscu è un patch in forma di FreeBPX. Inoltre, un PBX analogicu Samsung IDCS500 hà travagliatu in parallelu è, in generale, era u sistema di cumunicazione principale in a cumpagnia; a telefonia IP hà travagliatu solu per u dipartimentu di vendita. È tuttu avaria cuntinutu à cocilu cusì, ma un bellu ghjornu hè statu datu un decretu per trasfirià tutti à a telefonia IP, i termini sò stati accunsentiti, l'equipaggiu hè acquistatu, è u pianu di trasferimentu di l'impresa in u 21u seculu cuminciò à esse implementatu.
A prima cosa chì cumencia à preoccupà in una tale situazione hè u numeru rapidamente crescente di telefuni chì deve esse amministratu in qualchì modu, a seconda cosa chì era assai preoccupante era u libru di telefunu. Se Endpoint Manager puderia aiutà à noi cù u primu (chì, per via, hè statu tagliatu da l'ultime versioni di FreePBX), allora alcune dumande sò state cù u libru:

  • Prima, cumu per assicurà a so precisione quandu u locu / fluidità di l'utilizatori hè in constantemente cambiante?
  • Siconda, cumu per depersonalize completamente i telefoni. È ùn compie micca u nome di u cuntattu ogni volta?

U prublema era interessante, a suluzione ùn hà micca pigliatu assai per ghjunghje. Avà daraghju u listinu sanu, è dopu avemu da fighjà in ordine.

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

U prugramma scorri nantu à l'urdinatore di l'utilizatore è funziona sempre chì l'urdinatore hè cunnessu à a reta per un telefunu, postu chì Yealink T19 ùn pò micca funzionà cum'è gateway.

Prima, avemu bisognu di capisce s'ellu hè cunnessu? è ciò chì mac è ip u nostru telefunu hà.

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

Quì avemu aduprà a funzione sniff da u framework scapy, cù u so aiutu ricevemu un pacchettu udp predeterminatu, aspittà 70 seconde è s'ellu ùn catturamu nunda, esce.

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

In seguitu, assicuremu chì u dispusitivu hè veramente Yealink è rinviate i valori necessarii (ip è mac).

Utilizendu una dumanda speciale, truvamu u contu currente in u telefunu. Per fà questu, a cunfigurazione attuale hè scaricata da u telefunu è analizata.

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

Truvate a password per stu contu. Per fà questu, vultemu à a tavola asterisk.sip è u campu di dati in questu.

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

Ebbè, per a tappa finale cunnettamu à ldap AD è usendu sAMAccountName ottenutu attraversu a funzione getpass.getuser() piglià u cn di l'utilizatore attuale (chì di solitu cuntene u nome cumpletu di l'utilizatore).

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

Cunnettamu à una tavola pre-creata in a basa di dati (l'aghju creatu quì) è entre in tuttu ciò chì avemu amparatu, vale à dì: ip, mac, username.

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

Pudemu piantà quì, perchè avemu digià creatu un libru d'indirizzu dinamicu, pudete dumandà, ma aghju andatu più in là è aghju aghjustatu l'auto-provisioning di i dispositi quì.

Per fà questu, una cunfigurazione di mudellu hè scaricata da un servitore tftp pre-configuratu, in quale facemu i nostri cambiamenti è salvemu cum'è mac.cfg. Vale à dì, per Yealink ci sò dui tipi di cunfigurazione, unu hè globale, è u sicondu hè appiicatu à un telefunu specificu è deve esse di a forma mac_phone.cfg.

Dopu à tutti i cambiamenti in u schedariu è salvatu daretu à u servore tftp, avemu da u cumandamentu à u telefonu à pruvista è reboot u dispusitivu.

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

Dopu avè riavviatu u dispusitivu, avemu u nostru nome cumpletu nantu à a pantalla di u telefunu + un libru d'indirizzu sempre cumpletu in forma di basa di dati, allora tuttu ciò chì resta hè di aghjunghje XML è un pocu PHP per vede dinamicamente u cuntenutu. Ci sò assai di tali esempii, ancu YEALINK stessu li hà.

PS: Per una scalabilità più grande, pudete spustà i paràmetri principali (variabili) in un schedariu separatu.

Source: www.habr.com

Add a comment