mirror of
https://github.com/openwrt-xiaomi/xmir-patcher.git
synced 2026-09-22 22:57:55 +03:00
gateway: Fix functions run_cmd, download, upload for TELNET
This commit is contained in:
+119
-18
@@ -9,6 +9,8 @@ import datetime
|
||||
import random
|
||||
import hashlib
|
||||
import subprocess
|
||||
import gzip
|
||||
import base64
|
||||
import re
|
||||
import requests
|
||||
import atexit
|
||||
@@ -75,6 +77,7 @@ class Gateway():
|
||||
self.nonce_key = None
|
||||
self.stok = None # HTTP session token
|
||||
self.status = -2
|
||||
self.errcode = -1
|
||||
self.ftp = None
|
||||
self.socket = None # TCP socket for SSH
|
||||
self.ssh = None # SSH session
|
||||
@@ -898,6 +901,8 @@ class Gateway():
|
||||
|
||||
def run_cmd(self, command, msg = None, timeout = None, die_on_error = True):
|
||||
error = 0
|
||||
reslist = [ ]
|
||||
self.errcode = -1
|
||||
if self.use_ssh:
|
||||
ssh = self.get_ssh(self.verbose)
|
||||
else:
|
||||
@@ -911,6 +916,8 @@ class Gateway():
|
||||
cmdlist = command
|
||||
if not cmdlist:
|
||||
raise ValueError('Incorrect command list')
|
||||
if '\n' in ';'.join(cmdlist):
|
||||
raise ValueError('Incorrect command format (1)')
|
||||
for idx, cmd in enumerate(cmdlist):
|
||||
if self.use_ssh:
|
||||
channel = ssh.open_session()
|
||||
@@ -935,16 +942,41 @@ class Gateway():
|
||||
except Exception:
|
||||
pass
|
||||
#status = channel.get_exit_status()
|
||||
self.errcode = 0
|
||||
reslist.append('')
|
||||
else: # telnet
|
||||
cmd += '\n'
|
||||
tn.write(cmd.encode('ascii'))
|
||||
tn.read_until(tn.prompt, timeout = 4 if timeout is None else timeout)
|
||||
end = b'echo -e "\\xA6$?\\xA7\\xB8_END"'
|
||||
try:
|
||||
tn.write(cmd.encode('latin1') + b'\n' + end + b'\n')
|
||||
res = tn.read_until(b'\xA7\xB8_END\r\n' + tn.prompt, timeout = 4 if timeout is None else timeout)
|
||||
p1 = res.find(b'\r\n')
|
||||
p2 = res.rfind(b'\r\n' + tn.prompt + end)
|
||||
p3 = res.rfind(b'\r\n\xA6')
|
||||
p4 = res.rfind(b'\xA7\xB8_END')
|
||||
if p1 < 0 or p2 < 0 or p3 < 0 or p4 < 0 or p3 >= p4:
|
||||
if die_on_error:
|
||||
die('TELNET: execute command: incorrect response (1)')
|
||||
reslist.append(None)
|
||||
self.errcode = -2
|
||||
else:
|
||||
self.errcode = int(res[p3+3:p4].decode(), 10)
|
||||
reslist.append(res[p1+2:p2].decode().replace('\r\n', '\n'))
|
||||
except Exception as e:
|
||||
error = -4
|
||||
if die_on_error:
|
||||
die(f'TELNET: execute command error: "{e}"')
|
||||
if error != 0:
|
||||
break
|
||||
if not self.use_ssh:
|
||||
tn.write(b"exit\n")
|
||||
tn.close()
|
||||
return True if error == 0 else None
|
||||
if not self.use_ssh: # telnet
|
||||
try:
|
||||
tn.write(b"exit\n")
|
||||
tn.close()
|
||||
except Exception:
|
||||
pass
|
||||
if error == 0:
|
||||
return reslist if isinstance(command, list) else reslist[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def download(self, fn_remote, fn_local, verbose = 1):
|
||||
if verbose and self.verbose:
|
||||
@@ -965,11 +997,44 @@ class Gateway():
|
||||
read_size += size
|
||||
elif self.use_ftp:
|
||||
ftp = self.get_ftp(self.verbose)
|
||||
file = open(fn_local, 'wb')
|
||||
ftp.retrbinary('RETR ' + fn_remote, file.write)
|
||||
file.close()
|
||||
else:
|
||||
raise RuntimeError('FIXME')
|
||||
with open(fn_local, 'wb') as file:
|
||||
ftp.retrbinary('RETR ' + fn_remote, file.write)
|
||||
else: # telnet
|
||||
os.remove(fn_local) if os.path.exists(fn_local) else None
|
||||
size = self.get_remote_file_size(fn_remote)
|
||||
if size < 0:
|
||||
return False
|
||||
if size == 0:
|
||||
with open(fn_local, 'wb') as file:
|
||||
pass
|
||||
return True
|
||||
tn = self.get_telnet(self.verbose)
|
||||
fin = b" ; echo 'X''_FIN_''X'"
|
||||
blksize = 64*1000
|
||||
filesize = 0
|
||||
with open(fn_local, 'wb') as file:
|
||||
while filesize < size:
|
||||
count = blksize
|
||||
if filesize + count > size:
|
||||
count = size - filesize
|
||||
cmd = f"dd if='{fn_remote}' iflag=skip_bytes,count_bytes skip={filesize} count={count} 2>/dev/null | base64"
|
||||
tn.write(cmd.encode('latin1') + fin + b'\n')
|
||||
res = tn.read_until(b'X_FIN_X\r\n' + tn.prompt, timeout = 4)
|
||||
p1 = res.find(b'\r\n')
|
||||
p2 = res.rfind(b'X_FIN_X\r\n')
|
||||
if p1 < 0 or p2 < 0 or p1 >= p2:
|
||||
break
|
||||
data = base64.standard_b64decode(res[p1+2:p2].replace(b'\r\n', b''))
|
||||
filesize += len(data)
|
||||
file.write(data)
|
||||
try:
|
||||
tn.write(b"exit\n")
|
||||
tn.close()
|
||||
except Exception:
|
||||
pass
|
||||
if filesize != size:
|
||||
os.remove(fn_local) if os.path.exists(fn_local) else None
|
||||
return False
|
||||
return True
|
||||
|
||||
def upload(self, fn_local, fn_remote, md5chk = True, verbose = 1):
|
||||
@@ -977,7 +1042,6 @@ class Gateway():
|
||||
die(f'File "{fn_local}" not found.')
|
||||
if md5chk:
|
||||
md5_local = self.get_md5_for_local_file(fn_local)
|
||||
file = open(fn_local, 'rb')
|
||||
if verbose and self.verbose:
|
||||
print(f'Upload file: "{fn_local}" ....')
|
||||
if self.use_ssh:
|
||||
@@ -985,17 +1049,48 @@ class Gateway():
|
||||
finfo = os.stat(fn_local)
|
||||
channel = ssh.scp_send64(fn_remote, finfo.st_mode & 0o777, finfo.st_size, finfo.st_mtime, finfo.st_atime)
|
||||
size = 0
|
||||
if True:
|
||||
with open(fn_local, 'rb') as file:
|
||||
for data in file:
|
||||
channel.write(data)
|
||||
size = size + len(data)
|
||||
#except ssh2.exceptions.SCPProtocolError as e:
|
||||
elif self.use_ftp:
|
||||
ftp = self.get_ftp(self.verbose)
|
||||
ftp.storbinary('STOR ' + fn_remote, file)
|
||||
else:
|
||||
raise RuntimeError('FIXME')
|
||||
file.close()
|
||||
with open(fn_local, 'rb') as file:
|
||||
ftp.storbinary('STOR ' + fn_remote, file)
|
||||
else: # telnet
|
||||
with open(fn_local, 'rb') as file:
|
||||
data = file.read()
|
||||
data = gzip.compress(data, compresslevel = 9)
|
||||
data = base64.standard_b64encode(data)
|
||||
fn_remote_tmp = '/tmp/_T_' + os.path.basename(fn_remote)
|
||||
if len(fn_remote_tmp) > 80:
|
||||
raise ValueError('Incorrect length of filename')
|
||||
self.run_cmd(f"rm -f '{fn_remote_tmp}'")
|
||||
tn = self.get_telnet(self.verbose)
|
||||
filesize = 0
|
||||
while filesize < len(data):
|
||||
chunk = data[filesize:filesize+400]
|
||||
if len(chunk) > 0:
|
||||
fopt = b'>' if filesize == 0 else b'>>'
|
||||
cmd = b"echo -n " + chunk + fopt + fn_remote_tmp.encode()
|
||||
tn.write(cmd + b'\n')
|
||||
res = tn.read_until(b'\r\n' + tn.prompt, timeout = 4)
|
||||
p1 = res.find(b'echo -n ')
|
||||
p2 = res.rfind(b'\r\n' + tn.prompt)
|
||||
if p1 < 0 or p2 < 0 or p1 >= p2 or p2 - p1 != len(cmd):
|
||||
break
|
||||
filesize += len(chunk)
|
||||
try:
|
||||
tn.write(b"exit\n")
|
||||
tn.close()
|
||||
except Exception:
|
||||
pass
|
||||
if filesize != len(data):
|
||||
self.run_cmd(f"rm -f '{fn_remote_tmp}'")
|
||||
return False
|
||||
self.run_cmd(f"rm -f '{fn_remote}' ; cat '{fn_remote_tmp}' | base64 -d | gzip -d > '{fn_remote}'")
|
||||
self.run_cmd(f"rm -f '{fn_remote_tmp}'")
|
||||
if md5chk:
|
||||
md5_remote = self.get_md5_for_remote_file(fn_remote)
|
||||
if md5_remote != md5_local:
|
||||
@@ -1006,6 +1101,12 @@ class Gateway():
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_remote_file_size(self, fn_remote):
|
||||
size = self.run_cmd(f"wc -c '{fn_remote}' 2>/dev/null" + " | awk '{print $1}'")
|
||||
if not size:
|
||||
return -1
|
||||
return int(size, 10)
|
||||
|
||||
def get_md5_for_remote_file(self, fn_remote, timeout = 8):
|
||||
fname = os.path.basename(fn_remote)
|
||||
num = str(random.randint(10000, 1000000))
|
||||
|
||||
Reference in New Issue
Block a user