Skip to content

Commit

Permalink
Version 0.3.1, rev238, Connection encryption using TLS, One click sit…
Browse files Browse the repository at this point in the history
…e clone feature, Encryption stats, Disable encryption startup parameter, Disable ssl compression startup parameter, Exchange supported encryption methods at handshake, Alternative open port checker, Option to store site privatekey in users.json, Torrent tracker swap, Test for bip32 based site creation, cloning and sslcert creation, Fix for Chrome plugin on OSX, Separate siteSign websocket command, Update pybitcointools to major speedup, Re-add sslwrap for python 0.2.9+, Disable SSL compression to save memory and better performance
  • Loading branch information
shortcutme committed Jun 9, 2015
1 parent f0597af commit a78907c
Show file tree
Hide file tree
Showing 64 changed files with 4,132 additions and 204 deletions.
19 changes: 13 additions & 6 deletions plugins/Stats/StatsPlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def getObjSize(self, obj, hpy = None):
def actionStats(self):
import gc, sys
from Ui import UiRequest
from Crypt import CryptConnection

hpy = None
if self.get.get("size") == "1": # Calc obj size
Expand All @@ -47,24 +48,25 @@ def actionStats(self):
yield """
<style>
* { font-family: monospace }
table * { text-align: right; padding: 0px 10px }
table td, table th { text-align: right; padding: 0px 10px }
</style>
"""

# Memory
try:
yield "rev%s | " % config.rev
yield "IP external: %s | " % config.ip_external
yield "%s | " % config.ip_external
yield "Opened: %s | " % main.file_server.port_opened
yield "Recv: %.2fMB, Sent: %.2fMB | " % (float(main.file_server.bytes_recv)/1024/1024, float(main.file_server.bytes_sent)/1024/1024)
yield "Crypt: %s | " % CryptConnection.manager.crypt_supported
yield "In: %.2fMB, Out: %.2fMB | " % (float(main.file_server.bytes_recv)/1024/1024, float(main.file_server.bytes_sent)/1024/1024)
yield "Peerid: %s | " % main.file_server.peer_id
import psutil
process = psutil.Process(os.getpid())
mem = process.get_memory_info()[0] / float(2 ** 20)
yield "Mem: %.2fMB | " % mem
yield "Threads: %s | " % len(process.threads())
yield "CPU: usr %.2fs sys %.2fs | " % process.cpu_times()
yield "Open files: %s | " % len(process.open_files())
yield "Files: %s | " % len(process.open_files())
yield "Sockets: %s | " % len(process.connections())
yield "Calc size <a href='?size=1'>on</a> <a href='?size=0'>off</a>"
except Exception, err:
Expand All @@ -73,15 +75,20 @@ def actionStats(self):

