Tự động cung cấp Yealink T19 + sổ địa chỉ động

Khi đến làm việc cho công ty này, tôi đã có sẵn một số cơ sở dữ liệu về thiết bị IP, một số máy chủ có dấu hoa thị và một bản vá dưới dạng FreeBPX. Ngoài ra, tổng đài analog Samsung IDCS500 hoạt động song song và nhìn chung là hệ thống liên lạc chính trong công ty, điện thoại IP chỉ hoạt động cho bộ phận bán hàng. Và mọi thứ lẽ ra vẫn tiếp tục như vậy, nhưng một ngày đẹp trời, một nghị định được đưa ra để chuyển tất cả mọi người sang điện thoại IP, thời hạn đã được thống nhất, thiết bị được mua và kế hoạch chuyển doanh nghiệp sang thế kỷ 21 bắt đầu được thực hiện.
Điều đầu tiên bắt đầu lo lắng trong tình huống như vậy là số lượng máy điện thoại cần được quản lý bằng cách nào đó ngày càng tăng nhanh, điều thứ hai rất đáng lo ngại là danh bạ điện thoại. Nếu Trình quản lý điểm cuối có thể giúp chúng tôi về phiên bản đầu tiên (nhân tiện, phiên bản này đã bị loại khỏi các phiên bản mới nhất của FreePBX), thì một số câu hỏi sẽ nảy sinh với cuốn sách:

  • Thứ nhất, làm thế nào để đảm bảo tính chính xác khi vị trí/tính linh hoạt của người dùng liên tục thay đổi?
  • Thứ hai, làm thế nào để cá nhân hóa hoàn toàn điện thoại. Và không điền tên liên lạc mỗi lần?

Vấn đề rất thú vị, giải pháp không mất nhiều thời gian để đến. Bây giờ tôi sẽ đưa ra danh sách đầy đủ và sau đó chúng ta sẽ xem xét nó theo thứ tự.

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

Chương trình chạy trên máy tính của người dùng và hoạt động với điều kiện máy tính được kết nối với mạng qua điện thoại, vì Yealink T19 không thể hoạt động như một cổng.

Đầu tiên chúng ta cần hiểu liệu nó có được kết nối hay không? và điện thoại của chúng tôi có mac và ip gì.

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

Ở đây, chúng tôi sử dụng chức năng đánh hơi từ khung scapy, với sự trợ giúp của nó, chúng tôi nhận được gói udp được xác định trước, đợi 70 giây và nếu không bắt được gì, chúng tôi sẽ thoát.

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

Tiếp theo, chúng tôi đảm bảo rằng thiết bị thực sự là Yealink và trả về các giá trị cần thiết (ip và mac).

Sử dụng một yêu cầu đặc biệt, chúng tôi tìm ra tài khoản hiện tại trên điện thoại. Để thực hiện việc này, cấu hình hiện tại được tải xuống từ điện thoại và phân tích cú pháp.

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

Tìm ra mật khẩu cho tài khoản này. Để làm điều này, chúng ta chuyển sang bảng asterisk.sip và trường dữ liệu trong đó.

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

Chà, ở giai đoạn cuối cùng, chúng tôi kết nối với ldap AD và sử dụng sAMAccountName thu được thông qua hàm getpass.getuser() lấy cn của người dùng hiện tại (thường chứa tên đầy đủ của người dùng).

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

Chúng tôi kết nối với một bảng được tạo sẵn trong cơ sở dữ liệu (tôi đã tạo nó ở đó) và nhập mọi thứ chúng tôi đã học được, cụ thể là: ip, mac, tên người dùng.

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

Bạn có thể hỏi, chúng tôi có thể dừng ở đây vì chúng tôi đã tạo một sổ địa chỉ động, nhưng tôi đã đi xa hơn và thêm tính năng tự động cung cấp thiết bị tại đây.

Để thực hiện việc này, một cấu hình mẫu được tải xuống từ máy chủ tftp được định cấu hình trước, trong đó chúng tôi thực hiện các thay đổi và lưu nó dưới dạng mac.cfg. Nghĩa là, đối với Yealink, có hai loại cấu hình, một loại là toàn cầu và loại thứ hai áp dụng cho một điện thoại cụ thể và phải có dạng mac_phone.cfg

Sau tất cả các thay đổi trong tệp và lưu nó trở lại máy chủ tftp, chúng tôi ra lệnh cho điện thoại cung cấp và khởi động lại thiết bị.

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

Sau khi khởi động lại thiết bị, chúng tôi sẽ nhận được tên đầy đủ của mình trên màn hình điện thoại + sổ địa chỉ luôn được điền chính xác dưới dạng cơ sở dữ liệu, sau đó tất cả những gì còn lại là thêm XML và một chút PHP để hiển thị nội dung động. Có rất nhiều ví dụ như vậy, ngay cả chính YEALINK cũng có.

Tái bút: Để có khả năng mở rộng cao hơn, bạn có thể di chuyển các cài đặt (biến) chính sang một tệp riêng.

Nguồn: www.habr.com

Thêm một lời nhận xét