Unicode error for python echo server when "Ctrl+C" pressed - python-3.x

I have created python echo server. The echo server can be started using command
sudo python3.8 echo_server.py
The client can be connected to it using command
telnet localhost 5000
I have implemented some commands like time,exit and network (i.e. if you type 'time' command on telnet client, the server responds with system time.)
For "network" command contain 3 while loops (i.e 1 loop for selecting network type, 2 loop for selecting static\dynamic, 3 loop for configuring ssid/password for wifi settings).
Now while configuring ssid/pwd, I wanted to go back to main command prompt for telnet client (i.e. where we can enter time,network command) on press of "Ctrl+c". But I am not able to handle the "Ctrl+c" on the echo server.I am getting below exception when "Ctrl+c" is pressed on telnet client
Exception in thread MyThread:
Traceback (most recent call last):
File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
self.run()
File "echo_server.py", line 47, in run
tx_data = ""
File "/usr/lib/python3.8/codecs.py", line 322, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
Main Terminating...
Please find my echo_server.py file
import socket
import os
import os.path
import subprocess
import time
from subprocess import Popen, PIPE, STDOUT
from threading import Thread
from time import sleep
class MyThread(Thread):
def __init__(self, val):
''' Constructor. '''
Thread.__init__(self)
self.val = val
class Filter:
"""Substitute \n with \r\n in stream output to terminal."""
def __init__(self, file):
self.file = file
def write(self, data):
self.file.write(data.replace("\n", "\r\n"))
def run(self):
while True:
HOST = '' # Symbolic name meaning all available interfaces
PORT = 5000 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
s.bind((HOST, PORT))
s.listen(5)
except socket.error:
time.sleep(5)
continue
conn, addr = s.accept()
file = conn.makefile(mode="rw")
filtered_file =MyThread.Filter(file)
file.flush()
filtered_file.write("Enter 'help' to list all valid commands\n")
while True:
file.write("Command> ")
file.flush()
data = file.readline()
tx_data = ""
if not data: break
data = data.strip()
if not data:
continue
elif data == 'time':
f= os.popen('date')
date=f.read().strip()
tx_data = date + '\n'
elif data == "network":
while True:
file.write("1. WiFi\n")
file.flush()
file.write("2. Ethenet\n")
file.flush()
file.write("3. Exi\n")
file.flush()
file.write("Enter a choice:")
file.flush()
choice = file.readline()
choice = choice.strip()
if choice == "1":
while True:
file.write("1. DHCP\n")
file.flush()
file.write("2. Static\n")
file.flush()
file.write("3. Exit\n")
file.flush()
file.write("Enter a choice:")
file.flush()
subchoice = file.readline()
subchoice = choice.strip()
if subchoice == "1":
while True:
file.write("Enter ssid:")
file.flush()
ssid = file.readline()
ssid = ssid.strip()
file.write("Enter pwd:")
file.flush()
pwd = file.readline()
pwd = pwd.strip()
break
break
elif choice == "2":
break
elif choice == "3":
break
else:
break
elif data == 'help':
tx_data = '''Valid commands are as below:
Enter number against the command for execution
1. time
2. network
3. exit\n'''
elif data == 'exit':
break
else:
tx_data = "Unknown Command: " + data + \
"\nEnter 'help' for list of valid commands\n"
filtered_file.write(tx_data)
#print 'Closing connection with client'
file.close()
conn.close()
# Run following code when the program starts
if __name__ == '__main__':
# Declare objects of My Thread class
my_obj = MyThread(4)
my_obj.setName('MyThread')
# Start running the threads!
my_obj.start()
# Wait for the threads to finish...
my_obj.join()
print('Main Terminating...')
I am not sure how to handle "Ctrl+c" so that control is back at the prompt when we can enter command.
Please let me know if any one has any suggestion to resolve this.

