عندما جئت للعمل في هذه الشركة ، كان لدي بالفعل بعض القواعد على أجهزة بروتوكول الإنترنت ، وعدة خوادم ذات علامة نجمية ورذاذ في شكل FreeBPX. بالإضافة إلى ذلك ، عمل هاتف سامسونغ IDCS500 التمثيلي عبر الهاتف بشكل متوازٍ ، وكان عمومًا نظام الاتصالات الرئيسي في الشركة ، وكان الاتصال الهاتفي عبر بروتوكول الإنترنت يعمل فقط لقسم المبيعات. وكل شيء كان سيظل يغلي باستمرار ، لكن في يوم من الأيام ، تم إصدار المرسوم بنقل الجميع إلى مهاتفة بروتوكول الإنترنت ، وتم الاتفاق على التواريخ ، وشراء المعدات ، وبدأ تنفيذ خطة لنقل المؤسسة إلى القرن الحادي والعشرين.
أول ما بدأ يزعج في هذا الموقف هو العدد المتزايد بسرعة من الهواتف التي تحتاج إلى إدارتها بطريقة أو بأخرى ، والشيء الثاني الذي كان مصدر قلق كبير هو دفتر الهاتف. إذا كان 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("~") + "\\" + "AppData\\Local\\" + 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 لا يمكن أن يعمل كبوابة.
أولا نحن بحاجة إلى فهم ما إذا كان متصلا؟ وأي ماك و 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 ، نستخدمها نحصل على حزمة 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 نفسها لديها.
ملاحظة: لمزيد من قابلية التوسع ، يمكنك نقل الإعدادات الرئيسية (المتغيرات) إلى ملف منفصل.