Initial commit
This commit is contained in:
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Back up one MTD partition of the GPON stick over telnet, md5-verified.
|
||||
Usage: gpon_backup.py <mtd_index> <outfile> [idle_timeout] [hard_cap]
|
||||
Streams `cat /dev/mtdN | uuencode` over the telnet session and uudecodes locally
|
||||
(Python's uu module was removed in 3.13, so we decode by hand)."""
|
||||
import socket, sys, time, re, hashlib
|
||||
from gpon_env import HOST, PORT, USER, PASS
|
||||
|
||||
idx = sys.argv[1]
|
||||
outfile = sys.argv[2]
|
||||
idle_to = float(sys.argv[3]) if len(sys.argv)>3 else 12.0
|
||||
hard_cap = float(sys.argv[4]) if len(sys.argv)>4 else 300.0
|
||||
|
||||
def strip_iac(data, outbuf):
|
||||
res=bytearray(); i=0
|
||||
while i<len(data):
|
||||
b=data[i]
|
||||
if b==0xFF and i+1<len(data):
|
||||
c=data[i+1]
|
||||
if c in (251,252,253,254) and i+2<len(data):
|
||||
o=data[i+2]
|
||||
if c==253: outbuf.append(bytes([255,252,o]))
|
||||
elif c==251: outbuf.append(bytes([255,254,o]))
|
||||
i+=3; continue
|
||||
elif c==250:
|
||||
j=data.find(b'\xff\xf0', i+2); i=len(data) if j<0 else j+2; continue
|
||||
else: i+=2; continue
|
||||
res.append(b); i+=1
|
||||
return bytes(res)
|
||||
|
||||
s=socket.create_connection((HOST,PORT),timeout=8)
|
||||
def rx(idle, cap, stop=None):
|
||||
s.settimeout(0.5); buf=bytearray(); last=time.time(); start=time.time()
|
||||
while True:
|
||||
try:
|
||||
d=s.recv(16384)
|
||||
if not d: break
|
||||
ob=[]; buf.extend(strip_iac(d,ob))
|
||||
for o in ob: s.sendall(o)
|
||||
last=time.time()
|
||||
if stop and stop(buf): break
|
||||
except socket.timeout:
|
||||
if time.time()-last>idle: break
|
||||
if time.time()-start>cap: break
|
||||
return buf.decode('latin-1','replace')
|
||||
|
||||
def send(x): s.sendall(x.encode()+b'\r\n')
|
||||
|
||||
out=rx(2,8)
|
||||
if re.search(r'(ogin|sername)',out): send(USER); out+=rx(2,6)
|
||||
if re.search(r'assword',out,re.I): send(PASS); out+=rx(2,6)
|
||||
send('system'); rx(1.5,5)
|
||||
send('shell'); rx(1.5,5)
|
||||
|
||||
send('md5sum /dev/mtd%s'%idx)
|
||||
m=rx(90,150, stop=lambda b: re.search(rb'[0-9a-f]{32}\s+/dev/mtd',b))
|
||||
mm=re.search(r'([0-9a-f]{32})\s+/dev/mtd%s'%re.escape(idx), m)
|
||||
expected=mm.group(1) if mm else None
|
||||
|
||||
t0=time.time()
|
||||
send('cat /dev/mtd%s | uuencode p'%idx)
|
||||
def done(b):
|
||||
bi=b.find(b'begin ')
|
||||
return bi>=0 and (b'\nend\r' in b[bi:] or b'\nend\n' in b[bi:])
|
||||
d=rx(idle_to, hard_cap, stop=done)
|
||||
dt=time.time()-t0
|
||||
try: s.close()
|
||||
except Exception: pass
|
||||
|
||||
def uudecode(text):
|
||||
out=bytearray(); started=False
|
||||
for ln in text.split('\n'):
|
||||
ln=ln.rstrip('\r')
|
||||
if not started:
|
||||
if ln.startswith('begin '): started=True
|
||||
continue
|
||||
if ln=='end': break
|
||||
if ln=='' or ln=='`': continue
|
||||
n=(ord(ln[0])-0x20)&0x3f
|
||||
if n<=0: continue
|
||||
data=ln[1:]; dec=bytearray()
|
||||
for i in range(0,len(data),4):
|
||||
g=data[i:i+4]; v=[(ord(c)-0x20)&0x3f for c in g]
|
||||
while len(v)<4: v.append(0)
|
||||
dec.append(((v[0]<<2)|(v[1]>>4))&0xff)
|
||||
dec.append(((v[1]<<4)|(v[2]>>2))&0xff)
|
||||
dec.append(((v[2]<<6)|v[3])&0xff)
|
||||
out.extend(dec[:n])
|
||||
return bytes(out)
|
||||
|
||||
raw=uudecode(d)
|
||||
with open(outfile,'wb') as f: f.write(raw)
|
||||
actual=hashlib.md5(raw).hexdigest()
|
||||
print("mtd%s -> %s : %d bytes in %.1fs"%(idx,outfile,len(raw),dt))
|
||||
print(" expected md5 (stick): %s"%expected)
|
||||
print(" actual md5 (local): %s"%actual)
|
||||
print(" VERIFY: %s"%("OK" if (expected and actual==expected) else "*** MISMATCH / INCOMPLETE ***"))
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Drive the stick's #ONT OMCI CLI (NOT the busybox shell). Stays at #ONT> and navigates menus.
|
||||
Usage: gpon_cli.py [--idle S] [--cap S] 'cmd1' 'cmd2' ...
|
||||
Sends each cmd as a CLI line (e.g. 'system', 'misc', '?', 'eqsn get')."""
|
||||
import socket, sys, time, re
|
||||
from gpon_env import HOST, PORT, USER, PASS
|
||||
args=sys.argv[1:]; idle=8.0; cap=60.0; cmds=[]
|
||||
while args:
|
||||
a=args.pop(0)
|
||||
if a=='--idle': idle=float(args.pop(0))
|
||||
elif a=='--cap': cap=float(args.pop(0))
|
||||
else: cmds.append(a)
|
||||
|
||||
def strip_iac(data,outbuf):
|
||||
res=bytearray(); i=0
|
||||
while i<len(data):
|
||||
b=data[i]
|
||||
if b==0xFF and i+1<len(data):
|
||||
c=data[i+1]
|
||||
if c in (251,252,253,254) and i+2<len(data):
|
||||
o=data[i+2]
|
||||
if c==253: outbuf.append(bytes([255,252,o]))
|
||||
elif c==251: outbuf.append(bytes([255,254,o]))
|
||||
i+=3; continue
|
||||
elif c==250:
|
||||
j=data.find(b'\xff\xf0',i+2); i=len(data) if j<0 else j+2; continue
|
||||
else: i+=2; continue
|
||||
res.append(b); i+=1
|
||||
return bytes(res)
|
||||
|
||||
s=socket.create_connection((HOST,PORT),timeout=8)
|
||||
def rx(idle,cap,stop=None):
|
||||
s.settimeout(0.5); buf=bytearray(); last=time.time(); start=time.time()
|
||||
while True:
|
||||
try:
|
||||
d=s.recv(16384)
|
||||
if not d: break
|
||||
ob=[]; buf.extend(strip_iac(d,ob))
|
||||
for o in ob: s.sendall(o)
|
||||
last=time.time()
|
||||
if stop and stop(buf): break
|
||||
except socket.timeout:
|
||||
if time.time()-last>idle: break
|
||||
if time.time()-start>cap: break
|
||||
return buf.decode('latin-1','replace')
|
||||
def send(x): s.sendall(x.encode()+b'\r\n')
|
||||
|
||||
o=rx(3,8)
|
||||
if re.search(r'(ogin|sername)',o): send(USER); o+=rx(2,6)
|
||||
if re.search(r'assword',o,re.I): send(PASS); o+=rx(3,8)
|
||||
# now sitting at #ONT> (do NOT enter shell)
|
||||
promptend=lambda b: b.rstrip().endswith(b'>')
|
||||
for c in cmds:
|
||||
send(c)
|
||||
out=rx(idle,cap,stop=promptend)
|
||||
print("\n>>> %s\n%s"%(c,out))
|
||||
try: s.close()
|
||||
except Exception: pass
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run commands in the stick's root shell over telnet (idle-tolerant, waits for prompt).
|
||||
Usage: gpon_cmd.py [--idle S] [--cap S] 'cmd1' 'cmd2' ..."""
|
||||
import socket, sys, time, re
|
||||
from gpon_env import HOST, PORT, USER, PASS
|
||||
args=sys.argv[1:]; idle=8.0; cap=120.0; cmds=[]
|
||||
while args:
|
||||
a=args.pop(0)
|
||||
if a=='--idle': idle=float(args.pop(0))
|
||||
elif a=='--cap': cap=float(args.pop(0))
|
||||
else: cmds.append(a)
|
||||
|
||||
def strip_iac(data,outbuf):
|
||||
res=bytearray(); i=0
|
||||
while i<len(data):
|
||||
b=data[i]
|
||||
if b==0xFF and i+1<len(data):
|
||||
c=data[i+1]
|
||||
if c in (251,252,253,254) and i+2<len(data):
|
||||
o=data[i+2]
|
||||
if c==253: outbuf.append(bytes([255,252,o]))
|
||||
elif c==251: outbuf.append(bytes([255,254,o]))
|
||||
i+=3; continue
|
||||
elif c==250:
|
||||
j=data.find(b'\xff\xf0',i+2); i=len(data) if j<0 else j+2; continue
|
||||
else: i+=2; continue
|
||||
res.append(b); i+=1
|
||||
return bytes(res)
|
||||
|
||||
s=socket.create_connection((HOST,PORT),timeout=8)
|
||||
def rx(idle,cap,stop=None):
|
||||
s.settimeout(0.5); buf=bytearray(); last=time.time(); start=time.time()
|
||||
while True:
|
||||
try:
|
||||
d=s.recv(16384)
|
||||
if not d: break
|
||||
ob=[]; buf.extend(strip_iac(d,ob))
|
||||
for o in ob: s.sendall(o)
|
||||
last=time.time()
|
||||
if stop and stop(buf): break
|
||||
except socket.timeout:
|
||||
if time.time()-last>idle: break
|
||||
if time.time()-start>cap: break
|
||||
return buf.decode('latin-1','replace')
|
||||
def send(x): s.sendall(x.encode()+b'\r\n')
|
||||
|
||||
o=rx(2,8)
|
||||
if re.search(r'(ogin|sername)',o): send(USER); o+=rx(2,6)
|
||||
if re.search(r'assword',o,re.I): send(PASS); o+=rx(2,6)
|
||||
send('system'); rx(1.5,5)
|
||||
send('shell'); rx(1.5,5)
|
||||
promptend=lambda b: b.rstrip().endswith(b'shell>')
|
||||
for c in cmds:
|
||||
send(c)
|
||||
out=rx(idle,cap,stop=promptend)
|
||||
print("\n$ %s\n%s"%(c,out))
|
||||
try: s.close()
|
||||
except Exception: pass
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Shared connection settings for the telnet drivers in this directory.
|
||||
|
||||
Resolved highest-priority-first from: the process environment, `../.env`,
|
||||
`../example.env`, then built-in defaults. Keeps addresses and credentials out
|
||||
of the scripts (and out of git) — see ../example.env.
|
||||
"""
|
||||
import os
|
||||
|
||||
KEYS = ('GPON_HOST', 'GPON_PORT', 'GPON_USER', 'GPON_PASS')
|
||||
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
def _parse(path):
|
||||
vals = {}
|
||||
try:
|
||||
with open(path) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#') or '=' not in line:
|
||||
continue
|
||||
k, v = line.split('=', 1)
|
||||
vals[k.strip()] = v.strip().strip('"').strip("'")
|
||||
except OSError:
|
||||
pass
|
||||
return vals
|
||||
|
||||
|
||||
_cfg = _parse(os.path.join(_ROOT, 'example.env'))
|
||||
_cfg.update(_parse(os.path.join(_ROOT, '.env')))
|
||||
_cfg.update({k: v for k, v in os.environ.items() if k in KEYS})
|
||||
|
||||
HOST = _cfg.get('GPON_HOST') or '192.168.100.1'
|
||||
PORT = int(_cfg.get('GPON_PORT') or 23)
|
||||
USER = _cfg.get('GPON_USER') or 'root'
|
||||
PASS = _cfg.get('GPON_PASS') or 'admin'
|
||||
Executable
+71
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Minimal scriptable telnet client for probing the GPON stick CLI.
|
||||
Usage: gpon_telnet.py [--host H] [--port P] [--user U] [--pass P] [--wait S] [cmd ...]
|
||||
Host/port/user/pass default to the .env settings (see ../example.env).
|
||||
Each positional cmd is sent as one line after login; output captured per cmd.
|
||||
Reads extra commands (one per line) from stdin if --stdin given.
|
||||
"""
|
||||
import socket, sys, time, re
|
||||
import gpon_env
|
||||
|
||||
args = sys.argv[1:]
|
||||
HOST, PORT = gpon_env.HOST, gpon_env.PORT
|
||||
USER, PASS, WAIT, USE_STDIN = gpon_env.USER, gpon_env.PASS, 2.5, False
|
||||
cmds = []
|
||||
while args:
|
||||
a = args.pop(0)
|
||||
if a == '--host': HOST = args.pop(0)
|
||||
elif a == '--port': PORT = int(args.pop(0))
|
||||
elif a == '--user': USER = args.pop(0)
|
||||
elif a == '--pass': PASS = args.pop(0)
|
||||
elif a == '--wait': WAIT = float(args.pop(0))
|
||||
elif a == '--stdin': USE_STDIN = True
|
||||
else: cmds.append(a)
|
||||
if USE_STDIN:
|
||||
cmds += [l.rstrip('\n') for l in sys.stdin if l.strip() != '']
|
||||
|
||||
def strip_iac(data, outbuf):
|
||||
res = bytearray(); i = 0
|
||||
while i < len(data):
|
||||
b = data[i]
|
||||
if b == 0xFF and i+1 < len(data):
|
||||
cmd = data[i+1]
|
||||
if cmd in (251,252,253,254) and i+2 < len(data):
|
||||
opt = data[i+2]
|
||||
if cmd == 253: outbuf.append(bytes([255,252,opt])) # DO->WONT
|
||||
elif cmd == 251: outbuf.append(bytes([255,254,opt])) # WILL->DONT
|
||||
i += 3; continue
|
||||
elif cmd == 250: # SB..SE
|
||||
j = data.find(b'\xff\xf0', i+2)
|
||||
i = len(data) if j == -1 else j+2; continue
|
||||
else:
|
||||
i += 2; continue
|
||||
res.append(b); i += 1
|
||||
return bytes(res)
|
||||
|
||||
s = socket.create_connection((HOST, PORT), timeout=8)
|
||||
|
||||
def rx(timeout):
|
||||
s.settimeout(0.6); buf = bytearray(); end = time.time()+timeout
|
||||
while time.time() < end:
|
||||
try:
|
||||
data = s.recv(4096)
|
||||
if not data: break
|
||||
ob = []
|
||||
buf.extend(strip_iac(data, ob))
|
||||
for o in ob: s.sendall(o)
|
||||
except socket.timeout:
|
||||
if buf: break
|
||||
return buf.decode('latin-1','replace')
|
||||
|
||||
out = rx(3.0)
|
||||
if re.search(r'(ogin|sername)', out):
|
||||
s.sendall(USER.encode()+b'\r\n'); out += rx(2.0)
|
||||
if re.search(r'assword', out, re.I):
|
||||
s.sendall(PASS.encode()+b'\r\n'); out += rx(3.0)
|
||||
print("===== BANNER/LOGIN =====\n"+out)
|
||||
for c in cmds:
|
||||
s.sendall(c.encode()+b'\r\n'); o = rx(WAIT)
|
||||
print("\n===== CMD: %r =====\n%s" % (c, o))
|
||||
try: s.close()
|
||||
except Exception: pass
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Wait for the stick's telnet to cycle after a reboot, then run #ONT CLI reads to verify.
|
||||
Usage: gpon_verify.py [--wait S] [--idle S] [--cap S] 'cmd1' 'cmd2' ..."""
|
||||
import socket, sys, time, re
|
||||
from gpon_env import HOST, PORT, USER, PASS
|
||||
args=sys.argv[1:]; idle=5.0; cap=25.0; wait=140.0; cmds=[]
|
||||
while args:
|
||||
a=args.pop(0)
|
||||
if a=='--idle': idle=float(args.pop(0))
|
||||
elif a=='--cap': cap=float(args.pop(0))
|
||||
elif a=='--wait': wait=float(args.pop(0))
|
||||
else: cmds.append(a)
|
||||
|
||||
def strip_iac(data,outbuf):
|
||||
res=bytearray(); i=0
|
||||
while i<len(data):
|
||||
b=data[i]
|
||||
if b==0xFF and i+1<len(data):
|
||||
c=data[i+1]
|
||||
if c in (251,252,253,254) and i+2<len(data):
|
||||
o=data[i+2]
|
||||
if c==253: outbuf.append(bytes([255,252,o]))
|
||||
elif c==251: outbuf.append(bytes([255,254,o]))
|
||||
i+=3; continue
|
||||
elif c==250:
|
||||
j=data.find(b'\xff\xf0',i+2); i=len(data) if j<0 else j+2; continue
|
||||
else: i+=2; continue
|
||||
res.append(b); i+=1
|
||||
return bytes(res)
|
||||
|
||||
def port_open():
|
||||
try:
|
||||
c=socket.create_connection((HOST,PORT),timeout=3); c.close(); return True
|
||||
except (OSError,) : return False
|
||||
|
||||
t0=time.time()
|
||||
# 1) give the reboot a moment to take the stick down
|
||||
time.sleep(5)
|
||||
# 2) wait for it to go DOWN (confirms the reboot began)
|
||||
went_down=False
|
||||
while time.time()-t0 < 30:
|
||||
if not port_open(): went_down=True; break
|
||||
time.sleep(2)
|
||||
print("stick went DOWN: %s (t=%ds)"%(went_down, time.time()-t0))
|
||||
# 3) wait for it to come back UP
|
||||
came_up=False
|
||||
while time.time()-t0 < wait:
|
||||
if port_open(): came_up=True; break
|
||||
time.sleep(3)
|
||||
if not came_up:
|
||||
print("STICK DID NOT COME BACK within %ds -- check MikroTik sfp1 / re-seat"%wait); sys.exit(1)
|
||||
print("stick back UP (t=%ds); giving telnetd a few s to settle"%(time.time()-t0))
|
||||
time.sleep(4)
|
||||
|
||||
s=socket.create_connection((HOST,PORT),timeout=8)
|
||||
def rx(idle,cap,stop=None):
|
||||
s.settimeout(0.5); buf=bytearray(); last=time.time(); start=time.time()
|
||||
while True:
|
||||
try:
|
||||
d=s.recv(16384)
|
||||
if not d: break
|
||||
ob=[]; buf.extend(strip_iac(d,ob))
|
||||
for o in ob: s.sendall(o)
|
||||
last=time.time()
|
||||
if stop and stop(buf): break
|
||||
except socket.timeout:
|
||||
if time.time()-last>idle: break
|
||||
if time.time()-start>cap: break
|
||||
return buf.decode('latin-1','replace')
|
||||
def send(x): s.sendall(x.encode()+b'\r\n')
|
||||
|
||||
o=rx(3,10)
|
||||
if re.search(r'(ogin|sername)',o): send(USER); o+=rx(2,6)
|
||||
if re.search(r'assword',o,re.I): send(PASS); o+=rx(3,8)
|
||||
promptend=lambda b: b.rstrip().endswith(b'>')
|
||||
for c in cmds:
|
||||
send(c); out=rx(idle,cap,stop=promptend)
|
||||
print("\n>>> %s\n%s"%(c,out))
|
||||
try: s.close()
|
||||
except Exception: pass
|
||||
Reference in New Issue
Block a user