Here is my version of your program:
# echo_server.py
import socket
import os
import os.path
import subprocess
import time
from subprocess import Popen, PIPE, STDOUT
from threading import Thread
from time import sleep
def getcommand(thisfile, thisprompt, stripped=True):
thisfile.write(thisprompt)
thisfile.flush()
command = thisfile.readline()[:-1]
# print(f"{len(command)} chars received") # just for debug
# for i in command:
# print(ord(i))
if stripped:
return command.strip()
return command
class MyThread(Thread):
def __init__(self, val):
''' Constructor. '''
Thread.__init__(self)
self.val = val
class Filter:
"""Substitute \n with \r\n in stream output to terminal."""
def __init__(self, file):
self.file = file
def write(self, data):
self.file.write(data.replace("\n", "\r\n"))
def run(self):
while True:
HOST = '' # Symbolic name meaning all available interfaces
PORT = 5000 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
s.bind((HOST, PORT))
s.listen(5)
except socket.error:
time.sleep(5)
continue
conn, addr = s.accept()
file = conn.makefile(mode="rw", encoding="UTF-8", errors="ignore")
filtered_file = MyThread.Filter(file)
file.flush()
filtered_file.write("Enter 'help' to list all valid commands\n")
while True:
data = getcommand(file, "Command> ", stripped=False)
tx_data = ""
if not data:
print("No Command!")
break # closing connection
data = data.strip()
if not data:
print("Just Spaces!")
continue
elif data == 'time':
f= os.popen('date')
date=f.read().strip()
tx_data = date + '\n'
elif data == "network":
while True:
file.write("1. WiFi\n")
file.flush()
file.write("2. Ethernet\n")
file.flush()
file.write("3. Exit\n")
file.flush()
choice = getcommand(file, "Enter a choice:")
if choice == "1":
while True:
file.write("1. DHCP\n")
file.flush()
file.write("2. Static\n")
file.flush()
file.write("3. Exit\n")
file.flush()
subchoice = getcommand(file, "Enter a choice:")
if subchoice == "1":
while True:
ssid = getcommand(file, "Enter ssid:")
pwd = getcommand(file, "Enter password:")
if ssid and pwd:
break
break
elif choice == "2":
break
elif choice == "3":
break
elif data == 'help':
tx_data = '''Valid commands are as below:
Enter number against the command for execution
1. time
2. network
3. exit\n'''
elif data == 'exit':
break
else:
tx_data = "Unknown Command: " + data + \
"\nEnter 'help' for list of valid commands\n"
filtered_file.write(tx_data)
print('Closing connection with client')
file.close()
conn.close()
# Run following code when the program starts
if __name__ == '__main__':
# Declare objects of My Thread class
my_obj = MyThread(4)
my_obj.setName('MyThread')
# Start running the threads!
my_obj.start()
# Wait for the threads to finish...
my_obj.join()
print('Main Terminating...')
I made a new function to handle the input of text from the terminal. This function strips the new line char appended by readline() and optionally strips leading and trailing blanks from the input string. This enables your choice to close the connection if the user enters no data, while keeping it open and repeating the prompt if s/he just enters whitespace.
I also used the 'errors' optional argument in conn.makefile(), to avoid the UnicodeDecodeError, and added a line to repeat the input of ssid and password if either is blank. I also removed the else clause when the main choice is "network", to let the user out only by choosing 3.
With this version, there's no need to use Ctrl-C (that also has a meaning as KeyboardInterrupt) and if the user entered it, it wouldn't disrupt the server anymore.
Now, if you press Ctrl-C in the client, you stop receiving the server's output, but if you blindly enter the "exit" choices you get out of the connection gracefully.

Because ctrl-c not a unicode character and _io.TextIOWrapper (your file variable) can't handle it in unicode mode.
You can initialize wrapper in non unicode mode
file = conn.makefile(mode="rw",encoding='latin-1')
This will not disconnect client by ctrl-c and you should handle all special characters by your self (because telnet will still send it)
I changed your code to handle decoder errors as is. This will disconnect client on ctrl-c
while True:
file.write("Command> ")
file.flush()
try:
data = file.readline()
except UnicodeDecodeError as e:
conn.close()
break;
tx_data = ""
#if not data: break <-- This will throw error data undefined
data = data.strip()
Actually better solution not to use text wrapper at all
data = conn.recv(1024)
if not data: break

you have to initialize wrapper in other encoding type, use encoding = "latin-1"

Related

Probelms: Recreating Netcat in Python3 using the Blackhat Python Book

