Երբ ես եկա այս ընկերությունում աշխատելու, ես արդեն ունեի IP սարքերի տվյալների բազա, աստղանիշով մի քանի սերվեր և FreeBPX-ի տեսքով կարկատել: Բացի այդ, զուգահեռ աշխատում էր անալոգային PBX 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
Այստեղ մենք օգտագործում ենք sniff ֆունկցիան scapy frame-ից, որի օգնությամբ մենք ստանում ենք կանխորոշված 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
Պարզեք այս հաշվի գաղտնաբառը: Դա անելու համար մենք դիմում ենք asterisk.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-ն ունի:
Հ.Գ. Ավելի լայնածավալության համար կարող եք հիմնական կարգավորումները (փոփոխականները) տեղափոխել առանձին ֆայլ:
Source: www.habr.com