# Connections
yield "<b>Connections</b> (%s, total made: %s):<br>" % (len(main.file_server.connections), main.file_server.last_connection_id)
yield "<table><tr> <th>id</th> <th>protocol</th> <th>type</th> <th>ip</th> <th>open</th> <th>ping</th> <th>buff</th>"
yield "<th>idle</th> <th>open</th> <th>delay</th> <th>sent</th> <th>received</th> <th>last sent</th> <th>waiting</th> <th>version</th> <th>peerid</th> </tr>"
yield "<table><tr> <th>id</th> <th>proto</th> <th>type</th> <th>ip</th> <th>open</th> <th>crypt</th> <th>ping</th> <th>buff</th>"
yield "<th>idle</th> <th>open</th> <th>delay</th> <th>out</th> <th>in</th> <th>last sent</th> <th>waiting</th> <th>version</th> <th>peerid</th> </tr>"
for connection in main.file_server.connections:
if "cipher" in dir(connection.sock):
cipher = connection.sock.cipher()[0]
else:
cipher = connection.crypt
yield self.formatTableRow([
("%3d", connection.id),
("%s", connection.protocol),
("%s", connection.type),
("%s:%s", (connection.ip, connection.port)),
("%s", connection.handshake.get("port_opened")),
("<span title='%s'>%s</span>", (connection.crypt, cipher)),
("%6.3f", connection.last_ping_delay),
("%s", connection.incomplete_buff_recv),
("since", max(connection.last_send_time, connection.last_recv_time)),
Expand Down
10 changes: 6 additions & 4 deletions src/Config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

class Config(object):
def __init__(self):
self.version = "0.3.0"
self.rev = 196
self.version = "0.3.1"
self.rev = 238
self.parser = self.createArguments()
argv = sys.argv[:] # Copy command line arguments
argv = self.parseConfig(argv) # Add arguments from config file
Expand Down Expand Up @@ -77,7 +77,7 @@ def createArguments(self):
# PeerPing
action = subparsers.add_parser("peerPing", help='Send Ping command to peer')
action.add_argument('peer_ip', help='Peer ip')
action.add_argument('peer_port', help='Peer port')
action.add_argument('peer_port', help='Peer port', nargs='?')

# PeerGetFile
action = subparsers.add_parser("peerGetFile", help='Request and print a file content from peer')
Expand Down Expand Up @@ -118,8 +118,10 @@ def createArguments(self):
parser.add_argument('--fileserver_port',help='FileServer bind port', default=15441, type=int, metavar='port')
parser.add_argument('--disable_udp', help='Disable UDP connections', action='store_true')
parser.add_argument('--proxy', help='Socks proxy address', metavar='ip:port')
parser.add_argument('--use_openssl', help='Use OpenSSL liblary for speedup', type='bool', choices=[True, False], default=use_openssl)
parser.add_argument('--ip_external', help='External ip (tested on start if None)', metavar='ip')
parser.add_argument('--use_openssl', help='Use OpenSSL liblary for speedup', type='bool', choices=[True, False], default=use_openssl)
parser.add_argument('--disable_encryption', help='Disable connection encryption', action='store_true')
parser.add_argument('--disable_sslcompression', help='Disable SSL compression to save memory', type='bool', choices=[True, False], default=True)

parser.add_argument('--coffeescript_compiler', help='Coffeescript compiler for developing', default=coffeescript, metavar='executable_path')

Expand Down
63 changes: 44 additions & 19 deletions src/Connection/Connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
from Config import config
from Debug import Debug
from util import StreamingMsgpack
from Crypt import CryptConnection

class Connection(object):
__slots__ = ("sock", "ip", "port", "peer_id", "id", "protocol", "type", "server", "unpacker", "req_id", "handshake", "connected", "event_connected", "closed", "start_time", "last_recv_time", "last_message_time", "last_send_time", "last_sent_time", "incomplete_buff_recv", "bytes_recv", "bytes_sent", "last_ping_delay", "last_req_time", "last_cmd", "name", "updateName", "waiting_requests")
__slots__ = ("sock", "ip", "port", "peer_id", "id", "protocol", "type", "server", "unpacker", "req_id", "handshake", "crypt", "connected", "event_connected", "closed", "start_time", "last_recv_time", "last_message_time", "last_send_time", "last_sent_time", "incomplete_buff_recv", "bytes_recv", "bytes_sent", "last_ping_delay", "last_req_time", "last_cmd", "name", "updateName", "waiting_requests")

def __init__(self, server, ip, port, sock=None):
self.sock = sock
Expand All @@ -22,6 +23,8 @@ def __init__(self, server, ip, port, sock=None):
self.unpacker = None # Stream incoming socket messages here
self.req_id = 0 # Last request id
self.handshake = {} # Handshake info got from peer
self.crypt = None # Connection encryption method

self.connected = False
self.event_connected = gevent.event.AsyncResult() # Solves on handshake received
self.closed = False
Expand Down Expand Up @@ -76,6 +79,7 @@ def connect(self):

# Handle incoming connection
def handleIncomingConnection(self, sock):
self.log("Incoming connection...")
self.type = "in"
self.messageLoop()

Expand All @@ -90,10 +94,9 @@ def messageLoop(self):
self.connected = True

self.unpacker = msgpack.Unpacker()
sock = self.sock
try:
while True:
buff = sock.recv(16*1024)
buff = self.sock.recv(16*1024)
if not buff: break # Connection closed
self.last_recv_time = time.time()
self.incomplete_buff_recv += 1
Expand All @@ -118,12 +121,32 @@ def handshakeInfo(self):
"version": config.version,
"protocol": "v2",
"peer_id": self.server.peer_id,
"fileserver_port": config.fileserver_port,
"fileserver_port": self.server.port,
"port_opened": self.server.port_opened,
"rev": config.rev
"rev": config.rev,
"crypt_supported": CryptConnection.manager.crypt_supported,
"crypt": self.crypt
}


def setHandshake(self, handshake):
self.handshake = handshake
if handshake.get("port_opened", None) == False: # Not connectable
self.port = 0
else:
self.port = handshake["fileserver_port"] # Set peer fileserver port
# Check if we can encrypt the connection
if handshake.get("crypt_supported"):
if handshake.get("crypt"): # Recommended crypt by server
crypt = handshake["crypt"]
else: # Select the best supported on both sides
crypt = CryptConnection.manager.selectCrypt(handshake["crypt_supported"])

if crypt:
self.crypt = crypt
self.event_connected.set(True) # Mark handshake as done


# Handle incoming message
def handleMessage(self, message):
self.last_message_time = time.time()
Expand All @@ -133,29 +156,31 @@ def handleMessage(self, message):
del self.waiting_requests[message["to"]]
elif message["to"] == 0: # Other peers handshake
ping = time.time()-self.start_time
if config.debug_socket: self.log("Got handshake response: %s, ping: %s" % (message, ping))
if config.debug_socket: self.log("Handshake response: %s, ping: %s" % (message, ping))
self.last_ping_delay = ping
self.handshake = message
if self.handshake.get("port_opened", None) == False: # Not connectable
self.port = 0
else:
self.port = message["fileserver_port"] # Set peer fileserver port
self.event_connected.set(True) # Mark handshake as done
# Server switched to crypt, lets do it also
if message.get("crypt"):
self.crypt = message["crypt"]
server = (self.type == "in")
self.log("Crypt out connection using: %s (server side: %s)..." % (self.crypt, server))
self.sock = CryptConnection.manager.wrapSocket(self.sock, self.crypt, server)
self.sock.do_handshake()
self.setHandshake(message)
else:
self.log("Unknown response: %s" % message)
elif message.get("cmd"): # Handhsake request
if message["cmd"] == "handshake":
self.handshake = message["params"]
if self.handshake.get("port_opened", None) == False: # Not connectable
self.port = 0
else:
self.port = self.handshake["fileserver_port"] # Set peer fileserver port
self.event_connected.set(True) # Mark handshake as done
if config.debug_socket: self.log("Handshake request: %s" % message)
self.setHandshake(message["params"])
data = self.handshakeInfo()
data["cmd"] = "response"
data["to"] = message["req_id"]
self.send(data)
self.send(data) # Send response to handshake
# Sent crypt request to client
if self.crypt:
server = (self.type == "in")
self.log("Crypt in connection using: %s (server side: %s)..." % (self.crypt, server))
self.sock = CryptConnection.manager.wrapSocket(self.sock, self.crypt, server)
else:
self.server.handleRequest(self, message)
else: # Old style response, no req_id definied
Expand Down
7 changes: 4 additions & 3 deletions src/Connection/ConnectionServer.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from gevent.server import StreamServer
from gevent.pool import Pool
import socket, os, logging, random, string, time
import socket, os, logging, random, string, time, sys
import gevent, msgpack
import cStringIO as StringIO
from Debug import Debug
from Connection import Connection
from Config import config
from Crypt import CryptConnection


class ConnectionServer:
Expand Down Expand Up @@ -39,12 +40,13 @@ def __init__(self, ip=None, port=None, request_handler=None):
self.stream_server = StreamServer((ip.replace("*", ""), port), self.handleIncomingConnection, spawn=self.pool, backlog=100)
if request_handler: self.handleRequest = request_handler

CryptConnection.manager.loadCerts()


def start(self):
self.running = True
self.log.debug("Binding to: %s:%s, (msgpack: %s), supported crypt: %s" % (self.ip, self.port, ".".join(map(str, msgpack.version)), CryptConnection.manager.crypt_supported ) )
try:
self.log.debug("Binding to: %s:%s (msgpack: %s)" % (self.ip, self.port, ".".join(map(str, msgpack.version))))
self.stream_server.serve_forever() # Start normal connection server
except Exception, err:
self.log.info("StreamServer bind error, must be running already: %s" % err)
Expand Down Expand Up @@ -152,7 +154,6 @@ def checkConnections(self):
connection.close()



# -- TESTING --

def testCreateServer():
Expand Down
101 changes: 101 additions & 0 deletions src/Crypt/CryptConnection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import sys, logging, os
from Config import config
import gevent
from util import SslPatch

class CryptConnectionManager:
def __init__(self):
# OpenSSL params
if sys.platform.startswith("win"):
self.openssl_bin = "src\\lib\\opensslVerify\\openssl.exe"
else:
self.openssl_bin = "openssl"
self.openssl_env = {"OPENSSL_CONF": "src/lib/opensslVerify/openssl.cnf"}

self.crypt_supported = [] # Supported cryptos


# Select crypt that supported by both sides
# Return: Name of the crypto
def selectCrypt(self, client_supported):
for crypt in self.crypt_supported:
if crypt in client_supported:
return crypt
return False


# Wrap socket for crypt
# Return: wrapped socket
def wrapSocket(self, sock, crypt, server=False):
if crypt == "tls-rsa":
ciphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:AES128-GCM-SHA256:AES128-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK"
if server:
return gevent.ssl.wrap_socket(sock, server_side=server, keyfile='%s/key-rsa.pem' % config.data_dir, certfile='%s/cert-rsa.pem' % config.data_dir, ciphers=ciphers)
else:
return gevent.ssl.wrap_socket(sock, ciphers=ciphers)
else:
return sock


def removeCerts(self):
for file_name in ["cert-rsa.pem", "key-rsa.pem"]:
file_path = "%s/%s" % (config.data_dir, file_name)
if os.path.isfile(file_path): os.unlink(file_path)


# Loand and create cert files is necessary
def loadCerts(self):
if config.disable_encryption: return False

if self.loadSslRsaCert():
self.crypt_supported.append("tls-rsa")


# Try to create RSA server cert + sign for connection encryption
# Return: True on success
def loadSslRsaCert(self):
import subprocess

if os.path.isfile("%s/cert-rsa.pem" % config.data_dir) and os.path.isfile("%s/key-rsa.pem" % config.data_dir):
return True # Files already exits

back = subprocess.Popen(
"%s req -x509 -newkey rsa:2048 -sha256 -batch -keyout %s/key-rsa.pem -out %s/cert-rsa.pem -nodes -config %s" % (self.openssl_bin, config.data_dir, config.data_dir, self.openssl_env["OPENSSL_CONF"]),
shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=self.openssl_env
).stdout.read().strip()
logging.debug("Generating RSA cert and key PEM files...%s" % back)

if os.path.isfile("%s/cert-rsa.pem" % config.data_dir) and os.path.isfile("%s/key-rsa.pem" % config.data_dir):
return True
else:
logging.error("RSA ECC SSL cert generation failed, cert or key files not exits.")
return False


# Not used yet: Missing on some platform
def createSslEccCert(self):
return False
import subprocess

# Create ECC privatekey
back = subprocess.Popen(
"%s ecparam -name prime256v1 -genkey -out %s/key-ecc.pem" % (self.openssl_bin, config.data_dir),
shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=self.openssl_env
).stdout.read().strip()
self.log.debug("Generating ECC privatekey PEM file...%s" % back)

# Create ECC cert
back = subprocess.Popen(
"%s req -new -key %s/key-ecc.pem -x509 -nodes -out %s/cert-ecc.pem -config %s" % (self.openssl_bin, config.data_dir, config.data_dir, self.openssl_env["OPENSSL_CONF"]),
shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=self.openssl_env
).stdout.read().strip()
self.log.debug("Generating ECC cert PEM file...%s" % back)

if os.path.isfile("%s/cert-ecc.pem" % config.data_dir) and os.path.isfile("%s/key-ecc.pem" % config.data_dir):
return True
else:
self.logging.error("ECC SSL cert generation failed, cert or key files not exits.")
return False


manager = CryptConnectionManager()
Loading

0 comments on commit a78907c

Please sign in to comment.