Python socket sending old data - python

I'm having a hard time in Python with subprocess and the socket module. What's happening is, I'm sending the command to the client, but it doesn't send back anything. Until, I send another command, and I get the output of the previous. I've tried all the buffer sizes and none work.
SERVER:
while True:
shell = input(">> ")
conn.send(shell.encode())
data = conn.recv(1600)
print(data.decode())
CLIENT:
while True:
data = sock.recv(1600)
if not data: break
data = data.decode()
commd = subprocess.Popen(data, stdout=subprocess.PIPE, shell=True)
out, err = commd.communicate()
sock.send(out)
print("Exiting because no data")

Related

Python netcat not returning command shell

The code below is meant to copy the features of netcat for instances where netcat is removed from a server but python is not. However, no matter what I try I can't seem to figure out the following problem:
I run the following
./Netcat.py -l -p 9999 -c
followed by
./Netcat.py -t localhost -p 9999
in a separate terminal. I can confirm that, when acting as a server the script does, indeed, receive a connection from the second instance of the script and that it receives data when it is set (upon pressing CTRL+D). However, I then get a hung terminal which does not receive a command prompt back, nor does it have the ability to send more data. I am hoping someone can point out the error at this point.
What should happen is as follows:
spin up server insatance
run script as a client
type some data and close STDIN with CTRL+D at which point the client sends the data to the server
The server should then receive the data and send back a command prompt to the client
The problem is at step 4 and I'm pulling my hair out at this point.
Edit
Having run strace I determined that the client program gets hung up waiting to receive data which I have noted the corresponding line in the code. I do not see why this would be the case.
import sys # used for accessing command line args
import socket # creation of socket objects to listen & send/receive data
import getopt # helps scripts to parse the command line arguments in sys.argv
import concurrent.futures # for running commands in a subshell
import subprocess # The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes.
## Globals ##
listen = False
command = False
target = ""
port = 0
## END GLOBALS ##
def client_sender(buffer):
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
client.connect((target, port))
if len(buffer):
# bytes([source[, encoding[, errors]]])
client.send(bytes(buffer, 'utf-8'))
# continue sending and receiving data until user kills script
while True:
recv_len = 1
response = ''
while recv_len:
data = client.recv(4096) #<-- PROBLEM
recv_len = len(data)
response += data.decode('utf-8')
if recv_len < 4096:
break
print(response)
buffer = input('#: ')
buffer += '\n'
client.send(buffer)
except socket.error as e:
print('[*] Exception! Exiting')
print(e)
client.close()
def server_loop():
global target
global port
# if no target is defined, listen on all interfaces
if not len(target):
target = '0.0.0.0'
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((target, port))
server.listen(5)
print(f'listening on {target}:{port}')
while True:
client_socket, addr = server.accept()
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
executor.submit(client_handler, client_socket)
def run_command(command):
command = command.rstrip()
# run command & retrieve output
try:
output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
except:
return 'Failed to execute command.\r\n'
def client_handler(client_socket):
global command
# check if shell requested
if command:
while True:
client_socket.send('<BHP:#> ')
# receive until linefeed
cmd_buffer = ''
while '\n' not in cmd_buffer:
cmd_buffer += client_socket.recv(1024)
response = run_command(bufffer)
client_socket.send(response)
def main():
global listen
global port
global command
global target
# make sure the user provided options & arguments
if not len(sys.argv[1:]):
usage()
# parse commandline options
try:
opts, args = getopt.getopt(sys.argv[1:],"lt:p:c", #: succeeds options which expect an argument
['listen', 'target', 'port', 'command'])
except getopt.GetoptError as err:
print(str(err))
usage()
# handle commandline options
for option, argument in opts:
elif option in ('-l', '--listen'):
listen = True
elif option in ('-e', '--execute'):
execute = argument
elif option in ('-c', '--commandshell'):
command = True
elif option in ('-t', '--target'):
target = argument
elif option in ('-p', '--port'):
port = int(argument)
# not listening; sending data from stdin
if not listen and len(target) and port > 0:
buffer = sys.stdin.read()
client_sender(buffer)
if listen:
server_loop()
if __name__ == '__main__':
main()

