#!/usr/bin/env python3 from bs4 import BeautifulSoup import time import requests import pdb import os import random import re from enum import Enum IP = "192.168.42.100" class API_CMD_IDS(Enum): READ_PORTS = 768 READ_VLANS = 1283 READ_VLAN_PORTS = 1290 READ_VLAN_PORT_CFG = 1293 # this needs vid as a parameter and is parsed by parse_vlan_ports READ_STP_PORTS = 4098 class VLAN_PORT_MODE(Enum): GENERAL = 0 ACCESS = 1 TRUNK = 2 DOT1QTUNNEL = 3 class VLAN_PORT_ACL(Enum): EXCLUDED = 0 FORBIDDEN = 1 TAGGED = 2 UNTAGGED = 3 def zyxel_password(pw: str) -> str: alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" result = "" l = len(pw) for i in range(1, 322 - len(pw)): if i % 5 == 0 and l > 0: l -= 1 result += pw[l] elif i == 123: if len(pw) < 10: result += "0" else: c = str(len(pw) // 10) result += c elif i == 289: result += str(len(pw) % 10) else: rnd = random.choice(list(alphabet)) result += rnd return result def get_login_cookie(host: str, username: str, password: str) -> str: auth_id = requests.get( f"http://{host}/cgi-bin/dispatcher.cgi", params={"login": 1, "username": username, "password": zyxel_password(password)}, ) if auth_id.status_code != 200: raise Exception("error while getting auth_id {auth_id.text}") time.sleep(0.5) cookie = requests.post( f"http://{host}/cgi-bin/dispatcher.cgi", data={"authId": auth_id.text.strip(), "login_chk": "true"}, ) if cookie.text.strip() != "OK,": raise Exception("error while getting cookie {cookie.text}") return cookie.cookies.get("HTTP_XSSID") def get_cmd(host: str, xssid: str, cmd: int, **params) -> str: params.update({"cmd": cmd}) return requests.get( f"http://{host}/cgi-bin/dispatcher.cgi", params=params, cookies={"HTTP_XSSID": xssid}, ).text def parse_vlan_ports(response: str) -> dict: soup = BeautifulSoup(response, "html.parser") data = {} port_settings = soup.find_all( "input", attrs={"type": "hidden", "id": re.compile(r"vlanMode_\d+")} ) for port_setting in port_settings: port = port_setting.find_previous().text.strip() port_type = int(port_setting.attrs.get("value")) acl = list( filter( lambda p: "checked" in p.attrs, port_setting.find_next().find_all("input"), ) )[0] data.update( { port: { "mode": VLAN_PORT_MODE(port_type), "acl": VLAN_PORT_ACL(int(acl.attrs.get("value"))), } } ) return data def extract_data_from_table(response: str) -> dict: soup = BeautifulSoup(response, "html.parser") data = {} table_body = soup.find_all("table")[-1] rows = table_body.find_all("tr") keys = [ele.text.strip() for ele in rows[0].find_all("td")] entries = [] for row in rows[1:]: cols = row.find_all("td") cols = [ele.text.strip() for ele in cols] entries.append({k: v for (k, v) in zip(keys, cols) if k.strip() != ""}) return list(filter(lambda e: e, entries)) def dictify(data: list, key: str) -> dict: result = {} for d in data: k = d[key] del d[key] result.update({k: d}) return result if __name__ == "__main__": cookie = get_login_cookie(IP, "admin", os.environ.get("ADMIN_PW")) print(dictify(extract_data_from_table(get_cmd(IP, cookie, API_CMD_IDS.READ_PORTS.value)), "Port")) print(dictify(extract_data_from_table(get_cmd(IP, cookie, API_CMD_IDS.READ_VLANS.value)), "VLAN ID")) print(dictify(extract_data_from_table(get_cmd(IP, cookie, API_CMD_IDS.READ_VLAN_PORTS.value)), "Port")) print(dictify(extract_data_from_table(get_cmd(IP, cookie, API_CMD_IDS.READ_STP_PORTS.value)), "Port")) print(parse_vlan_ports(get_cmd(IP, cookie, API_CMD_IDS.READ_VLAN_PORT_CFG.value, vid=2)))