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