Python connect socket to process

I have a (very) simple web server I wrote in C and I want to test it. I wrote it so it takes data on stdin and sends out on stdout. How would I connect the input/output of a socket (created with socket.accept()) to the input/output of a process created with subprocess.Popen?
Sounds simple, right? Here's the killer: I'm running Windows.
Can anyone help?
Here's what I've tried:
Passing the client object itself as stdin/out to subprocess.Popen. (It never hurts to try.)
Passing socket.makefile() results as stdin/out to subprocess.Popen.
Passing the socket's file number to os.fdopen().
Also, in case the question was unclear, here's a slimmed-down version of my code:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('', PORT))
sock.listen(5)
cli, addr = sock.accept()
p = subprocess.Popen([PROG])
#I want to connect 'p' to the 'cli' socket so whatever it sends on stdout
#goes to the client and whatever the client sends goes to its stdin.
#I've tried:
p = subprocess.Popen([PROG], stdin = cli.makefile("r"), stdout = cli.makefile("w"))
p = subprocess.Popen([PROG], stdin = cli, stdout = cli)
p = subprocess.Popen([PROG], stdin = os.fdopen(cli.fileno(), "r"), stdout = os.fdopen(cli.fileno(), "w"))
#but all of them give me either "Bad file descriptor" or "The handle is invalid".
I had the same issue and tried the same way to bind the socket, also on windows. The solution I came out with was to share the socket and bind it on the process to stdin and stdout. My solutions are completely in python but I guess that they are easily convertible.
import socket, subprocess
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('', PORT))
sock.listen(5)
cli, addr = sock.accept()
process = subprocess.Popen([PROG], stdin=subprocess.PIPE)
process.stdin.write(cli.share(process.pid))
process.stdin.flush()
# you can now use `cli` as client normally
And in the other process:
import sys, os, socket
sock = socket.fromshare(os.read(sys.stdin.fileno(), 372))
sys.stdin = sock.makefile("r")
sys.stdout = sock.makefile("w")
# stdin and stdout now write to `sock`
The 372 is the len of a measured socket.share call. I don't know if this is constant, but it worked for me. This is possible only in windows, as the share function is only available on that OS.

Python client-server script hangs until I press [enter]