help I am a beginner, I am running kali on AWS free tier, i am trying to recreate netcat using python. I was able to compile it but only the -h command works when i run it, its not listening(-l). please help. am adding some random text because its not letting me submit this question. i am using the book blackhat python and a you-tube series on it
import argparse
import socket
import shlex
import subprocess
import sys
import textwrap
import threading
def execute(cmd):
cmd = cmd.strip()
if not cmd:
return
output = subprocess.check_output(shlex.split(cmd),stderr=subprocess.STDOUT)
return output.decode()
# the engine of netcat
class Netcat:
def __init__(self,args,buffer=None):
self.args = args
self.buffer = buffer
self.socket = socket.socket(socket.AF_INET, sock.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
def run(self):
if self.args.listen:
self.listen()
else:
self.send()
#if we are not running a listener
def send(self):
self.socket.connect((self.args.target , self.args.port))
if self.buffer:
self.socket.send(self.buffer)
try:
while True:
recv_len = 1
response = ''
while recv_len:
data = self.socket.recv(4096)
recv_len = len(data)
response += data.decode()
if recv_len < 4096:
break
if response:
print(response)
buffer = input('>')
buffer += '\n'
self.socket.send(buffer.encode())
except KeyboardInterrupt:
print('User terminated.')
self.socket.close()
sys.exit()
#if we are running a listener
def listen(self):
self.socket.bind((self.args.target, self.args.port))
self.socket.listen(5)
while True:
client_socket, _ = self.socket.accept()
client_threading = threading.Thread(target=self.handle,args=(client_socket,))
client_threading.start()
def handle(self, client_socket):
if self.args.execute:
output = execute(self.args.execute)
client_socket.send(output.encode())
#if argument for file upload is passed
elif self.args.upload:
file_buffer = b''
while True:
data = client_socket.recv(4096)
if data:
file_buffer += data
print(len(file_buffer))
else:
break
with open(self.args.upload, 'wb') as f:
f.write(file_buffer)
message = f'Saved file {self.args.upload}'
client_socket.send(message.encode())
elif self.args.command:
cmd_buffer = b''
while True:
try:
client.socket.send(b' #> ' )
while '\n' not in cmd_buffer.decode():
cmd_buffer += client_socket.recv(64)
response = execute(cmd_buffer.decode())
if response:
client_socket.send(response.encode())
cmd_buffer = b''
except Exception as e:
print(f'server killed {e}')
self.socket.close()
sys.exit()
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='BHP Net Tool',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=textwrap.dedent('''Example:
netcat.py -t 192.16.1.108 -p 5555 -l -c #command shell
netcat.py -t 192.16.1.108 -p 5555 -l -u=mytest.text #uplod file
netcat.py -t 192.16.1.108 -p 5555 -l -e=\"cat /etc/passwd\" #execute command
echo 'ABC' | ./netcat.py -t 192.16.1.108 -p 135 #echo text to server port 135
netcat.py -t 192.16.1.108 -p 5555 #connect to server
'''))
parser.add_argument('-c', '--command', action='store_true', help='command shell')
parser.add_argument('-e', '--execute', help='execute specified command')
parser.add_argument('-l', '--listen', action='store_true', help='listen')
parser.add_argument('-p', '--port', type=int,default=5555, help='specified port')
parser.add_argument('-t', '--target', default='192.168.1.203', help='specified IP')
parser.add_argument('-u', '--upload', help='upload file')
args = parser.parse_args()
if args.listen:
buffer = ''
else:
buffer = sys.stdin.read()
nc = NetCat(args, buffer.encode())
You don't call the run function at any point of the script.
Also here you made a simple syntax error:
[...]
elif self.args.command:
cmd_buffer = b''
while True:
try:
client.socket.send(b' #> ' )
while '\n' not in cmd_buffer.decode():
cmd_buffer += client_socket.recv(64)
response = execute(cmd_buffer.decode())
if response:
client_socket.send(response.encode())
cmd_buffer = b''
except Exception as e:
print(f'server killed {e}')
self.socket.close()
sys.exit()
It's client_socket.send(b' #> ' ) not client.socket.send(b' #> ' ).
Hope I've been helpful!

Python CMD2 Interrupt printing promp

