Мен осы компанияға жұмысқа келгенімде менде IP құрылғыларының кейбір деректер базасы, жұлдызшасы бар бірнеше серверлер және FreeBPX түріндегі патч болды. Сонымен қатар, Samsung IDCS500 аналогтық АТС параллель жұмыс істеді және тұтастай алғанда компаниядағы негізгі байланыс жүйесі болды, IP телефония тек сату бөлімі үшін жұмыс істеді. Бәрі осылай пісірілетін еді, бірақ жақсы күндердің бірінде барлығын IP телефонияға көшіру туралы қаулы шығарылды, мерзімдер келісілді, құрал-жабдықтар сатып алынды және кәсіпорынды 21 ғасырға көшіру жоспары жүзеге асырыла бастады.
Мұндай жағдайда алаңдата бастайтын бірінші нәрсе - бұл қандай да бір жолмен басқаруды қажет ететін телефон аппараттарының тез өсіп келе жатқан саны, екінші қатты алаңдатқан нәрсе - телефон кітапшасы. Егер Endpoint Manager бізге бірінші нұсқада көмектесе алса (айтпақшы, ол FreePBX соңғы нұсқаларынан алынып тасталған), онда кітапқа қатысты кейбір сұрақтар туындады:
- Біріншіден, пайдаланушылардың орны/өткімділігі үнемі өзгеріп тұрғанда оның дәлдігін қалай қамтамасыз етуге болады?
- Екіншіден, телефондарды қалай толығымен иесіздандыруға болады. Әрдайым контакт атын толтырмайсыз ба?
Мәселе қызық болды, шешімі көп күттірмеді. Енді мен толық тізімді беремін, содан кейін оны ретімен қараймыз.
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')
Бағдарлама пайдаланушының компьютерінде жұмыс істейді және компьютер желіге телефон арқылы қосылған жағдайда жұмыс істейді, өйткені Yealink T19 шлюз ретінде жұмыс істей алмайды.
Біріншіден, біз түсінуіміз керек, ол байланысты ма? және біздің телефонда қандай Mac және IP бар.
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
Мұнда біз scapy құрылымынан sniff функциясын қолданамыз, оның көмегімен біз алдын ала анықталған udp пакетін аламыз, 70 секунд күтеміз және ештеңе ұстамасақ, шығамыз.
count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060"
Әрі қарай, біз құрылғының шынымен Yealink екеніне көз жеткіземіз және қажетті мәндерді (ip және mac) қайтарамыз.
Арнайы сұрауды пайдалана отырып, біз телефондағы ағымдағы есептік жазбаны анықтаймыз. Ол үшін ағымдағы конфигурация телефоннан жүктеліп, талданады.
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
Осы есептік жазбаның құпия сөзін табыңыз. Ол үшін жұлдызша.sip кестесіне және ондағы деректер өрісіне жүгінеміз.
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
Ал, соңғы кезеңде біз ldap AD-ге қосыламыз және функция арқылы алынған sAMAccountName пайдаланамыз getpass.getuser() ағымдағы пайдаланушының cn кодын алыңыз (ол әдетте пайдаланушының толық аты-жөнін қамтиды).
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
Біз дерекқордағы алдын ала жасалған кестеге қосыламыз (мен оны сол жерде жасадым) және біз білген барлық нәрсені енгіземіз, атап айтқанда: ip, mac, пайдаланушы аты.
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()
Біз осы жерде тоқтай аламыз, өйткені біз динамикалық мекенжай кітабын жасап қойдық, сіз сұрай аласыз, бірақ мен одан әрі бардым және құрылғыларды автоматты түрде қамтамасыз етуді осында қостым.
Ол үшін шаблон конфигурациясы алдын ала конфигурацияланған tftp серверінен жүктеледі, біз оған өзгерістер енгізіп, оны mac.cfg ретінде сақтаймыз. Яғни, Yealink үшін конфигурацияның екі түрі бар, біреуі жаһандық, екіншісі белгілі бір телефонға қолданылады және mac_phone.cfg пішімінде болуы керек.
Файлдағы барлық өзгерістерден кейін және оны tftp серверіне сақтағаннан кейін біз телефонға құрылғыны қамтамасыз ету және қайта жүктеу пәрменін береміз.
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')
Құрылғыны қайта жүктегеннен кейін біз телефон экранында толық аты-жөнімізді аламыз + дерекқор түрінде әрқашан дұрыс толтырылған мекенжай кітабы, содан кейін мазмұнды динамикалық түрде көрсету үшін XML және кішкене PHP қосу ғана қалады. Мұндай мысалдар өте көп, тіпті YEALINK-тің өзінде де бар.
PS: Үлкенірек масштабтау үшін негізгі параметрлерді (айнымалыларды) бөлек файлға жылжытуға болады.
Ақпарат көзі: www.habr.com