# -*- coding: utf-8 -*-
"""
pync - arbitrary TCP and UDP connections and listens (Netcat for Python).
"""
from __future__ import unicode_literals
import argparse
import contextlib
import errno
import io
import itertools
import logging
import multiprocessing
import os
import platform
import random
import select
import shlex
import socket
import subprocess
import sys
import threading
import time
try:
import msvcrt
_mswindows = True
except ImportError:
_mswindows = False
try:
# py2
import Queue as queue
except ImportError:
# py3
import queue
import socks
from .argparsing import GroupingArgumentParser
from . import compat
from .conin import NonBlockingConsoleInput as NetcatConsoleInput
from .process import (
NonBlockingPopen, ProcessTerminated,
PythonProcess, PythonStdinWriter, PythonStdoutReader,
)
# For handling Process error.
try:
# py3
FileNotFoundError
except NameError:
# py2
FileNotFoundError = IOError
TOSKEYWORDS = dict(
af11=0x28,
af12=0x30,
af21=0x38,
af22=0x50,
af23=0x58,
af31=0x68,
af32=0x70,
af33=0x78,
af41=0x88,
af42=0x90,
af43=0x98,
critical=0xa0,
cs0=0x00,
cs1=0x20,
cs2=0x40,
cs3=0x60,
cs4=0x80,
cs5=0xa0,
cs6=0xc0,
cs7=0xe0,
ef=0xb8,
inetcontrol=0xc0,
lowcost=0x02,
lowdelay=0x10,
netcontrol=0xe0,
reliability=0x04,
throughput=0x08,
)
PIPE = subprocess.PIPE
STDOUT = subprocess.STDOUT
QUEUE = -3
def _debug(s):
sys.__stderr__.write(s+'\n')
sys.__stderr__.flush()
def _readwrite_close(nc):
# For NetcatContext().start() method.
try:
nc.readwrite()
finally:
nc.close()
class NetcatError(Exception):
def __init__(self, msg, *args):
super(NetcatError, self).__init__(msg, *args)
self.msg = msg
def __str__(self):
return str(self.msg)
class NetcatSocketError(NetcatError):
def __init__(self, socket_err, *args):
msg = str(socket_err)
super(NetcatSocketError, self).__init__(msg, socket_err, *args)
self.socket_err = socket_err
class NetcatProxyError(NetcatError):
def __init__(self, proxy_err, *args):
msg = str(proxy_err)
if proxy_err.socket_err is not None:
msg = str(proxy_err.socket_err)
super(NetcatProxyError, self).__init__(msg, proxy_err, *args)
self.proxy_err = proxy_err
class NetcatIOBase(object):
def read(self, n):
raise io.UnsupportedOperation
def write(self, data):
raise io.UnsupportedOperation
class NetcatIO(NetcatIOBase):
Reader = None
Writer = None
def __init__(self, reader=None, writer=None):
self.reader = reader
self.writer = writer
if self.reader is None and self.Reader is not None:
self.reader = self.Reader()
if self.writer is None and self.Writer is not None:
self.writer = self.Writer()
def read(self, n):
return self.reader.read(n)
def write(self, data):
return self.writer.write(data)
class NetcatStdinReader(NetcatIOBase):
def __getattr__(self, name):
return getattr(sys.stdin, name)
def __eq__(self, other):
return other == sys.stdin
def read(self, n):
return sys.stdin.read(n)
class NetcatStdoutWriter(NetcatIOBase):
def __getattr__(self, name):
return getattr(sys.stdout, name)
def __eq__(self, other):
return other == sys.stdout
def write(self, data):
return sys.stdout.write(data)
class NetcatStderrWriter(NetcatIOBase):
def __getattr__(self, name):
return getattr(sys.stderr, name)
def __eq__(self, other):
return other == sys.stderr
def write(self, data):
return sys.stderr.write(data)
class NetcatPipeIOBase(NetcatIOBase):
def __init__(self, conn):
self.connection = conn
self._fileno = self.connection.fileno()
if _mswindows:
self._fileno = msvcrt.open_osfhandle(self._fileno, os.O_TEXT)
super(NetcatPipeIOBase, self).__init__()
def fileno(self):
return self._fileno
def poll(self):
raise io.UnsupportedOperation
def send_bytes(self, data):
raise io.UnsupportedOperation
def recv_bytes(self):
raise io.UnsupportedOperation
def close(self):
self.connection.close()
class NetcatPipeReader(NetcatPipeIOBase):
def poll(self):
return self.connection.poll()
def recv_bytes(self):
return self.connection.recv_bytes()
def read(self, n):
if self.poll():
return self.recv_bytes()
class NetcatPipeWriter(NetcatPipeIOBase):
def send_bytes(self, data):
return self.connection.send_bytes(data)
def write(self, data):
self.send_bytes(data)
if not data:
self.close()
class NetcatPipeIO(NetcatIO):
Reader = NetcatPipeReader
Writer = NetcatPipeWriter
def __init__(self):
recv_conn, send_conn = multiprocessing.Pipe(False)
reader = self.Reader(recv_conn)
writer = self.Writer(send_conn)
super(NetcatPipeIO, self).__init__(reader, writer)
class NetcatQueueIOBase(NetcatIOBase):
def __init__(self, q):
self.queue = q
super(NetcatQueueIOBase, self).__init__()
def get_nowait(self):
raise io.UnsupportedOperation
def put(self, data):
raise io.UnsupportedOperation
class NetcatQueueReader(NetcatQueueIOBase):
def get_nowait(self):
return self.queue.get_nowait()
def read(self, n):
try:
return self.get_nowait()
except queue.Empty:
pass
class NetcatQueueWriter(NetcatQueueIOBase):
def put(self, data):
return self.queue.put(data)
def write(self, data):
self.put(data)
class NetcatQueueIO(NetcatIO):
Reader = NetcatQueueReader
Writer = NetcatQueueWriter
def __init__(self):
q = multiprocessing.Queue()
reader = self.Reader(q)
writer = self.Writer(q)
super(NetcatQueueIO, self).__init__(reader, writer)
class NetcatFileIOBase(NetcatIOBase):
def __init__(self, f):
self.file = f
try:
self._fileno = self.fileno()
except (AttributeError, io.UnsupportedOperation):
self._fileno = None
super(NetcatFileIOBase, self).__init__()
def fileno(self):
return self.file.fileno()
def poll(self):
raise io.UnsupportedOperation
class NetcatFileReader(NetcatFileIOBase):
def __init__(self, f):
super(NetcatFileReader, self).__init__(f)
self.__poll_fileno = True
self.__read_fileno = True
def read(self, n):
if self.poll():
if self.__read_fileno:
try:
return self._read_fileno(n)
except OSError as e:
if e.errno != errno.EBADF:
raise
except TypeError:
pass
self.__read_fileno = False
return self._read_file(n)
def poll(self):
if self.__poll_fileno:
try:
return self._poll_fileno()
except (OSError, TypeError):
self.__poll_fileno = False
return self._poll_file()
def _read_fileno(self, n):
return os.read(self._fileno, n)
def _read_file(self, n):
return self.file.read(n)
def _poll_file(self):
return True
def _poll_fileno(self):
readables, _, _ = select.select([self._fileno], [], [], 0)
if self._fileno in readables:
return True
return False
class NetcatFileWriter(NetcatFileIOBase):
def __init__(self, f):
super(NetcatFileWriter, self).__init__(f)
self.__write_fileno = True
def write(self, data):
if self.__write_fileno:
try:
self._write_fileno(data)
except OSError as e:
if e.errno != errno.EBADF:
raise
except TypeError:
pass
else:
self.flush()
return
self.__write_fileno = False
self._write_file(data)
self.flush()
def _write_file(self, data):
self.file.write(data)
def _write_fileno(self, data):
os.write(self._fileno, data)
def flush(self):
self.file.flush()
class NetcatFileIO(NetcatIO):
Reader = NetcatFileReader
Writer = NetcatFileWriter
def __init__(self, *args, **kwargs):
raise NotImplementedError
class NetcatConsoleWriter(NetcatFileWriter):
def __init__(self):
super(NetcatConsoleWriter, self).__init__(sys.stdout)
class NetcatContext(object):
D = False
v = False
stdin = sys.stdin
stdout = sys.stdout
stderr = sys.stderr
def __init__(self,
D=None,
v=None,
stdin=None, stdout=None, stderr=None, **kwargs):
if D is not None:
self.D = D
if v is not None:
self.v = v
self.stdin = stdin or self.stdin
self.stdout = stdout or self.stdout
self.stderr = stderr or self.stderr
if isinstance(self.stdin, NetcatIO):
self._stdin = self.stdin.reader
self.stdin = self.stdin.writer
elif isinstance(self.stdin, NetcatIOBase):
self._stdin = self.stdin
self.stdin = None
elif self.stdin is sys.stdin:
if self.stdin.isatty():
self._stdin = NetcatConsoleInput()
else:
self._stdin = NetcatFileReader(NetcatStdinReader())
self.stdin = None
elif self.stdin == PIPE:
pipe = NetcatPipeIO()
self._stdin = pipe.reader
self.stdin = pipe.writer
elif self.stdin == QUEUE:
q = NetcatQueueIO()
self._stdin = q.reader
self.stdin = q.writer
else:
self._stdin = NetcatFileReader(self.stdin)
self.stdin = None
if isinstance(self.stdout, NetcatIO):
self._stdout = self.stdout.writer
self.stdout = self.stdout.reader
elif isinstance(self.stdout, NetcatIOBase):
self._stdout = self.stdout
self.stdout = None
elif self.stdout is sys.stdout:
self._stdout = NetcatFileWriter(NetcatStdoutWriter())
self.stdout = None
elif self.stdout == PIPE:
pipe = NetcatPipeIO()
self._stdout = pipe.writer
self.stdout = pipe.reader
elif self.stdout == QUEUE:
q = NetcatQueueIO()
self._stdout = q.writer
self.stdout = q.reader
else:
self._stdout = NetcatFileWriter(self.stdout)
self.stdout = None
if isinstance(self.stderr, NetcatIO):
self._stderr = self.stderr.writer
self.stderr = self.stderr.reader
if isinstance(self.stderr, NetcatIOBase):
self._stderr = self.stderr
self.stderr = None
elif self.stderr is sys.stderr:
self._stderr = NetcatFileWriter(NetcatStderrWriter())
self.stderr = None
elif self.stderr == PIPE:
pipe = NetcatPipeIO()
self._stderr = pipe.writer
self.stderr = pipe.reader
elif self.stderr == QUEUE:
q = NetcatQueueIO()
self._stderr = q.writer
self.stderr = q.reader
elif self.stderr == STDOUT:
self._stderr = self._stdout
self.stderr = self.stdout
else:
self._stderr = NetcatFileWriter(self.stderr)
self.stderr = None
self._init_kwargs(**kwargs)
def _init_kwargs(self, **kwargs):
"""
Override this to parse and initialize
any unknown keyword arguments
"""
if kwargs:
raise ValueError(kwargs)
def __enter__(self):
return self
def __exit__(self, *args, **kwargs):
self.close()
def run(self):
try:
self.readwrite()
finally:
self.close()
def __start(self, daemon=False):
raise NotImplementedError
try:
return self.start_process(daemon=daemon)
except:
return self.start_thread(daemon=daemon)
def start_process(self, daemon=False):
p = multiprocessing.Process(
target=self.run,
daemon=daemon,
)
p.start()
if isinstance(self._stdout, NetcatPipeWriter):
self._stdout.close()
return p
def start_thread(self, daemon=False):
t = threading.Thread(
target=self.run,
daemon=daemon,
)
t.start()
return t
def close(self):
"""
Override to add any cleanup code.
"""
pass
def _print_message(self, message, file=None):
if message:
if file is None:
file = self._stderr
try:
file.write(message+'\n')
except TypeError:
file.write(message.encode()+b'\n')
file.flush()
def print_verbose(self, message):
if self.v:
self._print_message(message, file=self._stderr)
def print_debug(self, message):
if self.D:
self._print_message(message, file=self._stderr)
[docs]class NetcatConnection(NetcatContext):
"""
Wraps a socket object to provide Netcat-like functionality.
:param q: Quit the readwrite loop after EOF on stdin and delay of secs.
:type q: int, optional
You can use sub-classes of this class as a context manager using the "with" statement:
.. code-block:: python
with NetcatConnection(...) as nc:
nc.readwrite()
If you choose not to use the "with" statement, please make sure to use
the close() method after use:
.. code-block:: python
nc = NetcatConnection(...)
nc.readwrite()
nc.close()
"""
C = False
d = False
i = 0
q = 0
w = None
plen = 2048
def __init__(self, net, C=None, d=None, i=None, q=None, w=None, **kwargs):
super(NetcatConnection, self).__init__(**kwargs)
self.net = net
self.dest, self.port = self._getpeername(net)
if C is not None:
self.C = C
if d is not None:
self.d = d
if i is not None:
self.i = i
if q is not None:
self.q = q
if w is not None:
self.w = w
[docs] @classmethod
def connect(cls, dest, port, **kwargs):
"""
Factory method to connect to a server and return a NetcatConnection
instance.
This method should be implemented by a sub-class.
:param dest: The destination hostname or IP address to connect to.
:type dest: str
:param port: The port number to connect to.
:type port: int
:param kwargs: Any other keyword arguments get passed to __init__.
:returns: Returns a subclass of :class:`pync.NetcatConnection` once
a connection has been established.
:rtype: :class:`pync.NetcatConnection`
:Example:
.. code-block:: python
with NetcatConnection.connect('localhost', 8000) as conn:
conn.readwrite()
"""
raise NotImplementedError
[docs] @classmethod
def listen(cls, dest, port, **kwargs):
"""
Factory method to listen for a connection and return a NetcatConnection
instance.
This method should be implemented by a sub-class.
:param dest: The hostname or IP address to bind to.
:type dest: str
:param port: The port number to bind to.
:type port: int
:param kwargs: Any other keyword arguments get passed to __init__.
:returns: Returns a subclass of :class:`pync.NetcatConnection` once a
connection has been established.
:rtype: :class:`pync.NetcatConnection`
:Example:
.. code-block:: python
with NetcatConnection.listen('localhost', 8000) as conn:
conn.readwrite()
"""
raise NotImplementedError
@property
def timeout(self):
return self.w
def _getpeername(self, sock):
try:
# IPv4
dest, port = sock.getpeername()
except ValueError:
# IPv6
dest, port, _, _ = sock.getpeername()
return dest, port
def recv(self, n, blocking=True):
if blocking:
return self.net.recv(n)
try:
can_read, _, _ = select.select([self.net], [], [], 0)
except ValueError:
return self.net.recv(n)
if self.net in can_read:
return self.net.recv(n)
def send(self, data):
self.net.sendall(data)
[docs] def close(self):
super(NetcatConnection, self).close()
self.net.close()
def shutdown(self, how):
try:
return self.net.shutdown(how)
except (socket.error, OSError):
pass
def shutdown_rd(self):
self.shutdown(socket.SHUT_RD)
def shutdown_wr(self):
self.shutdown(socket.SHUT_WR)
[docs] def readwrite(self):
"""
The main loop to read and write i_o.
Read from stdin and send to network.
Receive from network and write to stdout.
Write verbose/debug/error messages to stderr.
This loop is based on the netcat-openbsd 1.105-7 ubuntu version.
:Example:
.. code-block:: python
with NetcatConnection(sock) as nc:
nc.readwrite()
"""
netin_eof, stdin_eof = False, None
time_now, time_sleep = time.time, time.sleep
last_io, plen = time_now(), self.plen
carriage_return, quit_eof = self.C, self.q
i, timeout = self.i, self.timeout
net_send, net_recv = self.send, self.recv
net_shutdown_rd, net_shutdown_wr = self.shutdown_rd, self.shutdown_wr
stdin_detach = self.d
stdin_read = self._stdin.read
stdout_write = self._stdout.write
#stdout_flush = self._stdout.flush
try:
while not netin_eof:
sleep = True
if i:
time_sleep(i)
# netin
try:
net_data = net_recv(plen, blocking=False)
except (socket.error, OSError):
return
if net_data:
# stdout
stdout_write(net_data)
#stdout_flush()
last_io, sleep = time_now(), False
elif net_data is not None:
# netin EOF
stdout_write(b'')
net_shutdown_rd()
netin_eof = True
# stdin
if not stdin_detach:
try:
stdin_data = stdin_read(plen)
except EOFError:
stdin_data = b''
# netout
if stdin_data:
if carriage_return:
stdin_data = stdin_data.replace(b'\n', b'\r\n')
try:
net_send(stdin_data)
except socket.error as e:
if e.errno != errno.EPIPE:
# Not a broken pipe.
raise
# Broken pipe.
# netin connection lost
return
last_io, sleep = time_now(), False
elif stdin_data is not None:
# stdin EOF
if not stdin_eof:
stdin_eof = time_now()
# If the user asked to exit on EOF, do it
if quit_eof == 0:
net_shutdown_wr()
#self._stdin.close()
# If the user asked to die after a while, arrange for it
if quit_eof > 0:
stdin_eof_elapsed = time_now() - stdin_eof
if stdin_eof_elapsed >= quit_eof:
return
if timeout is not None:
idle_time_elapsed = time_now() - last_io
if idle_time_elapsed >= timeout:
return
if sleep:
time_sleep(.001)
except NetcatStopReadWrite:
# IO has requested to stop the readwrite loop.
pass
[docs]class NetcatTCPConnection(NetcatConnection):
"""
Wraps a TCP socket to provide Netcat-like functionality.
"""
[docs] @classmethod
def connect(cls, dest, port, **kwargs):
"""
Factory method to connect to a TCP server and return a
:class:`pync.NetcatTCPConnection` object.
:param dest: The destination hostname or IP address to connect to.
:type dest: str
:param port: The port number to connect to.
:type port: int
:param kwargs: Any other keyword arguments get passed to __init__.
:rtype: :class:`pync.NetcatTCPConnection`
:Example:
.. code-block:: python
from pync import NetcatTCPConnection
with NetcatTCPConnection.connect('localhost', 8000) as conn:
conn.readwrite()
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((dest, port))
return cls(sock, **kwargs)
[docs] @classmethod
def listen(cls, dest, port, **kwargs):
"""
Factory method to listen for an incoming TCP connection and
return a :class:`pync.NetcatTCPConnection` object.
:param dest: The destination hostname or IP address to bind to.
:type dest: str
:param port: The port number to bind to.
:type port: int
:param kwargs: Any other keyword arguments get passed to __init__.
:rtype: :class:`pync.NetcatTCPConnection`
:Example:
.. code-block:: python
from pync import NetcatTCPConnection
with NetcatTCPConnection.listen('localhost', 8000) as conn:
conn.readwrite()
"""
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_sock.bind((dest, port))
server_sock.listen(1)
# ctrl-c interrupt doesn't seem to break out of server accept.
# So using select for non-blocking server accept.
while True:
readables, _, _ = select.select([server_sock], [], [], .002)
if server_sock in readables:
sock, _ = server_sock.accept()
break
server_sock.close()
return cls(sock, **kwargs)
[docs]class NetcatUDPConnection(NetcatConnection):
"""
Wraps a UDP socket object to provide Netcat-like functionality.
"""
[docs] @classmethod
def connect(cls, dest, port, **kwargs):
"""
:TODO:
"""
# TODO
raise NotImplementedError
[docs] @classmethod
def listen(cls, dest, port, **kwargs):
"""
:TODO:
"""
raise NotImplementedError
def recv(self, *args, **kwargs):
try:
return super(NetcatUDPConnection, self).recv(*args, **kwargs)
except (socket.error, compat.ConnectionRefusedError):
raise NetcatStopReadWrite
class ConnectionRefused(Exception):
'''
Same as ConnectionRefusedError but passes back the
dest and port of the refused connection.
'''
def __init__(self, dest, port):
self.dest = dest
self.port = port
class NetcatIterator(NetcatContext):
'''
Base class for Netcat clients and servers.
NetcatClients can iterate through one or more ports
and NetcatServers can accept one or more connections.
'''
Connection = None
c = None
e = None
T = None
y = None
Y = None
allow_reuse_port = True
def __init__(self, c=None, e=None, T=None, y=None, Y=None,
*args, **kwargs):
super(NetcatIterator, self).__init__(*args, **kwargs)
if c is not None:
self.c = c
if e is not None:
self.e = e
if T is not None:
self.T = T
if y is not None:
self.Y = Y
self._proc = None
def _init_kwargs(self, **kwargs):
self._conn_kwargs = kwargs
def _init_connection(self, sock):
inout = dict(
stdin=NetcatIO(reader=self._stdin, writer=self.stdin),
stdout=NetcatIO(reader=self.stdout, writer=self._stdout),
stderr=NetcatIO(reader=self.stderr, writer=self._stderr),
)
proc = None
if self.c:
cmd = self.c
sh = True
try:
proc = NetcatPopen(cmd,
shell=sh,
stdin=PIPE,
stdout=PIPE,
stderr=STDOUT,
)
except (FileNotFoundError, OSError) as e:
raise NetcatError(str(e))
elif self.e:
cmd = shlex.split(self.e)
sh = False
try:
proc = NetcatPopen(cmd,
shell=sh,
stdin=PIPE,
stdout=PIPE,
stderr=STDOUT,
)
except (FileNotFoundError, OSError) as e:
raise NetcatError(str(e))
elif self.y:
code = self.y
proc = NetcatPythonProcess(code)
elif self.Y:
filename = self.Y
proc = NetcatPythonProcess.from_file(filename)
if proc is not None:
inout.update(stdin=proc.stdout, stdout=proc.stdin)
self._proc = proc
self._conn_kwargs.update(inout)
return self.Connection(sock, **self._conn_kwargs)
def __iter__(self):
return self.iter_connections()
def __next__(self):
return self.next_connection()
@property
def tos(self):
''' Returns IP TOS integer value. '''
T = self.T
if T in TOSKEYWORDS:
T = TOSKEYWORDS[T]
return int(T)
def iter_connections(self):
''' Override in subclass
Iterate through and yield each connection.
Close each connection before moving on to the next.
'''
raise NotImplementedError
def next_connection(self):
''' Override in subclass
Return the next NetcatConnection.
'''
raise NotImplementedError
def readwrite(self):
for conn in self:
conn.readwrite()
def _set_common_sockopts(self, sock):
if self.allow_reuse_port:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if self.b:
sock.setsockopt(socket.IPPROTO_TCP, socket.SO_BROADCAST, 1)
if self.D:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_DEBUG, 1)
if self.T:
sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, self.tos)
if self.I:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, self.I)
if self.O:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, self.O)
def _getaddrinfo(self, addr, port):
# Used to raise socket error on bad address.
try:
return socket.getaddrinfo(
addr, port,
self.address_family, 0, 0, self.flags,
)
except socket.error as e:
raise NetcatSocketError(e)
def close(self):
super(NetcatIterator, self).close()
if self._proc is not None:
self._proc.close()
[docs]class NetcatClient(NetcatIterator):
"""
A Netcat client is iterable.
You can pass one or more ports and iterate through
each :class:`pync.NetcatConnection`.
:param dest: The destination hostname or IP address to connect to.
:type dest: str
:param port: The port number(s) to connect to.
:type port: int, list(int)
:param e: Execute a command upon connection.
:type e: str, optional
:param z: Set to True to turn Zero i_o on (connect then close).
Useful for simple port scanning.
:type z: bool, optional
You can use sub-classes of this class as a context manager using the "with" statement:
.. code-block:: python
with NetcatClient(...) as nc:
nc.readwrite()
If you choose not to use the "with" statement, please make sure to use
the close() method after use:
.. code-block:: python
nc = NetcatClient(...)
nc.readwrite()
nc.close()
:Example:
.. code-block:: python
:caption: We can connect to multiple ports one after another by passing
a list of ports.
with NetcatClient('localhost', [8000, 8001]) as nc:
for connection in nc:
connection.readwrite()
.. code-block:: python
:caption: Using the "z" and "v" options, we can perform a simple port scan.
with NetcatClient('localhost', [8000, 8002], z=True, v=True) as nc:
nc.readwrite()
"""
protocol_name = ''
address_family = socket.AF_INET
socket_type = None
v_conn_succeeded = 'Connection to {dest} {port} port [{proto_name}/{proto}] succeeded!'
v_conn_refused = 'connect to {dest} port {port} ({proto_name}) failed: Connection refused'
_4 = True
_6 = False
b = False
c = None
D = False
e = None
I = None
n = False
O = None
P = None
p = 0
r = False
s = ''
w = None
X = '5'
x = None
y = None
Y = None
z = False
def __init__(self, dest, port,
_4=None, _6=None, b=None, c=None, D=None, e=None, I=None,
n=None, O=None, P=None, p=None, r=None, s=None, w=None,
X=None, x=None, y=None, Y=None, z=None, **kwargs):
super(NetcatClient, self).__init__(**kwargs)
self.dest, self.port = dest, port
if _4 is not None:
self._4 = _4
if _6 is not None:
self._6 = _6
if b is not None:
self.b = b
if c is not None:
self.c = c
if D is not None:
self.D = D
if e is not None:
self.e = e
if I is not None:
self.I = I
if n is not None:
self.n = n
if O is not None:
self.O = O
if p is not None:
self.p = p
if r is not None:
self.r = r
if s is not None:
self.s = s
if w is not None:
self.w = w
if X is not None:
self.X = X
if x is not None:
self.x = x
if y is not None:
self.y = y
if Y is not None:
self.Y = Y
if z is not None:
self.z = z
self._conn_kwargs['w'] = self.w
if isinstance(self.port, int):
# Only one port passed, wrap it in a list
# for the __iter__ function.
self.port = [self.port]
if self.r:
self.port = list(self.port)
random.shuffle(self.port)
self._iterports = iter(self.port)
if self._6:
self.address_family = socket.AF_INET6
self.flags = 0
if self.n:
self.flags = socket.AI_NUMERICHOST
@property
def proxy_protocol(self):
protocols = {
'5': socks.SOCKS5,
'4': socks.SOCKS4,
'connect': socks.HTTP
}
return protocols[self.X]
@property
def proxy_address(self):
return self.x.split(':', 1)[0]
@property
def proxy_port(self):
defaults = {
'5': 1080,
'4': 1080,
'connect': 3128,
}
try:
port = self.x.split(':', 1)[1]
except IndexError:
port = defaults[self.X]
try:
port = int(port)
except (TypeError, ValueError):
port = repr(port)
return port
@property
def timeout(self):
return self.w
[docs] def iter_connections(self):
while True:
try:
nc_conn = self.next_connection()
except StopIteration:
# No more ports to connect to.
# Exit loop
return
except ConnectionRefused:
# Move onto next connection if any errors.
continue
try:
if not self.z:
yield nc_conn
finally:
nc_conn.close()
def _conn_refused(self, port, dest=None):
if dest is None:
dest = self.dest
self.print_verbose(
self.v_conn_refused.format(
dest=dest, port=port,
proto_name=self.protocol_name,
),
)
raise ConnectionRefused(self.dest, port)
[docs] def next_connection(self):
# This will raise StopIteration when no more ports.
port = next(self._iterports)
try:
nc_conn = self._create_connection((self.dest, port))
except compat.ConnectionRefusedError:
self._conn_refused(port)
except socks.ProxyError as e:
if e.socket_err and e.socket_err.errno == errno.ECONNREFUSED:
self._conn_refused(self.proxy_port,
dest=self.proxy_address,
)
raise NetcatProxyError(e)
except socket.error as e:
if e.errno != errno.ECONNREFUSED:
raise NetcatSocketError(e)
self._conn_refused(port)
else:
self._conn_succeeded(port)
if self.z:
# If zero io mode, close the connection.
nc_conn.close()
return nc_conn
def _conn_succeeded(self, port, dest=None):
if dest is None:
dest = self.dest
proto = '*'
if not self.n:
try:
proto = socket.getservbyport(port, self.protocol_name)
except (socket.error, OSError):
pass
self.print_verbose(
self.v_conn_succeeded.format(
dest=dest,
port=port,
proto_name=self.protocol_name,
proto=proto,
),
)
def _create_connection(self, addr):
dest, port = addr
addrinfo = self._getaddrinfo(dest, port)
sock = self._client_init()
self._client_bind(sock)
self._client_connect(sock, addr)
nc_conn = self._init_connection(sock)
return nc_conn
def _client_init(self):
if self.x:
# proxy socket
addrinfo = self._getaddrinfo(self.proxy_address, self.proxy_port)
s = socks.socksocket(self.address_family, self.socket_type)
s.set_proxy(
proxy_type=self.proxy_protocol,
addr=self.proxy_address,
port=self.proxy_port,
username=self.P,
)
return s
return socket.socket(self.address_family, self.socket_type)
def _client_bind(self, sock):
self._set_common_sockopts(sock)
if self.s or self.p:
source = self.s or None
port = self.p or None
addrinfo = self._getaddrinfo(source, port)
sock.bind((self.s, self.p))
def _client_connect(self, sock, addr):
if self.timeout:
sock.settimeout(self.timeout)
sock.connect(addr)
sock.settimeout(None)
[docs]class NetcatTCPClient(NetcatClient):
"""
A :class:`pync.NetcatClient` for the Transmission Control Protocol.
"""
protocol_name = 'tcp'
Connection = NetcatTCPConnection
socket_type = socket.SOCK_STREAM
[docs]class NetcatUDPClient(NetcatClient):
"""
A :class:`pync.NetcatClient` for the User Datagram Protocol.
"""
protocol_name = 'udp'
Connection = NetcatUDPConnection
socket_type = socket.SOCK_DGRAM
udp_scan_timeout = 3
def _client_connect(self, sock, addr):
super(NetcatUDPClient, self)._client_connect(sock, addr)
self._udptest(sock)
def _udptest(self, sock):
for i in compat.range(2):
sock.sendall(b'X')
timeout = self.timeout
if timeout is None:
timeout = self.udp_scan_timeout
# Give the remote host some time to reply.
for i in compat.range(0, timeout):
time.sleep(1)
sock.sendall(b'X')
[docs]class NetcatServer(NetcatIterator):
"""
A Netcat server is iterable.
You can iterate through each incoming connection.
:param port: The port number to bind the server to.
:type port: int
:param dest: The hostname or IP address to bind the server to.
:type dest: str, optional
:param e: Execute a command upon connection.
:type e: str, optional
:param k: Set to True to keep the server open between connections.
:type k: bool, optional
:param kwargs: Any other keyword arguments get passed to each
connection.
You can use sub-classes of this class as a context manager using the "with" statement:
.. code-block:: python
with NetcatServer(...) as nc:
nc.readwrite()
If you don't use the "with" statement, please make sure to use the
close() method after use:
.. code-block:: python
nc = NetcatServer(...)
nc.readwrite()
nc.close()
:Example:
.. code-block:: python
:caption: Use the "k" option to keep the server open and iterate
through each :class:`pync.NetcatConnection`.
with NetcatServer(8000, dest='localhost', k=True) as nc:
for connection in nc:
connection.readwrite()
"""
protocol_name = ''
address_family = socket.AF_INET
socket_type = None
v_listening = 'Listening on [{dest}] (family {family}, port {port})'
v_conn_accepted = 'Connection from [{dest}] port {port} [{proto_name}/{proto}] accepted (family {family}, sport {sport})'
v_listening_again = 'Connection closed, listening again.'
_4 = True
_6 = False
b = False
c = None
D = False
e = None
I = None
k = False
n = False
O = None
y = None
Y = None
def __init__(self, port, dest='', _4=None, _6=None, b=None, c=None,
D=None, e=None, I=None, k=None, n=None, O=None,
y=None, Y=None, **kwargs):
super(NetcatServer, self).__init__(**kwargs)
self.dest = dest
if dest == '':
# getaddrinfo doesn't accept an empty string.
# set to 0.0.0.0 to listen on all interfaces.
self.dest = '0.0.0.0'
self.port = port
if not isinstance(port, int) and not isinstance(port, str):
# port is not an int or a string.
# getaddrinfo expects an int or string.
# All objects have __repr__ so call repr to get string.
self.port = repr(port)
if _4 is not None:
self._4 = _4
if _6 is not None:
self._6 = _6
if b is not None:
self.b = b
if c is not None:
self.c = c
if D is not None:
self.D = D
if e is not None:
self.e = e
if I is not None:
self.I = I
if k is not None:
self.k = k
if n is not None:
self.n = n
if O is not None:
self.O = O
if y is not None:
self.y = y
if Y is not None:
self.Y = Y
if _6:
self.address_family = socket.AF_INET6
self.flags = 0
if self.n:
self.flags = socket.AI_NUMERICHOST
self._sock = socket.socket(self.address_family, self.socket_type)
bind_and_activate = True
if bind_and_activate:
try:
self._server_bind()
self._server_activate()
except:
self._server_close()
raise
def _listening(self):
self.print_verbose(self.v_listening.format(
dest=self.dest,
family=self.address_family,
port=self.port,
))
def _listening_again(self):
self.print_verbose(self.v_listening_again)
[docs] def iter_connections(self):
self._listening()
try:
nc_conn = self.next_connection()
except StopIteration:
return
try:
yield nc_conn
finally:
self._close_request(nc_conn)
if self.k:
while True:
self._listening_again()
try:
nc_conn = self.next_connection()
except StopIteration:
return
try:
yield nc_conn
finally:
self._close_request(nc_conn)
def _conn_accepted(self, cli_dest, cli_port):
proto = '*'
if not self.n:
try:
proto = socket.getservbyport(self.port, self.protocol_name)
except (socket.error, OSError):
pass
self.print_verbose(self.v_conn_accepted.format(
dest=cli_dest,
port=self.port,
proto_name=self.protocol_name,
proto=proto,
family=self.address_family,
sport=cli_port,
))
[docs] def next_connection(self):
while True:
try:
can_read, _, _ = select.select([self._sock], [], [], .002)
except (ValueError, socket.error):
# Bad / closed socket.
# This can occur when the server is closed.
raise StopIteration
if self._sock in can_read:
cli_sock, cli_addr = self._get_request()
try:
# IPv4
cli_dest, cli_port = cli_addr
except ValueError:
# IPv6
cli_dest, cli_port, _, _ = cli_addr
nc_conn = self._init_connection(cli_sock)
break
self._conn_accepted(cli_dest, cli_port)
return nc_conn
def _server_bind(self):
addrinfo = self._getaddrinfo(self.dest, self.port)
self._set_common_sockopts(self._sock)
try:
self._sock.bind((self.dest, self.port))
except socket.error as e:
raise NetcatSocketError(e)
def _server_activate(self):
pass
def _server_close(self):
self._sock.close()
def _get_request(self):
''' Override in subclass
Accept connection.
Return (socket, addr) tuple.
'''
raise NotImplementedError
def _close_request(self, request):
request.close()
[docs] def close(self):
"""
Close the server.
"""
super(NetcatServer, self).close()
self._server_close()
[docs]class NetcatTCPServer(NetcatServer):
"""
A :class:`pync.NetcatServer` for the Transmission Control Protocol.
"""
protocol_name = 'tcp'
Connection = NetcatTCPConnection
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
request_queue_size = 1
[docs] def next_connection(self):
nc_conn = super(NetcatTCPServer, self).next_connection()
if not self.k:
self._server_close()
return nc_conn
def _server_activate(self):
self._sock.listen(self.request_queue_size)
def _get_request(self):
return self._sock.accept()
[docs]class NetcatUDPServer(NetcatServer):
"""
A :class:`pync.NetcatServer` for the User Datagram Protocol.
"""
protocol_name = 'udp'
Connection = NetcatUDPConnection
address_family = socket.AF_INET
socket_type = socket.SOCK_DGRAM
max_packet_size = 8192
def _get_request(self):
data, addr = self._sock.recvfrom(self.max_packet_size)
try:
# py3
self._stdout.buffer.write(data)
except AttributeError:
# py2
self._stdout.write(data)
self._sock.connect(addr)
return self._sock, addr
def _close_request(self, request):
if not self.k:
request.close()
class NetcatStopReadWrite(Exception):
"""
Exception to stop the readwrite loop.
"""
class NetcatPythonStdinWriter(PythonStdinWriter, NetcatIOBase):
def write(self, *args, **kwargs):
try:
return super(NetcatPythonStdinWriter, self).write(*args, **kwargs)
except OSError:
raise NetcatStopReadWrite
class NetcatPythonStdoutReader(PythonStdoutReader, NetcatIOBase):
def read(self, *args, **kwargs):
try:
return super(NetcatPythonStdoutReader, self).read(*args, **kwargs)
except ProcessTerminated:
raise NetcatStopReadWrite
class NetcatPythonProcess(PythonProcess):
StdinWriter = NetcatPythonStdinWriter
StdoutReader = NetcatPythonStdoutReader
class NetcatPopen(NonBlockingPopen):
"""
A non-blocking process to be used with Netcat classes.
Use this instead of <subprocess.Popen>.
"""
def __init__(self, *args, **kwargs):
super(NetcatPopen, self).__init__(*args, **kwargs)
self.stdin = NetcatProcessWriter(self.stdin)
self.stdout = NetcatProcessReader(self.stdout)
class NetcatProcessIOBase(NetcatIOBase):
def __init__(self, f):
self.file = f
super(NetcatProcessIOBase, self).__init__()
class NetcatProcessWriter(NetcatProcessIOBase):
def write(self, data):
try:
self.file.write(data)
except OSError:
raise NetcatStopReadWrite
self.flush()
def flush(self):
self.file.flush()
class NetcatProcessReader(NetcatProcessIOBase):
def read(self, n):
try:
return self.file.read(n)
except ProcessTerminated:
raise NetcatStopReadWrite
class NetcatPortAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
# If one port is given on the command line, set that as value.
# If more that one is given, sort and chain as one iterator.
if not values:
return
if len(values) == 1 and values[0].start == (values[0].stop - 1):
# Only one port given.
setattr(namespace, self.dest, values[0].start)
return
# sort the list of port ranges.
sorted_values = sorted(values, key=lambda r: r.start)
# chain the port ranges into one iter.
chained_values = itertools.chain(*sorted_values)
setattr(namespace, self.dest, chained_values)
return
class NetcatArgumentParser(GroupingArgumentParser):
prog = 'Netcat'
usage = ("%(prog)s [-46bCDdhklnruvz] [-c string] [-e filename] [-I length]"
"\n\t [-i interval] [-O length] [-P proxy_username] [-p source_port]"
"\n\t [-q seconds] [-s source] [-T toskeyword] [-w timeout]"
"\n\t [-X proxy_protocol] [-x proxy_address[:port]]"
"\n\t [-Y pyfile] [-y pycode] [dest] [port]"
)
description = 'arbitrary TCP and UDP connections and listens (Netcat for Python).'
add_help = False
PortAction = NetcatPortAction
def __init__(self, *args, **kwargs):
super(NetcatArgumentParser, self).__init__(*args, **kwargs)
self.add_argument('-4',
help='Use IPv4',
action='store_true',
dest='_4',
)
self.add_argument('-6',
help='Use IPv6',
action='store_true',
dest='_6',
)
self.add_argument('-b',
help='Allow broadcast',
action='store_true',
)
self.add_argument('-c',
help='specify shell commands to exec after connect (use with caution).',
metavar='string',
)
self.add_argument('-C',
help='Send CRLF as line-ending',
action='store_true',
)
self.add_argument('-D',
help='Enable the debug socket option',
action='store_true',
)
self.add_argument('-d',
help='Detach from stdin',
action='store_true',
)
self.add_argument('-e',
help='specify filename to exec after connect (use with caution).',
metavar='filename',
)
self.add_argument('-h', '--help',
help='show this help message and exit.',
action='help',
)
self.add_argument('-I',
help='TCP receive buffer length',
metavar='length',
type=int,
)
self.add_argument('-i',
help='Delay interval for lines sent, ports scanned',
type=int,
metavar='secs',
)
self.add_argument('-k',
group='server arguments',
help='Keep inbound sockets open for multiple connects',
action='store_true',
)
self.add_argument('-l',
group='server arguments',
help='Listen mode, for inbound connects',
action='store_true',
)
self.add_argument('-n',
help='Suppress name/port resolutions',
action='store_true',
)
self.add_argument('-O',
help='TCP send buffer length',
metavar='length',
type=int,
)
self.add_argument('-P',
group='client arguments',
help='Username for proxy authentication',
metavar='proxy_username',
)
self.add_argument('-p',
help='Specify local port for remote connects',
metavar='source_port',
type=self.source_port,
)
self.add_argument('-q',
help='quit after EOF on stdin and delay of seconds',
metavar='seconds',
default=0,
type=int,
)
self.add_argument('-r',
group='client arguments',
help='Randomize remote ports',
action='store_true',
)
self.add_argument('-s',
group='client arguments',
help='Local source address',
metavar='source',
)
self.add_argument('-T',
help='Set IP Type of Service',
metavar='toskeyword',
type=self.toskeyword,
)
self.add_argument('-u',
help='UDP mode [default: TCP]',
action='store_true',
)
self.add_argument('-v',
help='Verbose',
action='store_true',
)
self.add_argument('-w',
help='Timeout for connects and final net reads',
metavar='secs',
type=self.timeout,
)
self.add_argument('-X',
group='client arguments',
help='Proxy protocol: "4", "5" (SOCKS) or "connect"',
metavar='proxy_protocol',
choices=['5', '4', 'connect'],
default='5',
)
self.add_argument('-x',
group='client arguments',
help='Specify proxy address and port',
metavar='proxy_address[:port]',
)
self.add_argument('-Y',
help='specify python file to exec after connect (use with caution).',
metavar='pyfile',
)
self.add_argument('-y',
help='specify python code to exec after connect (use with caution).',
metavar='pycode',
)
self.add_argument('-z',
group='client arguments',
help='Zero-I/O mode [used for scanning]',
action='store_true',
)
self.add_argument('dest',
help='The destination host name or ip to connect or bind to',
nargs='?',
default='',
metavar='dest',
)
self.add_argument('port',
help='The port number to connect or bind to',
type=self.port,
metavar='port',
nargs='*',
action=self.PortAction,
)
def _valid_port(self, value):
return 1 <= int(value) <= 65535
def timeout(self, value):
value = int(value)
if value < 0:
raise ValueError('timeout too small')
return value
def toskeyword(self, value):
if value in TOSKEYWORDS:
return TOSKEYWORDS[value]
try:
value = int(value)
except ValueError:
# value might be a hex number.
value = int(value, 16)
if 0 <= value <= 255:
return value
raise ValueError('illegal tos value {}'.format(value))
def source_port(self, value):
msg = 'invalid source_port value: {}'
if not self._valid_port(value):
raise ValueError(msg.format(value))
return int(value)
def port(self, value):
# This should always return a range of ports.
# Even if only one port is given.
#
# The PortAction then turns it into a single port
# if one port is given or a chain of sorted port
# ranges if more than one port is given.
msg = 'invalid port value: {}'
try:
# assume port value is a range.
# e.g 8000-8005
start_port, end_port = [int(x) for x in value.split('-')]
except ValueError:
# port value is not a range.
value = int(value)
if not self._valid_port(value):
raise ValueError(msg.format(value))
return compat.range(value, value+1)
if start_port > end_port:
start_port, end_port = end_port, start_port
for p in [start_port, end_port]:
if not self._valid_port(p):
raise ValueError(msg.format(p))
return compat.range(start_port, end_port+1)
def parse_args(self, args):
# First, modify the args to allow a negative number
# to be passed to the -q option.
# The argparse module doesn't seem to provide a
# solution to this problem so modifying the args
# directly before parsing them seems to be the
# only option.
#
# For a negative number to work, I just need to
# remove the space between -q and it's argument
# then argparse won't complain with an error.
#
# pync -q -1 localhost 8000 -> pync -q-1 localhost 8000
_args = list()
skip = False
for i, a in enumerate(args):
if skip:
skip = False
continue
if a.startswith('-') and a.endswith('q'):
try:
a += args[i+1]
except IndexError:
pass
else:
skip = True
_args.append(a)
args = _args
grouped_args = self.group_parse_args(args)
args = grouped_args['general arguments']
client_args = grouped_args['client arguments']
server_args = grouped_args['server arguments']
if server_args.l:
# Server mode.
if args.dest and args.port and not args.p:
# pync -l localhost 8000
pass
elif args.dest and not args.port and not args.p:
# pync -l 8000
# Get the port from args.dest.
# This will need feeding through the parser
# again to detect any port number errors.
args.port = args.dest
args.dest = ''
test_args = ['dest', args.port]
test_args = self.group_parse_args(test_args)['general arguments']
args.port = test_args.port
elif not args.dest and not args.port and args.p:
# pync -lp 8000
pass
elif args.dest and not args.port and args.p:
# pync -lp 8000 localhost
pass
elif args.dest and args.port and args.p:
# pync -lp 8000 localhost 8001
pass
else:
self.print_usage()
self.exit()
else:
# Client mode.
if args.dest and args.port:
# pync localhost 8000
pass
elif args.dest and args.port and args.p:
# pync -p 1234 localhost 8000
pass
else:
self.print_usage()
self.exit()
kwargs = dict()
kwargs.update(vars(args))
if server_args.l:
kwargs.update(vars(server_args))
else:
kwargs.update(vars(client_args))
return argparse.Namespace(**kwargs)
[docs]class Netcat(object):
"""
Factory class that returns the correct Netcat object based
on the arguments given.
:param dest: The IP address or hostname to connect or bind to depending
on the "l" parameter.
:type dest: str, optional
:param port: The port number to connect or bind to depending on the "l" parameter.
:type port: int, list(int)
:param l: Set to True to create a server and listen for incoming connections.
:type l: bool, optional
:param u: Set to True to use UDP for transport instead of the default TCP.
:type u: bool, optional
:param p: The source port number to bind to.
:type p: int, optional
:param kwargs: All other keyword arguments get passed to the underlying
Netcat class.
You can use this class as a context manager using the "with" statement:
.. code-block:: python
with Netcat(...) as nc:
nc.readwrite()
If you use it without the "with" statement, please make sure to use the
close method after use:
.. code-block:: python
nc = Netcat(...)
nc.readwrite()
nc.close()
:Examples:
.. code-block:: python
:caption: Use the "l" option to create a :class:`pync.NetcatTCPServer`
object.
from pync import Netcat
with Netcat(dest='localhost', port=8000, l=True) as nc:
nc.readwrite()
.. code-block:: python
:caption: By default, without the "l" option, Netcat will return a
:class:`pync.NetcatTCPClient` object.
from pync import Netcat
with Netcat(dest='localhost', port=8000) as nc:
nc.readwrite()
.. code-block:: python
:caption: Create a :class:`pync.NetcatUDPServer` with the "u" and "l" options.
from pync import Netcat
with Netcat(dest='localhost', port=8000, l=True, u=True) as nc:
nc.readwrite()
.. code-block:: python
:caption: And a :class:`pync.NetcatUDPClient` using only the "u" option.
from pync import Netcat
with Netcat(dest='localhost', port=8000, u=True) as nc:
nc.readwrite()
.. code-block:: python
:caption: Any other keyword arguments get passed to the underlying Netcat class.
from pync import Netcat
# Use the "k" option to keep the server open between connections.
with Netcat(dest='localhost', port=8000, l=True, k=True) as nc:
nc.readwrite()
.. code-block:: python
:caption: Pass a list of ports to connect to one after the other.
# Simple port scan example.
from pync import Netcat
# Use the "z" option to turn Zero i_o on (connect then close).
# Use the "v" option to turn verbose output on to see connection success or failure.
ports = [8000, 8003, 8002]
with Netcat(dest='localhost', port=ports, z=True, v=True) as nc:
nc.readwrite()
"""
ArgumentParser = NetcatArgumentParser
TCPClient = NetcatTCPClient
TCPServer = NetcatTCPServer
UDPClient = NetcatUDPClient
UDPServer = NetcatUDPServer
stdin = sys.stdin
stdout = sys.stdout
stderr = sys.stderr
#stdin = None
#stdout = None
#stderr = None
def __new__(cls, dest='', port=None, l=False, u=False, p=None,
stdin=None, stdout=None, stderr=None, **kwargs):
stdin = stdin or cls.stdin
stdout = stdout or cls.stdout
stderr = stderr or cls.stderr
kwargs.update(dict(
stdin=stdin,
stdout=stdout,
stderr=stderr))
if l:
if p is not None:
port = p
if u:
return cls.UDPServer(port, dest=dest, **kwargs)
else:
return cls.TCPServer(port, dest=dest, **kwargs)
else:
if u:
return cls.UDPClient(dest, port, p=p, **kwargs)
else:
return cls.TCPClient(dest, port, p=p, **kwargs)
[docs] @classmethod
def from_args(cls, args, stdin=None, stdout=None, stderr=None):
"""
Create a Netcat object from command-line arguments instead of keyword
arguments.
:param args: A string containing the command-line arguments to create
the Netcat instance with.
:type args: str
:param stdin: A file-like object to read outgoing network data from.
:type stdin: file, optional
:param stdout: A file-like object to write incoming network data to.
:type stdout: file, optional
:param stderr: A file-like object to write verbose/debug/error messages to.
:type stderr: file, optional
:Example:
.. code-block:: python
from pync import Netcat
with Netcat.from_args('-l localhost 8000') as nc:
nc.readwrite()
"""
stdin = stdin or cls.stdin
stdout = stdout or cls.stdout
stderr = stderr or cls.stderr
try:
# Assume args is a string and try to split it.
args = shlex.split(args)
except AttributeError:
# args is not a string, assume it's a list.
pass
parser = cls.ArgumentParser(stdout=stdout, stderr=stderr)
args = parser.parse_args(args)
kwargs = dict()
kwargs.update(vars(args))
kwargs.update(dict(
stdin=stdin,
stdout=stdout,
stderr=stderr,
))
return cls(**kwargs)
[docs]def pync(args, stdin=None, stdout=None, stderr=None, Netcat=Netcat):
"""
Create and run a Netcat instance.
This is similar to running **pync** from the command-line.
:param args: A string containing command-line arguments.
:type args: str
:param stdin: A file-like object to read outgoing network data from.
:type stdin: file, optional
:param stdout: A file-like object to write incoming network data to.
:type stdout: file, optional
:param stderr: A file-like object to write error/verbose/debug messages to.
:type stderr: file, optional
:return: Error status code depending on success (0) or failure (>0).
:rtype: int
:Examples:
.. code-block:: python
:caption: Create a local TCP server on port 8000.
from pync import pync
pync('-l localhost 8000')
.. code-block:: python
:caption: Connect to a local TCP server on port 8000.
from pync import pync
pync('localhost 8000')
.. code-block:: python
:caption: Create a local TCP server to host a file on port 8000.
from pync import pync
with open('file.in', 'rb') as f:
pync('-l localhost 8000', stdin=f)
.. code-block:: python
:caption: Connect to a local TCP server to download a file on
port 8000.
from pync import pync
with open('file.out', 'wb') as f:
pync('localhost 8000', stdout=f)
"""
_stdin = stdin or Netcat.stdin
_stdout = stdout or Netcat.stdout
_stderr = stderr or Netcat.stderr
exit = argparse.Namespace()
exit.status = 1
class PyncTCPClient(Netcat.TCPClient):
v_conn_refused = 'pync: ' + Netcat.TCPClient.v_conn_refused
def _conn_succeeded(self, port):
super(PyncTCPClient, self)._conn_succeeded(port)
exit.status = 0
class PyncTCPServer(Netcat.TCPServer):
def _listening(self):
super(PyncTCPServer, self)._listening()
exit.status = 0
class PyncUDPClient(Netcat.UDPClient):
v_conn_refused = 'pync: ' + Netcat.UDPClient.v_conn_refused
def _conn_succeeded(self, port):
super(PyncUDPClient, self)._conn_succeeded(port)
exit.status = 0
class PyncUDPServer(Netcat.UDPServer):
def _listening(self):
super(PyncUDPServer, self)._listening()
exit.status = 0
class PyncArgumentParser(Netcat.ArgumentParser):
prog = 'pync'
def print_help(self, *args, **kwargs):
super(PyncArgumentParser, self).print_help(*args, **kwargs)
exit.status = 0
class PyncNetcat(Netcat):
ArgumentParser = PyncArgumentParser
TCPClient = PyncTCPClient
TCPServer = PyncTCPServer
UDPClient = PyncUDPClient
UDPServer = PyncUDPServer
stdin = _stdin
stdout = _stdout
stderr = _stderr
try:
with PyncNetcat.from_args(args) as nc:
nc.readwrite()
except NetcatError as e:
_stderr.write('pync: {}\n'.format(e))
exit.status = 1
except KeyboardInterrupt:
_stderr.write('\n')
exit.status = 130
except SystemExit:
# ArgumentParser may raise SystemExit when error or help.
return exit.status
return exit.status