Automatinis aprūpinimas Yealink T19 + dinaminė adresų knyga

Kai atėjau dirbti į šią įmonę, jau turėjau tam tikrą IP įrenginių duomenų bazę, kelis serverius su žvaigždute ir pataisą FreeBPX pavidalu. Be to, analoginis PBX Samsung IDCS500 veikė lygiagrečiai ir apskritai buvo pagrindinė komunikacijos sistema įmonėje, IP telefonija veikė tik pardavimo skyriui. Ir viskas būtų toliau taip kepama, bet vieną gražią dieną buvo duotas potvarkis visus perkelti į IP telefoniją, sutarti terminai, nupirkta įranga ir pradėtas įgyvendinti planas perkelti įmonę į XXI amžių.
Pirmas dalykas, kuris tokioje situacijoje pradeda nerimauti, yra sparčiai augantis telefonų aparatų skaičius, kurį reikia kažkaip tvarkyti, antras dalykas, kuris labai kėlė nerimą, buvo telefonų knyga. Jei „Endpoint Manager“ galėjo mums padėti su pirmuoju (kuris, beje, buvo iškirptas iš naujausių „FreePBX“ versijų), tada su knyga kilo keletas klausimų:

  • Pirma, kaip užtikrinti jos tikslumą, kai nuolat kinta vartotojų vieta/slankumas?
  • Antra, kaip visiškai nuasmeninti telefonus. Ir ne kiekvieną kartą pildyti kontakto vardą?

Problema buvo įdomi, sprendimas netruko rasti. Dabar pateiksiu visą sąrašą, o tada žiūrėsime eilės tvarka.

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

Programa veikia vartotojo kompiuteryje ir veikia su sąlyga, kad kompiuteris yra prijungtas prie tinklo per telefoną, nes Yealink T19 negali veikti kaip vartai.

Pirma, turime suprasti, ar jis yra susijęs? o koks mac ir ip mūsų telefonas.

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

Čia mes naudojame sniff funkciją iš scapy framework, kurios pagalba gauname iš anksto numatytą udp paketą, laukiame 70 sekundžių ir jei nieko nepagauname, išeiname.

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

Tada įsitikiname, kad įrenginys tikrai yra „Yealink“, ir grąžiname reikiamas reikšmes (ip ir mac).

Specialiu prašymu išsiaiškiname einamąją sąskaitą telefone. Norėdami tai padaryti, dabartinė konfigūracija atsisiunčiama iš telefono ir išanalizuojama.

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

Sužinokite šios paskyros slaptažodį. Norėdami tai padaryti, kreipiamės į lentelę asterisk.sip ir joje esantį duomenų lauką.

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

Na, o paskutiniame etape prisijungiame prie ldap AD ir naudojame per funkciją gautą sAMAccountName getpass.getuser() paimkite dabartinio vartotojo cn (paprastai jame yra visas vartotojo vardas).

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

Prisijungiame prie iš anksto sukurtos lentelės duomenų bazėje (aš ten ją sukūriau) ir įvedame viską, ką sužinojome, būtent: ip, mac, vartotojo vardas.

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

Galime čia sustoti, nes jau sukūrėme dinaminę adresų knygą, galite paklausti, bet aš nuėjau toliau ir čia pridėjau automatinį įrenginių aprūpinimą.

Norėdami tai padaryti, šablono konfigūracija atsisiunčiama iš iš anksto sukonfigūruoto tftp serverio, kuriame atliekame pakeitimus ir išsaugome jį kaip mac.cfg. Tai reiškia, kad Yealink yra dviejų tipų konfigūracijos: vienas yra visuotinis, o antrasis taikomas konkrečiam telefonui ir turėtų būti mac_phone.cfg

Atlikę visus failo pakeitimus ir išsaugoję jį atgal į tftp serverį, telefonui duodame komandą parengti ir perkrauti įrenginį.

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

Perkrovę įrenginį telefono ekrane gauname pilną vardą + visada teisingai užpildytą adresų knygą duomenų bazės pavidalu, tada belieka pridėti XML ir šiek tiek PHP, kad dinamiškai būtų rodomas turinys. Tokių pavyzdžių yra labai daug, jų turi net pati YEALINK.

PS: Norėdami didesnio mastelio, pagrindinius nustatymus (kintamuosius) galite perkelti į atskirą failą.

Šaltinis: www.habr.com

Добавить комментарий