Auto provisioning Yealink T19 + dynamic na address book

Nang magtrabaho ako para sa kumpanyang ito, mayroon na akong ilang database ng mga IP device, ilang server na may asterisk at isang patch sa anyo ng FreeBPX. Bilang karagdagan, ang isang analogue na PBX Samsung IDCS500 ay nagtrabaho nang magkatulad at, sa pangkalahatan, ang pangunahing sistema ng komunikasyon sa kumpanya; Ang IP telephony ay nagtrabaho lamang para sa departamento ng pagbebenta. At ang lahat ay magpapatuloy sa pagluluto tulad nito, ngunit isang magandang araw ay ibinigay ang isang utos upang ilipat ang lahat sa IP telephony, napagkasunduan ang mga deadline, binili ang kagamitan, at ang plano na ilipat ang negosyo sa ika-21 siglo ay nagsimulang ipatupad.
Ang unang bagay na nagsisimulang mag-alala sa ganoong sitwasyon ay ang mabilis na lumalagong bilang ng mga set ng telepono na kailangang pangasiwaan kahit papaano, ang pangalawang bagay na lubhang nakakabahala ay ang phone book. Kung matutulungan kami ng Endpoint Manager sa una (na, sa pamamagitan ng paraan, ay tinanggal mula sa pinakabagong mga bersyon ng FreePBX), pagkatapos ay may ilang tanong na lumitaw sa aklat:

  • Una, paano masisiguro ang katumpakan nito kapag ang lokasyon/fluidity ng mga user ay patuloy na nagbabago?
  • Pangalawa, kung paano ganap na i-depersonalize ang mga telepono. At hindi punan ang pangalan ng contact sa bawat oras?

Ang problema ay kawili-wili, ang solusyon ay hindi nagtagal bago dumating. Ngayon ay ibibigay ko ang buong listahan, at pagkatapos ay titingnan natin ito sa pagkakasunud-sunod.

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

Gumagana ang program sa computer ng user at gumagana sa kondisyon na nakakonekta ang computer sa network sa pamamagitan ng telepono, dahil hindi maaaring gumana ang Yealink T19 bilang gateway.

Una, kailangan nating maunawaan kung ito ay konektado? at kung anong mac at ip meron ang phone natin.

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

Dito ginagamit namin ang sniff function mula sa scapy framework, sa tulong nito ay nakatanggap kami ng paunang natukoy na udp packet, maghintay ng 70 segundo at kung wala kaming mahuli, lalabas kami.

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

Susunod, tinitiyak namin na ang aparato ay talagang Yealink at ibabalik ang mga kinakailangang halaga (ip at mac).

Gamit ang isang espesyal na kahilingan, nalaman namin ang kasalukuyang account sa telepono. Upang gawin ito, ang kasalukuyang configuration ay nai-download mula sa telepono at na-parse.

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

Alamin ang password para sa account na ito. Upang gawin ito, bumaling kami sa asterisk.sip table at sa field ng data sa loob nito.

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

Well, para sa huling yugto kumonekta kami sa ldap AD at gamit ang sAMAccountName na nakuha sa pamamagitan ng function getpass.getuser() kunin ang cn ng kasalukuyang user (na kadalasang naglalaman ng buong pangalan ng user).

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

Kumonekta kami sa isang paunang nilikha na talahanayan sa database (ginawa ko ito doon) at ipasok ang lahat ng aming natutunan, katulad: 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()

Maaari kaming huminto dito, dahil nakagawa na kami ng isang dynamic na address book, maaari mong itanong, ngunit lumayo pa ako at nagdagdag ng awtomatikong pagbibigay ng mga device dito.

Upang gawin ito, ang isang configuration ng template ay dina-download mula sa isang paunang na-configure na tftp server, kung saan gagawin namin ang aming mga pagbabago at i-save ito bilang mac.cfg. Ibig sabihin, para sa Yealink mayroong dalawang uri ng configuration, ang isa ay pandaigdigan, at ang pangalawa ay nalalapat sa isang partikular na telepono at dapat ay nasa anyong mac_phone.cfg

Matapos ang lahat ng mga pagbabago sa file at i-save ito pabalik sa tftp server, binibigyan namin ang utos sa telepono na ibigay at i-reboot ang device.

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

Pagkatapos i-reboot ang device, makukuha namin ang aming buong pangalan sa screen ng telepono + isang address book na laging napunan nang tama sa anyo ng isang database, pagkatapos ang natitira na lang ay magdagdag ng XML at isang maliit na PHP upang dynamic na maipakita ang nilalaman. Mayroong maraming mga tulad na mga halimbawa, kahit na ang YEALINK mismo ay mayroon nito.

PS: Para sa mas malaking scalability, maaari mong ilipat ang mga pangunahing setting (mga variable) sa isang hiwalay na file.

Pinagmulan: www.habr.com

Magdagdag ng komento