mirror of
https://github.com/openmv/openmv.git
synced 2025-09-26 23:09:13 +08:00
scripts/libraries: Add networking utilities (uping, uftpd and utelnet).
This commit is contained in:
parent
e06cf95754
commit
6ae40d2c80
492
scripts/libraries/uftpd.py
Normal file
492
scripts/libraries/uftpd.py
Normal file
@ -0,0 +1,492 @@
|
|||||||
|
# Small ftp server for ESP8266 Micropython
|
||||||
|
# Based on the work of chrisgp - Christopher Popp and pfalcon - Paul Sokolovsky
|
||||||
|
#
|
||||||
|
# The server accepts passive mode only. It runs in background.
|
||||||
|
# Start the server with:
|
||||||
|
#
|
||||||
|
# import uftpd
|
||||||
|
# uftpd.start([port = 21][, verbose = level])
|
||||||
|
#
|
||||||
|
# port is the port number (default 21)
|
||||||
|
# verbose controls the level of printed activity messages, values 0, 1, 2
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 Christopher Popp (initial ftp server framework)
|
||||||
|
# Copyright (c) 2016 Paul Sokolovsky (background execution control structure)
|
||||||
|
# Copyright (c) 2016 Robert Hammelrath (putting the pieces together and a
|
||||||
|
# few extensions)
|
||||||
|
# Copyright (c) 2020 Jan Wieck Use separate FTP servers per socket for STA + AP mode
|
||||||
|
# Copyright (c) 2021 JD Smith Use a preallocated buffer and improve error handling.
|
||||||
|
# Distributed under MIT License
|
||||||
|
#
|
||||||
|
import socket
|
||||||
|
import network
|
||||||
|
import uos
|
||||||
|
import gc
|
||||||
|
import sys
|
||||||
|
import errno
|
||||||
|
from time import sleep_ms, localtime
|
||||||
|
from micropython import alloc_emergency_exception_buf
|
||||||
|
|
||||||
|
# constant definitions
|
||||||
|
_CHUNK_SIZE = const(1024)
|
||||||
|
_SO_REGISTER_HANDLER = const(20)
|
||||||
|
_COMMAND_TIMEOUT = const(300)
|
||||||
|
_DATA_TIMEOUT = const(100)
|
||||||
|
_DATA_PORT = const(13333)
|
||||||
|
|
||||||
|
# Global variables
|
||||||
|
ftpsockets = []
|
||||||
|
datasocket = None
|
||||||
|
client_list = []
|
||||||
|
verbose_l = 0
|
||||||
|
client_busy = False
|
||||||
|
# Interfaces: (IP-Address (string), IP-Address (integer), Netmask (integer))
|
||||||
|
|
||||||
|
_month_name = ("", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||||
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
|
||||||
|
|
||||||
|
|
||||||
|
class FTP_client:
|
||||||
|
|
||||||
|
def __init__(self, ftpsocket, local_addr):
|
||||||
|
self.command_client, self.remote_addr = ftpsocket.accept()
|
||||||
|
self.remote_addr = self.remote_addr[0]
|
||||||
|
self.command_client.settimeout(_COMMAND_TIMEOUT)
|
||||||
|
log_msg(1, "FTP Command connection from:", self.remote_addr)
|
||||||
|
self.command_client.setsockopt(socket.SOL_SOCKET,
|
||||||
|
_SO_REGISTER_HANDLER,
|
||||||
|
self.exec_ftp_command)
|
||||||
|
self.command_client.sendall("220 Hello, this is the {}.\r\n".format(sys.platform))
|
||||||
|
self.cwd = '/'
|
||||||
|
self.fromname = None
|
||||||
|
# self.logged_in = False
|
||||||
|
self.act_data_addr = self.remote_addr
|
||||||
|
self.DATA_PORT = 20
|
||||||
|
self.active = True
|
||||||
|
self.pasv_data_addr = local_addr
|
||||||
|
|
||||||
|
def send_list_data(self, path, data_client, full):
|
||||||
|
try:
|
||||||
|
for fname in uos.listdir(path):
|
||||||
|
data_client.sendall(self.make_description(path, fname, full))
|
||||||
|
except Exception as e: # path may be a file name or pattern
|
||||||
|
path, pattern = self.split_path(path)
|
||||||
|
try:
|
||||||
|
for fname in uos.listdir(path):
|
||||||
|
if self.fncmp(fname, pattern):
|
||||||
|
data_client.sendall(
|
||||||
|
self.make_description(path, fname, full))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def make_description(self, path, fname, full):
|
||||||
|
global _month_name
|
||||||
|
if full:
|
||||||
|
stat = uos.stat(self.get_absolute_path(path, fname))
|
||||||
|
file_permissions = ("drwxr-xr-x"
|
||||||
|
if (stat[0] & 0o170000 == 0o040000)
|
||||||
|
else "-rw-r--r--")
|
||||||
|
file_size = stat[6]
|
||||||
|
tm = stat[7] & 0xffffffff
|
||||||
|
tm = localtime(tm if tm < 0x80000000 else tm - 0x100000000)
|
||||||
|
if tm[0] != localtime()[0]:
|
||||||
|
description = "{} 1 owner group {:>10} {} {:2} {:>5} {}\r\n".\
|
||||||
|
format(file_permissions, file_size,
|
||||||
|
_month_name[tm[1]], tm[2], tm[0], fname)
|
||||||
|
else:
|
||||||
|
description = "{} 1 owner group {:>10} {} {:2} {:02}:{:02} {}\r\n".\
|
||||||
|
format(file_permissions, file_size,
|
||||||
|
_month_name[tm[1]], tm[2], tm[3], tm[4], fname)
|
||||||
|
else:
|
||||||
|
description = fname + "\r\n"
|
||||||
|
return description
|
||||||
|
|
||||||
|
def send_file_data(self, path, data_client):
|
||||||
|
buffer = bytearray(_CHUNK_SIZE)
|
||||||
|
mv = memoryview(buffer)
|
||||||
|
with open(path, "rb") as file:
|
||||||
|
bytes_read = file.readinto(buffer)
|
||||||
|
while bytes_read > 0:
|
||||||
|
data_client.write(mv[0:bytes_read])
|
||||||
|
bytes_read = file.readinto(buffer)
|
||||||
|
data_client.close()
|
||||||
|
|
||||||
|
def save_file_data(self, path, data_client, mode):
|
||||||
|
buffer = bytearray(_CHUNK_SIZE)
|
||||||
|
mv = memoryview(buffer)
|
||||||
|
with open(path, mode) as file:
|
||||||
|
bytes_read = data_client.readinto(buffer)
|
||||||
|
while bytes_read > 0:
|
||||||
|
file.write(mv[0:bytes_read])
|
||||||
|
bytes_read = data_client.readinto(buffer)
|
||||||
|
data_client.close()
|
||||||
|
|
||||||
|
def get_absolute_path(self, cwd, payload):
|
||||||
|
# Just a few special cases "..", "." and ""
|
||||||
|
# If payload start's with /, set cwd to /
|
||||||
|
# and consider the remainder a relative path
|
||||||
|
if payload.startswith('/'):
|
||||||
|
cwd = "/"
|
||||||
|
for token in payload.split("/"):
|
||||||
|
if token == '..':
|
||||||
|
cwd = self.split_path(cwd)[0]
|
||||||
|
elif token != '.' and token != '':
|
||||||
|
if cwd == '/':
|
||||||
|
cwd += token
|
||||||
|
else:
|
||||||
|
cwd = cwd + '/' + token
|
||||||
|
return cwd
|
||||||
|
|
||||||
|
def split_path(self, path): # instead of path.rpartition('/')
|
||||||
|
tail = path.split('/')[-1]
|
||||||
|
head = path[:-(len(tail) + 1)]
|
||||||
|
return ('/' if head == '' else head, tail)
|
||||||
|
|
||||||
|
# compare fname against pattern. Pattern may contain
|
||||||
|
# the wildcards ? and *.
|
||||||
|
def fncmp(self, fname, pattern):
|
||||||
|
pi = 0
|
||||||
|
si = 0
|
||||||
|
while pi < len(pattern) and si < len(fname):
|
||||||
|
if (fname[si] == pattern[pi]) or (pattern[pi] == '?'):
|
||||||
|
si += 1
|
||||||
|
pi += 1
|
||||||
|
else:
|
||||||
|
if pattern[pi] == '*': # recurse
|
||||||
|
if pi == len(pattern.rstrip("*?")): # only wildcards left
|
||||||
|
return True
|
||||||
|
while si < len(fname):
|
||||||
|
if self.fncmp(fname[si:], pattern[pi + 1:]):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
si += 1
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
if pi == len(pattern.rstrip("*")) and si == len(fname):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def open_dataclient(self):
|
||||||
|
if self.active: # active mode
|
||||||
|
data_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
data_client.settimeout(_DATA_TIMEOUT)
|
||||||
|
data_client.connect((self.act_data_addr, self.DATA_PORT))
|
||||||
|
log_msg(1, "FTP Data connection with:", self.act_data_addr)
|
||||||
|
else: # passive mode
|
||||||
|
data_client, data_addr = datasocket.accept()
|
||||||
|
log_msg(1, "FTP Data connection with:", data_addr[0])
|
||||||
|
return data_client
|
||||||
|
|
||||||
|
def exec_ftp_command(self, cl):
|
||||||
|
global datasocket
|
||||||
|
global client_busy
|
||||||
|
global my_ip_addr
|
||||||
|
|
||||||
|
try:
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
data = cl.readline().decode("utf-8").rstrip("\r\n")
|
||||||
|
|
||||||
|
if len(data) <= 0:
|
||||||
|
# No data, close
|
||||||
|
# This part is NOT CLEAN; there is still a chance that a
|
||||||
|
# closing data connection will be signalled as closing
|
||||||
|
# command connection
|
||||||
|
log_msg(1, "*** No data, assume QUIT")
|
||||||
|
close_client(cl)
|
||||||
|
return
|
||||||
|
|
||||||
|
if client_busy: # check if another client is busy
|
||||||
|
cl.sendall("400 Device busy.\r\n") # tell so the remote client
|
||||||
|
return # and quit
|
||||||
|
client_busy = True # now it's my turn
|
||||||
|
|
||||||
|
# check for log-in state may done here, like
|
||||||
|
# if self.logged_in == False and not command in\
|
||||||
|
# ("USER", "PASS", "QUIT"):
|
||||||
|
# cl.sendall("530 Not logged in.\r\n")
|
||||||
|
# return
|
||||||
|
|
||||||
|
command = data.split()[0].upper()
|
||||||
|
payload = data[len(command):].lstrip() # partition is missing
|
||||||
|
path = self.get_absolute_path(self.cwd, payload)
|
||||||
|
log_msg(1, "Command={}, Payload={}".format(command, payload))
|
||||||
|
|
||||||
|
if command == "USER":
|
||||||
|
# self.logged_in = True
|
||||||
|
cl.sendall("230 Logged in.\r\n")
|
||||||
|
# If you want to see a password,return
|
||||||
|
# "331 Need password.\r\n" instead
|
||||||
|
# If you want to reject an user, return
|
||||||
|
# "530 Not logged in.\r\n"
|
||||||
|
elif command == "PASS":
|
||||||
|
# you may check here for a valid password and return
|
||||||
|
# "530 Not logged in.\r\n" in case it's wrong
|
||||||
|
# self.logged_in = True
|
||||||
|
cl.sendall("230 Logged in.\r\n")
|
||||||
|
elif command == "SYST":
|
||||||
|
cl.sendall("215 UNIX Type: L8\r\n")
|
||||||
|
elif command in ("TYPE", "NOOP", "ABOR"): # just accept & ignore
|
||||||
|
cl.sendall('200 OK\r\n')
|
||||||
|
elif command == "QUIT":
|
||||||
|
cl.sendall('221 Bye.\r\n')
|
||||||
|
close_client(cl)
|
||||||
|
elif command == "PWD" or command == "XPWD":
|
||||||
|
cl.sendall('257 "{}"\r\n'.format(self.cwd))
|
||||||
|
elif command == "CWD" or command == "XCWD":
|
||||||
|
try:
|
||||||
|
if (uos.stat(path)[0] & 0o170000) == 0o040000:
|
||||||
|
self.cwd = path
|
||||||
|
cl.sendall('250 OK\r\n')
|
||||||
|
else:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
elif command == "PASV":
|
||||||
|
cl.sendall('227 Entering Passive Mode ({},{},{}).\r\n'.format(
|
||||||
|
self.pasv_data_addr.replace('.', ','),
|
||||||
|
_DATA_PORT >> 8, _DATA_PORT % 256))
|
||||||
|
self.active = False
|
||||||
|
elif command == "PORT":
|
||||||
|
items = payload.split(",")
|
||||||
|
if len(items) >= 6:
|
||||||
|
self.act_data_addr = '.'.join(items[:4])
|
||||||
|
if self.act_data_addr == "127.0.1.1":
|
||||||
|
# replace by command session addr
|
||||||
|
self.act_data_addr = self.remote_addr
|
||||||
|
self.DATA_PORT = int(items[4]) * 256 + int(items[5])
|
||||||
|
cl.sendall('200 OK\r\n')
|
||||||
|
self.active = True
|
||||||
|
else:
|
||||||
|
cl.sendall('504 Fail\r\n')
|
||||||
|
elif command == "LIST" or command == "NLST":
|
||||||
|
if payload.startswith("-"):
|
||||||
|
option = payload.split()[0].lower()
|
||||||
|
path = self.get_absolute_path(
|
||||||
|
self.cwd, payload[len(option):].lstrip())
|
||||||
|
else:
|
||||||
|
option = ""
|
||||||
|
try:
|
||||||
|
data_client = self.open_dataclient()
|
||||||
|
cl.sendall("150 Directory listing:\r\n")
|
||||||
|
self.send_list_data(path, data_client,
|
||||||
|
command == "LIST" or 'l' in option)
|
||||||
|
cl.sendall("226 Done.\r\n")
|
||||||
|
data_client.close()
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
if data_client is not None:
|
||||||
|
data_client.close()
|
||||||
|
elif command == "RETR":
|
||||||
|
try:
|
||||||
|
data_client = self.open_dataclient()
|
||||||
|
cl.sendall("150 Opened data connection.\r\n")
|
||||||
|
self.send_file_data(path, data_client)
|
||||||
|
# if the next statement is reached,
|
||||||
|
# the data_client was closed.
|
||||||
|
data_client = None
|
||||||
|
cl.sendall("226 Done.\r\n")
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
if data_client is not None:
|
||||||
|
data_client.close()
|
||||||
|
elif command == "STOR" or command == "APPE":
|
||||||
|
try:
|
||||||
|
data_client = self.open_dataclient()
|
||||||
|
cl.sendall("150 Opened data connection.\r\n")
|
||||||
|
self.save_file_data(path, data_client,
|
||||||
|
"wb" if command == "STOR" else "ab")
|
||||||
|
# if the next statement is reached,
|
||||||
|
# the data_client was closed.
|
||||||
|
data_client = None
|
||||||
|
cl.sendall("226 Done.\r\n")
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
if data_client is not None:
|
||||||
|
data_client.close()
|
||||||
|
elif command == "SIZE":
|
||||||
|
try:
|
||||||
|
cl.sendall('213 {}\r\n'.format(uos.stat(path)[6]))
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
elif command == "MDTM":
|
||||||
|
try:
|
||||||
|
tm=localtime(uos.stat(path)[8])
|
||||||
|
cl.sendall('213 {:04d}{:02d}{:02d}{:02d}{:02d}{:02d}\r\n'.format(*tm[0:6]))
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
elif command == "STAT":
|
||||||
|
if payload == "":
|
||||||
|
cl.sendall("211-Connected to ({})\r\n"
|
||||||
|
" Data address ({})\r\n"
|
||||||
|
" TYPE: Binary STRU: File MODE: Stream\r\n"
|
||||||
|
" Session timeout {}\r\n"
|
||||||
|
"211 Client count is {}\r\n".format(
|
||||||
|
self.remote_addr, self.pasv_data_addr,
|
||||||
|
_COMMAND_TIMEOUT, len(client_list)))
|
||||||
|
else:
|
||||||
|
cl.sendall("213-Directory listing:\r\n")
|
||||||
|
self.send_list_data(path, cl, True)
|
||||||
|
cl.sendall("213 Done.\r\n")
|
||||||
|
elif command == "DELE":
|
||||||
|
try:
|
||||||
|
uos.remove(path)
|
||||||
|
cl.sendall('250 OK\r\n')
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
elif command == "RNFR":
|
||||||
|
try:
|
||||||
|
# just test if the name exists, exception if not
|
||||||
|
uos.stat(path)
|
||||||
|
self.fromname = path
|
||||||
|
cl.sendall("350 Rename from\r\n")
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
elif command == "RNTO":
|
||||||
|
try:
|
||||||
|
uos.rename(self.fromname, path)
|
||||||
|
cl.sendall('250 OK\r\n')
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
self.fromname = None
|
||||||
|
elif command == "CDUP" or command == "XCUP":
|
||||||
|
self.cwd = self.get_absolute_path(self.cwd, "..")
|
||||||
|
cl.sendall('250 OK\r\n')
|
||||||
|
elif command == "RMD" or command == "XRMD":
|
||||||
|
try:
|
||||||
|
uos.rmdir(path)
|
||||||
|
cl.sendall('250 OK\r\n')
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
elif command == "MKD" or command == "XMKD":
|
||||||
|
try:
|
||||||
|
uos.mkdir(path)
|
||||||
|
cl.sendall('250 OK\r\n')
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
elif command == "SITE":
|
||||||
|
try:
|
||||||
|
exec(payload.replace('\0','\n'))
|
||||||
|
cl.sendall('250 OK\r\n')
|
||||||
|
except:
|
||||||
|
cl.sendall('550 Fail\r\n')
|
||||||
|
else:
|
||||||
|
cl.sendall("502 Unsupported command.\r\n")
|
||||||
|
# log_msg(2,
|
||||||
|
# "Unsupported command {} with payload {}".format(command,
|
||||||
|
# payload))
|
||||||
|
except OSError as err:
|
||||||
|
if verbose_l > 0:
|
||||||
|
log_msg(1, "Exception in exec_ftp_command:")
|
||||||
|
sys.print_exception(err)
|
||||||
|
if err.errno in (errno.ECONNABORTED, errno.ENOTCONN):
|
||||||
|
close_client(cl)
|
||||||
|
# handle unexpected errors
|
||||||
|
except Exception as err:
|
||||||
|
log_msg(1, "Exception in exec_ftp_command: {}".format(err))
|
||||||
|
# tidy up before leaving
|
||||||
|
client_busy = False
|
||||||
|
|
||||||
|
|
||||||
|
def log_msg(level, *args):
|
||||||
|
global verbose_l
|
||||||
|
if verbose_l >= level:
|
||||||
|
print(*args)
|
||||||
|
|
||||||
|
|
||||||
|
# close client and remove it from the list
|
||||||
|
def close_client(cl):
|
||||||
|
cl.setsockopt(socket.SOL_SOCKET, _SO_REGISTER_HANDLER, None)
|
||||||
|
cl.close()
|
||||||
|
for i, client in enumerate(client_list):
|
||||||
|
if client.command_client == cl:
|
||||||
|
del client_list[i]
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def accept_ftp_connect(ftpsocket, local_addr):
|
||||||
|
# Accept new calls for the server
|
||||||
|
try:
|
||||||
|
client_list.append(FTP_client(ftpsocket, local_addr))
|
||||||
|
except:
|
||||||
|
log_msg(1, "Attempt to connect failed")
|
||||||
|
# try at least to reject
|
||||||
|
try:
|
||||||
|
temp_client, temp_addr = ftpsocket.accept()
|
||||||
|
temp_client.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def num_ip(ip):
|
||||||
|
items = ip.split(".")
|
||||||
|
return (int(items[0]) << 24 | int(items[1]) << 16 |
|
||||||
|
int(items[2]) << 8 | int(items[3]))
|
||||||
|
|
||||||
|
|
||||||
|
def stop():
|
||||||
|
global ftpsockets, datasocket
|
||||||
|
global client_list
|
||||||
|
global client_busy
|
||||||
|
|
||||||
|
for client in client_list:
|
||||||
|
client.command_client.setsockopt(socket.SOL_SOCKET,
|
||||||
|
_SO_REGISTER_HANDLER, None)
|
||||||
|
client.command_client.close()
|
||||||
|
del client_list
|
||||||
|
client_list = []
|
||||||
|
client_busy = False
|
||||||
|
for sock in ftpsockets:
|
||||||
|
sock.setsockopt(socket.SOL_SOCKET, _SO_REGISTER_HANDLER, None)
|
||||||
|
sock.close()
|
||||||
|
ftpsockets = []
|
||||||
|
if datasocket is not None:
|
||||||
|
datasocket.close()
|
||||||
|
datasocket = None
|
||||||
|
|
||||||
|
|
||||||
|
# start listening for ftp connections on port 21
|
||||||
|
def start(port=21, verbose=0, splash=True):
|
||||||
|
global ftpsockets, datasocket
|
||||||
|
global verbose_l
|
||||||
|
global client_list
|
||||||
|
global client_busy
|
||||||
|
|
||||||
|
alloc_emergency_exception_buf(100)
|
||||||
|
verbose_l = verbose
|
||||||
|
client_list = []
|
||||||
|
client_busy = False
|
||||||
|
|
||||||
|
for interface in [network.AP_IF, network.STA_IF]:
|
||||||
|
wlan = network.WLAN(interface)
|
||||||
|
if not wlan.active():
|
||||||
|
continue
|
||||||
|
|
||||||
|
ifconfig = wlan.ifconfig()
|
||||||
|
addr = socket.getaddrinfo(ifconfig[0], port)
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
sock.bind(addr[0][4])
|
||||||
|
sock.listen(1)
|
||||||
|
sock.setsockopt(socket.SOL_SOCKET,
|
||||||
|
_SO_REGISTER_HANDLER,
|
||||||
|
lambda s : accept_ftp_connect(s, ifconfig[0]))
|
||||||
|
ftpsockets.append(sock)
|
||||||
|
if splash:
|
||||||
|
print("FTP server started on {}:{}".format(ifconfig[0], port))
|
||||||
|
|
||||||
|
datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
datasocket.bind(('0.0.0.0', _DATA_PORT))
|
||||||
|
datasocket.listen(1)
|
||||||
|
datasocket.settimeout(10)
|
||||||
|
|
||||||
|
def restart(port=21, verbose=0, splash=True):
|
||||||
|
stop()
|
||||||
|
sleep_ms(200)
|
||||||
|
start(port, verbose, splash)
|
||||||
|
|
||||||
|
|
||||||
|
start(splash=True)
|
107
scripts/libraries/uping.py
Normal file
107
scripts/libraries/uping.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
# µPing (MicroPing) for MicroPython
|
||||||
|
# copyright (c) 2018 Shawwwn <shawwwn1@gmail.com>
|
||||||
|
# License: MIT
|
||||||
|
|
||||||
|
# Internet Checksum Algorithm
|
||||||
|
# Author: Olav Morken
|
||||||
|
# https://github.com/olavmrk/python-ping/blob/master/ping.py
|
||||||
|
# @data: bytes
|
||||||
|
def checksum(data):
|
||||||
|
if len(data) & 0x1: # Odd number of bytes
|
||||||
|
data += b'\0'
|
||||||
|
cs = 0
|
||||||
|
for pos in range(0, len(data), 2):
|
||||||
|
b1 = data[pos]
|
||||||
|
b2 = data[pos + 1]
|
||||||
|
cs += (b1 << 8) + b2
|
||||||
|
while cs >= 0x10000:
|
||||||
|
cs = (cs & 0xffff) + (cs >> 16)
|
||||||
|
cs = ~cs & 0xffff
|
||||||
|
return cs
|
||||||
|
|
||||||
|
def ping(host, count=4, timeout=5000, interval=10, quiet=False, size=64):
|
||||||
|
import utime
|
||||||
|
import uselect
|
||||||
|
import uctypes
|
||||||
|
import usocket
|
||||||
|
import ustruct
|
||||||
|
import urandom
|
||||||
|
|
||||||
|
# prepare packet
|
||||||
|
assert size >= 16, "pkt size too small"
|
||||||
|
pkt = b'Q'*size
|
||||||
|
pkt_desc = {
|
||||||
|
"type": uctypes.UINT8 | 0,
|
||||||
|
"code": uctypes.UINT8 | 1,
|
||||||
|
"checksum": uctypes.UINT16 | 2,
|
||||||
|
"id": uctypes.UINT16 | 4,
|
||||||
|
"seq": uctypes.INT16 | 6,
|
||||||
|
"timestamp": uctypes.UINT64 | 8,
|
||||||
|
} # packet header descriptor
|
||||||
|
h = uctypes.struct(uctypes.addressof(pkt), pkt_desc, uctypes.BIG_ENDIAN)
|
||||||
|
h.type = 8 # ICMP_ECHO_REQUEST
|
||||||
|
h.code = 0
|
||||||
|
h.checksum = 0
|
||||||
|
h.id = urandom.randint(0, 65535)
|
||||||
|
h.seq = 1
|
||||||
|
|
||||||
|
# init socket
|
||||||
|
sock = usocket.socket(usocket.AF_INET, usocket.SOCK_RAW, 1)
|
||||||
|
sock.setblocking(0)
|
||||||
|
sock.settimeout(timeout/1000)
|
||||||
|
addr = usocket.getaddrinfo(host, 1)[0][-1][0] # ip address
|
||||||
|
sock.connect((addr, 1))
|
||||||
|
not quiet and print("PING %s (%s): %u data bytes" % (host, addr, len(pkt)))
|
||||||
|
|
||||||
|
seqs = list(range(1, count+1)) # [1,2,...,count]
|
||||||
|
c = 1
|
||||||
|
t = 0
|
||||||
|
n_trans = 0
|
||||||
|
n_recv = 0
|
||||||
|
finish = False
|
||||||
|
while t < timeout:
|
||||||
|
if t==interval and c<=count:
|
||||||
|
# send packet
|
||||||
|
h.checksum = 0
|
||||||
|
h.seq = c
|
||||||
|
h.timestamp = utime.ticks_us()
|
||||||
|
h.checksum = checksum(pkt)
|
||||||
|
if sock.send(pkt) == size:
|
||||||
|
n_trans += 1
|
||||||
|
t = 0 # reset timeout
|
||||||
|
else:
|
||||||
|
seqs.remove(c)
|
||||||
|
c += 1
|
||||||
|
|
||||||
|
# recv packet
|
||||||
|
while 1:
|
||||||
|
socks, _, _ = uselect.select([sock], [], [], 0)
|
||||||
|
if socks:
|
||||||
|
resp = socks[0].recv(4096)
|
||||||
|
resp_mv = memoryview(resp)
|
||||||
|
h2 = uctypes.struct(uctypes.addressof(resp_mv[20:]), pkt_desc, uctypes.BIG_ENDIAN)
|
||||||
|
# TODO: validate checksum (optional)
|
||||||
|
seq = h2.seq
|
||||||
|
if h2.type==0 and h2.id==h.id and (seq in seqs): # 0: ICMP_ECHO_REPLY
|
||||||
|
t_elasped = (utime.ticks_us()-h2.timestamp) / 1000
|
||||||
|
ttl = ustruct.unpack('!B', resp_mv[8:9])[0] # time-to-live
|
||||||
|
n_recv += 1
|
||||||
|
not quiet and print("%u bytes from %s: icmp_seq=%u, ttl=%u, time=%f ms" % (len(resp), addr, seq, ttl, t_elasped))
|
||||||
|
seqs.remove(seq)
|
||||||
|
if len(seqs) == 0:
|
||||||
|
finish = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
if finish:
|
||||||
|
break
|
||||||
|
|
||||||
|
utime.sleep_ms(1)
|
||||||
|
t += 1
|
||||||
|
|
||||||
|
# close
|
||||||
|
sock.close()
|
||||||
|
ret = (n_trans, n_recv)
|
||||||
|
not quiet and print("%u packets transmitted, %u packets received" % (n_trans, n_recv))
|
||||||
|
return (n_trans, n_recv)
|
113
scripts/libraries/utelnet.py
Normal file
113
scripts/libraries/utelnet.py
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
# Public domain code.
|
||||||
|
# Source https://github.com/cpopp/MicroTelnetServer
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import network
|
||||||
|
import uos
|
||||||
|
import errno
|
||||||
|
from uio import IOBase
|
||||||
|
|
||||||
|
last_client_socket = None
|
||||||
|
server_socket = None
|
||||||
|
|
||||||
|
# Provide necessary functions for dupterm and replace telnet control characters that come in.
|
||||||
|
class TelnetWrapper(IOBase):
|
||||||
|
def __init__(self, socket):
|
||||||
|
self.socket = socket
|
||||||
|
self.discard_count = 0
|
||||||
|
|
||||||
|
def readinto(self, b):
|
||||||
|
readbytes = 0
|
||||||
|
for i in range(len(b)):
|
||||||
|
try:
|
||||||
|
byte = 0
|
||||||
|
# discard telnet control characters and
|
||||||
|
# null bytes
|
||||||
|
while(byte == 0):
|
||||||
|
byte = self.socket.recv(1)[0]
|
||||||
|
if byte == 0xFF:
|
||||||
|
self.discard_count = 2
|
||||||
|
byte = 0
|
||||||
|
elif self.discard_count > 0:
|
||||||
|
self.discard_count -= 1
|
||||||
|
byte = 0
|
||||||
|
|
||||||
|
b[i] = byte
|
||||||
|
|
||||||
|
readbytes += 1
|
||||||
|
except (IndexError, OSError) as e:
|
||||||
|
if type(e) == IndexError or len(e.args) > 0 and e.args[0] == errno.EAGAIN:
|
||||||
|
if readbytes == 0:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return readbytes
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return readbytes
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
# we need to write all the data but it's a non-blocking socket
|
||||||
|
# so loop until it's all written eating EAGAIN exceptions
|
||||||
|
while len(data) > 0:
|
||||||
|
try:
|
||||||
|
written_bytes = self.socket.write(data)
|
||||||
|
data = data[written_bytes:]
|
||||||
|
except OSError as e:
|
||||||
|
if len(e.args) > 0 and e.args[0] == errno.EAGAIN:
|
||||||
|
# can't write yet, try again
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# something else...propagate the exception
|
||||||
|
raise
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.socket.close()
|
||||||
|
|
||||||
|
# Attach new clients to dupterm and
|
||||||
|
# send telnet control characters to disable line mode
|
||||||
|
# and stop local echoing
|
||||||
|
def accept_telnet_connect(telnet_server):
|
||||||
|
global last_client_socket
|
||||||
|
|
||||||
|
if last_client_socket:
|
||||||
|
# close any previous clients
|
||||||
|
uos.dupterm(None)
|
||||||
|
last_client_socket.close()
|
||||||
|
|
||||||
|
last_client_socket, remote_addr = telnet_server.accept()
|
||||||
|
print("Telnet connection from:", remote_addr)
|
||||||
|
last_client_socket.setblocking(False)
|
||||||
|
# dupterm_notify() not available under MicroPython v1.1
|
||||||
|
# last_client_socket.setsockopt(socket.SOL_SOCKET, 20, uos.dupterm_notify)
|
||||||
|
|
||||||
|
last_client_socket.sendall(bytes([255, 252, 34])) # dont allow line mode
|
||||||
|
last_client_socket.sendall(bytes([255, 251, 1])) # turn off local echo
|
||||||
|
|
||||||
|
uos.dupterm(TelnetWrapper(last_client_socket))
|
||||||
|
|
||||||
|
def stop():
|
||||||
|
global server_socket, last_client_socket
|
||||||
|
uos.dupterm(None)
|
||||||
|
if server_socket:
|
||||||
|
server_socket.close()
|
||||||
|
if last_client_socket:
|
||||||
|
last_client_socket.close()
|
||||||
|
|
||||||
|
# start listening for telnet connections on port 23
|
||||||
|
def start(port=23):
|
||||||
|
stop()
|
||||||
|
global server_socket
|
||||||
|
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
|
||||||
|
ai = socket.getaddrinfo("0.0.0.0", port)
|
||||||
|
addr = ai[0][4]
|
||||||
|
|
||||||
|
server_socket.bind(addr)
|
||||||
|
server_socket.listen(1)
|
||||||
|
server_socket.setsockopt(socket.SOL_SOCKET, 20, accept_telnet_connect)
|
||||||
|
|
||||||
|
for i in (network.AP_IF, network.STA_IF):
|
||||||
|
wlan = network.WLAN(i)
|
||||||
|
if wlan.active():
|
||||||
|
print("Telnet server started on {}:{}".format(wlan.ifconfig()[0], port))
|
Loading…
Reference in New Issue
Block a user