I am using CMD2 to create a server. I have broken my code down to the smallest bit of code that still produces the problem.
import socket
import _thread
from cmd2 import *
def grab_data(conn):
while True:
try:
data = conn.recv(1024)
print(data)
except:
print("disconnected.")
break
def grab_client(sock):
while True:
conn, addr = sock.accept()
print("New connection")
_thread.start_new_thread(grab_data, (conn,))
def start_conn(ip, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((ip, int(port)))
sock.listen(10)
print("Socket listening")
_thread.start_new_thread(grab_client, (sock,))
class CLI(Cmd):
def __init__(self):
Cmd.__init__(self)
self.prompt = "Prompt> "
def do_listen(self, arg):
start_conn('0.0.0.0', '4446')
def emptyline(self):
pass
def do_quit(self, arg):
return True
cli = CLI()
cli.cmdloop("Welcome to the server.")
The issue I run into is when a client connects. It does not reprint the prompt. It hangs at a empty line with just the cursor. I am not sure how to get the prompt to print back.
You're blocking trying to read 1024 bytes, so it's got to wait until that entire buffer is filled. To do proper line-based buffering is a bit tricky, but a simple (albeit non-performant) implementation is to read a character at a time and check if it's a newline:
line = ""
while True:
data = conn.recv(1)
line += data
if data in "\n\r":
break
(This is not great code, but let's see if that solves the problem and we can improve it.)

How to recieve files from a server in python?

I Have made a python script, where you can send files to a remote server and receive them. I can send them to the server but can not seem to retrieve them back I've attached both server and client.py
Server.py
import socket # Import socket module
import os
s = socket.socket()
s.bind(('139.59.173.187', 8010)) # Binds port and IP address
s.listen(3) #wait for client to join
c, addr = s.accept()
def RecvFile():
print ("File is comming ....")
file = c.recv(1024) #recieves file name from client
print(file)
f = open(file,'wb') #open that file or create one
l = c.recv(4096) #now recieves the contents of the file
while (l):
print ("Receiving...File Data")
f.write(l) #save input to file
l = c.recv(4096) #get again until done
print("The file",file,"has been succesfully saved to the server\nConnection from:",addr)
f.close()
def SendFile():
file = c.recv(1024)
print(file)
with open(file,'rb') as f:
c.sendall(f.read())
print("File has been sent to the client:", addr)
main()
def main():
option = str(c.recv(1024), 'utf-8')
print(option)
if option[:1] == "R":
SendFile()
elif option[:2] == "S":
RecvFile()
main()
s.close()
Client.py
import time
import socket
import sys
import urllib.request
#import paramiko
def login():
a = 1
while a == 1:
global user
user = input("Username:")
passw = input("Password:")
with open('User.txt') as f:
for line in f.readlines():
us, pw = line.strip().split("|", 1)
if (user == us) and (passw == pw):
print("login Successful!")
a = a+1
main()
return
print("Incorrect details, Try again!")
def register():
print("You will recieve a generated Username from your Name and age")
regName = input("First Name: ")
regAge = input("Age: ")
regPass = input("Password: ")
regUser = (regName[0:3]+regAge)
with open('User.txt', 'a') as file:
file.writelines(regUser+"|"+regPass+"\n")
print("You have Succesfully registered")
print("Your Username is:"+regUser)
print("Your Password is:"+regPass)
print("!Please keep these credentials secure!")
def main():
print("=========================Welcome to Bluetooth Projector=========================")
option = input("Here are your options:\n--> Press C to connect to the projector\n--> Press F for File System")
x = 1
while x == 1:
if option == "C" or option == "c":
optionc(x)
elif option == "F" or "f":
File_System()
def optionc():
print("Loading.....")
time.sleep(2)
print("Obtaining IP addres.....")
time.sleep(2)
print("Connecting.....")
time.sleep(3)
print("Connected")
x = x+1
def File_System():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('139.59.173.187',8010))
print("=========================Welcome to the File System =========================")
option = input("Here are your options:\n--> Press S to upload files\n--> Press R to retrieve files")
if option == "S" or option == "s":
s.send("S".encode('utf-8'))
file_name = input("Please input the file name\n>>>")
name = input("What would you like it to be saved as?\n>>>")
s.send(name.encode('utf-8'))
with open(file_name,'rb') as f:
s.sendall(f.read())
print("File has been sent to the server")
main()
elif option == "R" or option == "r":
s.send("R".encode('utf-8'))
filename = input("What is the filename you wish to recieve\n>>>")
s.send(filename.encode('utf-8'))
print ("File is comming ....")
f = open(filename,'wb') #open that file or create one
l = s.recv(1024) #now recieves the contents of the file
while (l):
print ("Receiving...File Data")
f.write(l) #save input to file
l = s.recv(1024)
print("The file",filename,"has been succesfully saved to your computer")
f.close()
while True:
check = input("Do you already have an account? Y/N \n ")
if check == "Y" or check == "y":
login()
break
elif check == "N" or check == "n":
register()
break
else:
print("pleae enter either Y or N")
Please look at the Filesystem() in the client file. I think it is receiving the second wave of bytes when getting files that is the problem
an was error is either broken pipe which I fixed.
But now the actually receiving file loop doesn't go through and stops getting the file bytes. I believe this is my error. I am confused however since my code is pretty much identical to sending the file to the server which has no complications.
But now the actually receiving file loop doesn't go through and stops getting the file bytes. I believe this is my error.
You are right. The client gets no indication that there aren't more file contents to come, and thus keeps on waiting in recv forever. To rectify this, you can let the server shut down the connection when the file has been sent, or, if you want the connection to be kept, inform the client about when it has to stop receiving, e. g. by sending it the file length beforehand.

Send and recieve files in python

So, I am making something in my computer science class where you can send files to a remote server and that works, but cant recv them back.
SERVER.PY
import socket # Import socket module
import os #imports os module
def RecvFile():
while True: # allows it to always save files and not have to be restarted.
c, addr = s.accept()
print ('Connection Accepted From',addr)# this prints out connection and port
print ("File is comming ....")
file = c.recv(1024) #recieves file name from client
f = open(file,'wb') #open that file or create one
l = c.recv(1024) #now recieves the contents of the file
while (l):
print ("Receiving...File Data")
f.write(l) #save input to file
l = c.recv(1024) #get again until done
print("The file",file,"has been succesfully saved to the server\nConnection from:",addr)
f.close()
def SendFile():
c, addr = s.accept()
print("Connection Accepted From",addr)
filename = c.recv(1024)
if os.path.isfile(filename):
s.send("EXISTS"+str(os.pthat.getsize(filename)))
response = c.recv(1024)
if response[:2] == 'OK':
with open(filename, 'rb') as f:
s.sendall(f.read)
f.close()
else:
s.send("ERR")
s.close()
def main():
s = socket.socket()
s.bind(('139.59.173.187', 8000)) # Binds port and IP address
s.listen(3)
print("Server Started")
while True:
c, addr = s.accept()
option = c.recv(1024)
if option == "S":
RecvFile()
elif option == "R":
SendFile()
else:
s.send("Incorrect input".encode('utf-8'))
main()
#if errors occur
c.close()
I think the server.py is the problem
Client .py
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('139.59.173.187',8000))
print("=========================Welcome to the File System =========================")
option = input("Here are your options:\n--> Press S to upload files\n--> Press R to retrieve files")
if option == "S" or option == "s":
s.send("S".encode('utf-8'))
file_name = input("Please input the file name\n>>>")
name = input("What would you like it to be saved as?\n>>>")
s.send(name.encode('utf-8'))
with open(file_name,'rb') as f:
s.sendall(f.read())
print("File has been sent to the server")
main()
elif option == "R" or option == "r":
s.send("R".encode('utf-8'))
filename = input("What is the filename you wish to recieve\n>>>")
s.send(filename.encode('utf-8'))
print ("File is comming ....")
f = open(filename,'wb') #open that file or create one
l = c.recv(1024) #now recieves the contents of the file
while (l):
print ("Receiving...File Data")
f.write(l) #save input to file
l = c.recv(1024) #get again until done
print("The file",filename,"has been succesfully saved to your computer")
f.close()
I have put 7 hours into learning this and the asignment is due in friday. rather than saying the code could you comment how it works etc.
In server.py, SendFile and RecvFile both use a socket object s that they have no access to. I recommend that you pass it as an argument :
def RecvFile(s):
#...
def SendFile(s):
#...
And then in main :
if option == "S":
RecvFile(s)
elif option == "R":
SendFile(s)

