Kun tulin töihin tähän yritykseen, minulla oli jo tietokanta IP-laitteista, useita tähdellä merkittyjä palvelimia ja korjaustiedosto FreeBPX:n muodossa. Lisäksi analoginen PBX Samsung IDCS500 toimi rinnakkain ja oli yleisesti ottaen yrityksen pääviestintäjärjestelmä, IP-puhelut toimi vain myyntiosastolla. Ja kaikki olisi jatkossakin kypsennetty tällä tavalla, mutta eräänä kauniina päivänä annettiin asetus siirtää kaikki IP-puheluihin, sovittiin määräajoista, ostettiin laitteet ja suunnitelmaa yrityksen siirtämisestä 21-luvulle alettiin toteuttaa.
Ensimmäinen asia, joka tällaisessa tilanteessa alkaa huolestumaan, on nopeasti kasvava määrä puhelimia, joita pitäisi jotenkin hallita, toiseksi erittäin huolestuttava asia oli puhelinluettelo. Jos Endpoint Manager saattoi auttaa meitä ensimmäisessä (joka muuten leikattiin pois FreePBX:n uusimmista versioista), kirjan kanssa heräsi kysymyksiä:
- Ensinnäkin, kuinka varmistaa sen tarkkuus, kun käyttäjien sijainti/sujuvuus muuttuu jatkuvasti?
- Toiseksi, kuinka puhelimia voidaan depersonalisoida kokonaan. Etkö täytä yhteyshenkilön nimeä joka kerta?
Ongelma oli mielenkiintoinen, ratkaisun saapuminen ei kestänyt kauan. Nyt annan täydellisen luettelon, ja sitten katsomme sitä järjestyksessä.
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')
Ohjelma toimii käyttäjän tietokoneella ja toimii edellyttäen, että tietokone on kytketty verkkoon puhelimen kautta, koska Yealink T19 ei voi toimia yhdyskäytävänä.
Ensinnäkin meidän on ymmärrettävä, onko se yhteydessä? ja mikä mac ja ip puhelimellamme 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
Täällä käytämme scapy-kehyksen sniff-toimintoa, jonka avulla vastaanotamme ennalta määrätyn udp-paketin, odotamme 70 sekuntia ja jos emme saa mitään, poistumme.
count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060"
Seuraavaksi varmistamme, että laite on todella Yealink ja palautamme tarvittavat arvot (ip ja mac).
Erikoispyynnön avulla selvitämme nykyisen tilin puhelimessa. Tätä varten nykyinen kokoonpano ladataan puhelimesta ja jäsennetään.
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
Selvitä tämän tilin salasana. Tätä varten siirrymme asterisk.sip-taulukkoon ja siinä olevaan tietokenttään.
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
Viimeistä vaihetta varten muodostamme yhteyden ldap AD:hen ja käytämme funktion kautta saatua sAMAccountNamea getpass.getuser() ota nykyisen käyttäjän cn (joka yleensä sisältää käyttäjän koko nimen).
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
Yhdistämme tietokannassa valmiiksi luotuun taulukkoon (loin sen siellä) ja kirjoitamme kaiken, mitä opimme, nimittäin: ip, mac, käyttäjätunnus.
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()
Voisimme lopettaa tähän, koska olemme jo luoneet dynaamisen osoitekirjan, voit kysyä, mutta menin pidemmälle ja lisäsin laitteiden automaattisen hallinnan tähän.
Tätä varten esikonfiguroidulta tftp-palvelimelta ladataan mallikokoonpano, johon teemme muutokset ja tallennamme sen nimellä mac.cfg. Toisin sanoen Yealinkille on olemassa kahdenlaisia määrityksiä, joista toinen on yleinen ja toinen koskee tiettyä puhelinta, ja sen tulee olla muotoa mac_phone.cfg
Kaikkien tiedostoon tehtyjen muutosten ja sen takaisin tftp-palvelimelle tallentamisen jälkeen annamme puhelimelle komennon varustaa ja käynnistää laite uudelleen.
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')
Laitteen uudelleenkäynnistyksen jälkeen saamme puhelimen näytölle koko nimemme + aina oikein täytetty osoitekirja tietokannan muodossa, sitten ei enää tarvitse kuin lisätä XML ja pieni PHP sisällön näyttämiseksi dynaamisesti. Tällaisia esimerkkejä on paljon, jopa YEALINKillä itsellään.
PS: Skaalautuvuuden lisäämiseksi voit siirtää pääasetukset (muuttujat) erilliseen tiedostoon.
Lähde: will.com