#!/usr/bin/env python3 import argparse import base64 import json import logging import ssl import urllib.parse import urllib.request logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s") logger = logging.getLogger(__name__) ROUTER_HOST = "192.168.1.1" ROUTER_USERNAME = "abdus" ROUTER_PASSWORD = "xAsametk50" def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Log in to the Zyxel router over HTTPS and reboot it", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument("--host", default=ROUTER_HOST) parser.add_argument("--username", default=ROUTER_USERNAME) parser.add_argument("--timeout", type=float, default=15.0, help="Request timeout in seconds") parser.add_argument("--dry-run", action="store_true", help="Log in but do not reboot") return parser.parse_args() def build_opener() -> urllib.request.OpenerDirector: # The router uses a self-signed certificate. Over HTTPS the UI skips its RSA/AES login layer. context = ssl.create_default_context() context.check_hostname = False context.verify_mode = ssl.CERT_NONE return urllib.request.build_opener( urllib.request.HTTPSHandler(context=context), urllib.request.HTTPCookieProcessor(), ) def post(opener: urllib.request.OpenerDirector, url: str, body: dict, timeout: float) -> dict: request = urllib.request.Request( url=url, data=json.dumps(body).encode(), headers={"Content-Type": "application/json"}, method="POST", ) with opener.open(request, timeout=timeout) as response: raw = response.read().decode() return json.loads(raw) if raw.strip() else {} def login(opener: urllib.request.OpenerDirector, base_url: str, username: str, timeout: float) -> str: body = post( opener=opener, url=f"{base_url}/UserLogin", body={ "Input_Account": username, "Input_Passwd": base64.b64encode(ROUTER_PASSWORD.encode()).decode(), "currLang": "en", "RememberPassword": 0, "SHA512_password": False, }, timeout=timeout, ) if body.get("result") != "ZCFG_SUCCESS": raise RuntimeError(f"Login rejected: {body}") return body.get("sessionkey", "") def reboot(opener: urllib.request.OpenerDirector, base_url: str, session_key: str, timeout: float) -> None: query = f"?{urllib.parse.urlencode({'sessionkey': session_key})}" if session_key else "" body = post(opener=opener, url=f"{base_url}/cgi-bin/Reboot{query}", body={}, timeout=timeout) logger.info("Reboot response: %s", body) def main() -> None: args = parse_args() base_url = f"https://{args.host}" opener = build_opener() session_key = login(opener=opener, base_url=base_url, username=args.username, timeout=args.timeout) logger.info("Logged in as %s", args.username) if args.dry_run: logger.info("Dry run, not rebooting") return reboot(opener=opener, base_url=base_url, session_key=session_key, timeout=args.timeout) logger.info("Reboot request sent, router is back in about a minute") if __name__ == "__main__": main()