I have a basic client-server script in Python using sockets. The server binds to a specific port and waits for a client connection. When a client connects, they are presented with a raw_input prompt that sends the entered commands to a subproc on the server and pipes the output back to the client.
Sometimes when I execute commands from the client, the output will hang and not present me with the raw_input prompt until I press the [enter] key.
At first I thought this might have been a buffer problem but it happens when I use commands with a small output, like 'clear' or 'ls', etc.
The client code:
import os, sys
import socket
from base64 import *
import time
try:
HOST = sys.argv[1]
PORT = int(sys.argv[2])
except IndexError:
print("You must specify a host IP address and port number!")
print("usage: ./handler_client.py 192.168.1.4 4444")
sys.exit()
socksize = 4096
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
server.connect((HOST, PORT))
print("[+] Connection established!")
print("[+] Type ':help' to view commands.")
except:
print("[!] Connection error!")
sys.exit(2)
while True:
data = server.recv(socksize)
cmd = raw_input(data)
server.sendall(str(cmd))
server.close()
Server code:
import os,sys
import socket
import time
from subprocess import Popen,PIPE,STDOUT,call
HOST = ''
PORT = 4444
socksize = 4096
activePID = []
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn.bind((HOST, PORT))
conn.listen(5)
print("Listening on TCP port %s" % PORT)
def reaper():
while activePID:
pid,stat = os.waitpid(0, os.WNOHANG)
if not pid: break
activePID.remove(pid)
def handler(connection):
time.sleep(3)
while True:
cmd = connection.recv(socksize)
proc = Popen(cmd,
shell=True,
stdout=PIPE,
stderr=PIPE,
stdin=PIPE,
)
stdout, stderr = proc.communicate()
if cmd == ":killme":
connection.close()
sys.exit(0)
elif proc:
connection.send( stdout )
connection.send("\nshell => ")
connection.close()
os._exit(0)
def accept():
while 1:
global connection
connection, address = conn.accept()
print "[!] New connection!"
connection.send("\nshell => ")
reaper()
childPid = os.fork() # forks the incoming connection and sends to conn handler
if childPid == 0:
handler(connection)
else:
activePID.append(childPid)
accept()
The problem I see is that the final loop in the client only does one server.recv(socksize), and then it calls raw_input(). If that recv() call does not obtain all of the data sent by the server in that single call, then it also won't collect the prompt that follows the command output and therefore won't show that next prompt. The uncollected input will sit in the socket until you enter the next command, and then it will be collected and shown. (In principle it could take many recv() calls to drain the socket and get to the appended prompt, not just two calls.)
If this is what's happening then you would hit the problem if the command sent back more than one buffer's worth (4KB) of data, or if it generated output in small chunks spaced out in time so that the server side could spread that data over multiple sends that are not coalesced quickly enough for the client to collect them all in a single recv().
To fix this, you need have the client do as many recv() calls as it takes to completely drain the socket. So you need to come up with a way for the client to know that the socket has been drained of everything that the server is going to send in this interaction.
The easiest way to do this is to have the server add boundary markers into the data stream and then have the client inspect those markers to discover when the final data from the current interaction has been collected. There are various ways to do this, but I'd probably have the server insert a "this is the length of the following chunk of data" marker ahead of every chunk it sends, and send a marker with a length of zero after the final chunk.
The client-side main loop then becomes:
forever:
read a marker;
if the length carried in the marker is zero then
break;
else
read exactly that many bytes;.
Note that the client must be sure to recv() the complete marker before it acts on it; stuff can come out of a stream socket in lumps of any size, completely unrelated to the size of the writes that sent that stuff into the socket at the sender's side.
You get to decide whether to send the marker as variable-length text (with a distinctive delimiter) or as fixed-length binary (in which case you have to worry about endian issues if the client and server can be on different systems). You also get to decide whether the client should show each chunk as it arrives (obviously you can't use raw_input() to do that) or whether it should collect all of the chunks and show the whole thing in one blast after the final chunk has been collected.

Python sending command over a socket

I'm having a bit of trouble. I want to create a simple program that connects to the server and executes a command using subprocess then returns the result to the client. It's simple but I can't get it to work. Right now this is what I have:
client:
import sys, socket, subprocess
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = sys.argv[1]
port = int(sys.argv[2])
socksize = 1024
conn.connect((host, port))
while True:
shell = raw_input("$ ")
conn.send(shell)
data = conn.recv(socksize)
#msglen = len(data)
output = data
iotype = subprocess.PIPE
cmd = ['/bin/sh', '-c', shell]
proc = subprocess.Popen(cmd, stdout=iotype).wait()
stdout,stderr = proc.communicate()
conn.send(stdout)
print(output)
if proc.returncode != 0:
print("Error")
server:
import sys, socket, subprocess
host = ''
port = 50106
socksize = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
print("Server started on port: %s" %port)
s.listen(1)
print("Now listening...\n")
conn, addr = s.accept()
while True:
print 'New connection from %s:%d' % (addr[0], addr[1])
data = conn.recv(socksize)
cmd = ['/bin/sh', '-c', data]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE).wait()
stdout,stderr = cmd.communicate()
if not data:
break
elif data == 'killsrv':
sys.exit()
Danger, Will Robinson!!!
Do you really want to send commands in clear text without authentication over the network? It is very, very dangerous.
Do it over SSH with paramiko.
Alright I've heard this answer too many times. I don't want to use SSH I'm just building it to learn more about sockets. I'm not going to actually use this if I want to send commands to a system. – AustinM
There is no way I could infer this noble quest from your question. :-)
The sockets module is a thin layer over the posix library; plain sockets is tedious and hard to get right. As of today (2014), asynchronous I/O and concurrency are not among Python's strongest traits - 3.4 is starting to change that but libraries will lag behind for a while. My advice is to spent your time learning some higher level API like Twisted (twistedmatrix.com/trac). If you are really interested in the low level stuff, dive in the project source.
Alright. Any idea on how I could use twisted for this type of thing? – AustinM
Look at twistedmatrix.com/documents/current/core/examples/#auto2
Well I can understand your frustration Austin; I was in the same boat. However trial and error at last worked out. Hopefully you were looking for this:
print "Command is:",command
op = subprocess.Popen(command, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
if op:
output=str(op.stdout.read())
print "Output:",output
conn.sendall(output)
else:
error=str(op.stderr.read())
print "Error:",error
conn.sendall(error)
It's unclear why you are using subprocess.Popen() for the same command in both the client and the server. Here's an outline of what I would try to do (pseudocode):
client
while True:
read command from user
send command to server
wait for and then read response from server
print response to user
server
while True:
wait for and then read command from client
if command is "killsrv", exit
execute command and capture output
send output to client
The problem with your code is this line (in both client and server):
proc = subprocess.Popen(cmd, stdout=iotype).wait()
stdout,stderr = proc.communicate()
You are calling wait on the Popen object, which means that the variable proc is getting an int (returned by wait) instead of a Popen object. You can just get rid of the wait -- since communicate waits for the process to end before returning, and you aren't checking the exit code anyway, you don't need to call it.
Then, in your client, I don't think you even need the subprocess calls, unless you're running some command that the server is sending back.

Send output from a python server back to client

I now have a small java script server working correctly, called by:
<?php
$handle = fsockopen("udp://78.129.148.16",12345);
fwrite($handle,"vzctlrestart110");
fclose($handle);
?>
On a remote server the following python server is running and executing the comand's
#!/usr/bin/python
import os
import socket
print " Loading Bindings..."
settings = {}
line = 0
for each in open('/root/actions.txt', 'r'):
line = line + 1
each = each.rstrip()
if each != "":
if each[0] != '#':
a = each.partition(':')
if a[2]:
settings[a[0]] = a[2]
else:
print " Err # line",line,":",each
print " Starting Server...",
port = 12345
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", port))
print "OK."
print " Listening on port:", port
while True:
datagram = s.recv(1024)
if not datagram:
break
print "Rx Cmd:", datagram
if settings.has_key(datagram):
print "Launch:", settings[datagram]
os.system(settings[datagram]+" &")
s.close()
Is it possible to easily send the output of the command back, when the server is started and is running a command the output is shown in the ssh window, however I want this output to be sent back to the browser of the original client, maybe setting the browser to wait for 15 seconds and then check for any data received via the socket.
I know I am asking quite a lot, however I am creating a PHP script which I have a large knowledge about, however my python knowledge lacks greatly.
Thanks,
Ashley
Yes, you can read the output of the command. For this I would recommend the Python subprocess module. Then you can just s.write() it back.
Naturally this has some implications, you would probably have to let your PHP script run for a while longer since the process may be running slow.
# The pipe behaves like a file object in Python.
process = Popen(cmd, shell=True, stdout=PIPE)
process_output = ""
while process.poll():
process_output += process.stdout.read(256)
s.write(process_output)
# Better yet.
process = Popen(cmd, shell=true, stdout=PIPE)
stdout, stderr = process.communicate() # will read and wait for process to end.
s.write(stdout)
Integrated into your code:
# ... snip ...
import subprocess
con, addr = s.accept()
while True:
datagram = con.recv(1024)
if not datagram:
break
print "Rx Cmd:", datagram
if settings.has_key(datagram):
print "Launch:", settings[datagram]
process = subprocess.Popen(settings[datagram]+" &", shell=True, stdout=subprocess.PIPE)
stdout, stderr = process.communicate()
con.send(stdout)
con.close()
s.close()
Here's an example of how to get the output of a command:
>>> import commands
>>> s = commands.getoutput("ls *")
>>> s
'client.py'

Categories