#!/usr/bin/env python3 """ Spaceweb DNS API — управление DNS записями через JSON-RPC. Панель: https://vps.sweb.ru Домены: niikn.com, dttb.ru, itilegent.ru Использование: python3 spaceweb-dns-api.py info niikn.com python3 spaceweb-dns-api.py add-mx niikn.com mail.niikn.com 10 python3 spaceweb-dns-api.py add-txt niikn.com @ "v=spf1 mx ~all" python3 spaceweb-dns-api.py add-txt niikn.com _dmarc "v=DMARC1; p=none" python3 spaceweb-dns-api.py del niikn.com TXT 1 python3 spaceweb-dns-api.py zone niikn.com """ import urllib.request import urllib.parse import json import http.cookiejar import sys LOGIN = "it5870yand" PASSWORD = "1qaz!QAZ" UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" def create_session(): """Логин в Spaceweb, возвращает opener с куками.""" cj = http.cookiejar.CookieJar() opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj)) # GET login page req = urllib.request.Request("https://mcp.sweb.ru/") req.add_header("User-Agent", UA) opener.open(req) # POST auth login_data = urllib.parse.urlencode({ "login": LOGIN, "password": PASSWORD, "new_panel": "1", "to": "//mcp.sweb.ru/main/index/", "savepref": "" }).encode() req = urllib.request.Request("https://mcp.sweb.ru/main/auth_submit/", data=login_data, method="POST") req.add_header("Content-Type", "application/x-www-form-urlencoded") req.add_header("User-Agent", UA) req.add_header("Referer", "https://mcp.sweb.ru/main/auth/") resp = opener.open(req) if "vps.sweb.ru" not in resp.url and "mcp.sweb.ru/main/index" not in resp.url: raise RuntimeError(f"Login failed, redirected to: {resp.url}") return opener def api_call(opener, method, params): """Вызов JSON-RPC API Spaceweb.""" payload = { "jsonrpc": "2.0", "id": 1, "user": LOGIN, "method": method, "params": params } body = urllib.parse.quote(json.dumps(payload, separators=(",", ":")), safe="").encode() req = urllib.request.Request("https://api.sweb.ru/domains/dns", data=body, method="POST") req.add_header("Content-Type", "application/x-www-form-urlencoded") req.add_header("User-Agent", UA) req.add_header("Origin", "https://vps.sweb.ru") req.add_header("Referer", "https://vps.sweb.ru/") resp = opener.open(req) result = json.loads(resp.read().decode()) if "error" in result: raise RuntimeError(f"API error: {result['error']['message']}") return result.get("result") def cmd_info(opener, domain): """Показать все DNS записи.""" records = api_call(opener, "info", {"domain": domain}) print(f"DNS записи для {domain}:") for r in records: t = r.get("type", "?") n = r.get("name", "@") or "@" v = r.get("value", "") idx = r.get("index", "?") extra = f" (pri: {r.get('priority')})" if r.get("priority") else "" print(f" [{idx:>2}] {t:5s} {n:30s} → {v[:100]}{extra}") def cmd_zone(opener, domain): """Показать zone file.""" result = api_call(opener, "getFile", {"domain": domain}) print(result["content"]) def cmd_add_mx(opener, domain, value, priority="10"): """Добавить MX запись.""" if not value.endswith("."): value += "." result = api_call(opener, "editMx", { "domain": domain, "subDomain": "", "action": "add", "priority": str(priority), "value": value }) print(f"MX добавлен: {domain} → {value} (pri {priority}): {result}") def cmd_add_txt(opener, domain, subdomain, value): """Добавить TXT запись.""" result = api_call(opener, "editTxt", { "domain": domain, "action": "add", "subDomain": subdomain, "value": value }) print(f"TXT добавлен: {subdomain}.{domain} → {value[:60]}...: {result}") def cmd_add_a(opener, domain, name, ip): """Добавить A запись (субдомен).""" result = api_call(opener, "editMain", { "domain": domain, "action": "add", "name": name, "type": "A", "value": ip, "prefix": "" }) print(f"A добавлен: {name}.{domain} → {ip}: {result}") def cmd_del(opener, domain, record_type, index): """Удалить запись по типу и индексу.""" method_map = { "A": "editMain", "AAAA": "editMain", "CNAME": "editMain", "MX": "editMx", "TXT": "editTxt", "NS": "editNs", "SRV": "editSrv", "CAA": "editCaa" } method = method_map.get(record_type.upper(), "editMain") result = api_call(opener, method, { "domain": domain, "action": "del", "index": int(index), "type": record_type.upper() }) print(f"Удалено: {record_type} index={index}: {result}") def main(): if len(sys.argv) < 3: print(__doc__) sys.exit(1) cmd = sys.argv[1] domain = sys.argv[2] opener = create_session() if cmd == "info": cmd_info(opener, domain) elif cmd == "zone": cmd_zone(opener, domain) elif cmd == "add-mx" and len(sys.argv) >= 4: pri = sys.argv[4] if len(sys.argv) > 4 else "10" cmd_add_mx(opener, domain, sys.argv[3], pri) elif cmd == "add-txt" and len(sys.argv) >= 5: cmd_add_txt(opener, domain, sys.argv[3], sys.argv[4]) elif cmd == "add-a" and len(sys.argv) >= 5: cmd_add_a(opener, domain, sys.argv[3], sys.argv[4]) elif cmd == "del" and len(sys.argv) >= 5: cmd_del(opener, domain, sys.argv[3], sys.argv[4]) else: print(__doc__) sys.exit(1) if __name__ == "__main__": main()