Python 2.7: How do I read single keypress in Linux (Including special characters)?

I have seen a lot of questions asked about how to read single keypress in python. For Windows, the answers say to use the msvcrt module, which works. But for Linux, they use something like this:
import termios, fcntl, sys, os
def kbhit():
fd = sys.stdin.fileno()
oldterm = termios.tcgetattr(fd)
newattr = termios.tcgetattr(fd)
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)
oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
try:
while True:
try:
c = sys.stdin.read(1)
return True
except IOError:
return False
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
or this:
def __init__(self):
import tty, sys
def __call__(self):
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
getch = _Getch()
The problem with those two is that they don't read special characters like cntrl-C (^C) or Arrow Keys. Is there a Linux way to read single keypress including special characters like arrow keys?
Check this piece of code:
def AllKeys(NormalInput=1):
""" Detect Key Input """
import termios, sys, tty
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
tty.setraw(fd)
tty.setcbreak(fd) # Control + C
try:
#while True:
ch = sys.stdin.read(NormalInput)
if ch == '\x1b':
ch = ch.lstrip(' ')
ch += sys.stdin.read(2)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
In my case it works as intended! It detect ctrl + 'char'
Just check this simple example of UDP client:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
try:
InKey = AllKeys()
print InKey
if InKey == "\x03":
raise KeyboardInterrupt
elif InKey == '~':
break
else:
sock.sendto(InKey, (UDP_IP, UDP_PORT))
except KeyboardInterrupt:
break
sock.shutdown()
sock.close()
You can expand this easily with a server that process the characters received for virtual keyboard input with Uinput (Virtual HID Device). Then secure it with port control using Iptables rules. Add ssh tunneling for security. Otherwise you'll see plain data traffic in an ARP Cache Poisoning (MITM) testing.

Resources