Skip to content

Commit

Permalink
merge openssl branch
Browse files Browse the repository at this point in the history
  • Loading branch information
samrushing committed Apr 18, 2013
2 parents e4023e7 + b4d13a0 commit d340e3c
Show file tree
Hide file tree
Showing 7 changed files with 1,938 additions and 6 deletions.
2 changes: 1 addition & 1 deletion coro/http/__init__.py
@@ -1,6 +1,6 @@
# -*- Mode: Python -*-

from server import server, tlslite_server, connection, http_request
from server import server, tlslite_server, openssl_server, connection, http_request
import handlers
import coro
from coro import read_stream
Expand Down
29 changes: 27 additions & 2 deletions coro/http/server.py
Expand Up @@ -458,11 +458,11 @@ def start (self, addr, retries=5):
Try up to <retries> time to bind to that address.
Raises an exception if the bind fails."""

self.sock = coro.tcp_sock()
self.addr = addr
self.sock = self.create_sock()
self.sock.set_reuse_addr()
done = 0
save_errno = 0
self.addr = addr
while not done:
for x in xrange (retries):
try:
Expand Down Expand Up @@ -505,6 +505,14 @@ def run (self):
def accept (self):
return self.sock.accept()

def create_sock (self):
# the assumption here is that you would never run an HTTP server
# on a unix socket, if you need that then override this method.
if ':' in self.addr[0]:
return coro.tcp6_sock()
else:
return coro.tcp_sock()

def create_connection (self):
return connection (self)

Expand Down Expand Up @@ -558,3 +566,20 @@ def read_private (self):
open (self.key_path).read(),
private=True
)

class openssl_server (server):

def __init__ (self, ctx, verify=False):
self.ctx = ctx
# XXX do something with verify
self.verify = verify
server.__init__ (self)

def create_sock (self):
import coro.ssl
import socket
if ':' in self.addr[0]:
domain = socket.AF_INET6
else:
domain = socket.AF_INET
return coro.ssl.sock (self.ctx, domain=domain)
152 changes: 152 additions & 0 deletions coro/ssl/__init__.py
@@ -0,0 +1,152 @@
# -*- Mode: Python -*-

import coro
from coro.ssl import openssl
import socket

ssl_op_map = {
"sslv2":openssl.SSL_OP.NO_SSLv3|openssl.SSL_OP.NO_TLSv1,
"sslv3":openssl.SSL_OP.NO_SSLv2|openssl.SSL_OP.NO_TLSv1,
"tlsv1":openssl.SSL_OP.NO_SSLv2|openssl.SSL_OP.NO_SSLv3,
"sslv2sslv3":openssl.SSL_OP.NO_TLSv1,
"sslv3tlsv1":openssl.SSL_OP.NO_SSLv2,
"sslv2sslv3tlsv1":0
}

from openssl import x509, pkey, dh_param

def new_ctx (cert=None, chain=(), key=None, proto=None, ciphers=None, dhparam=None, next_protos=None):
ctx = openssl.ssl_ctx()
if cert:
ctx.use_cert (cert, chain)
if key:
ctx.use_key (key)
if proto:
ctx.set_options (ctx.get_options() | ssl_op_map[proto])
if ciphers:
ctx.set_ciphers (ciphers)
if dhparam:
ctx.set_tmp_dh (dhparam)
if next_protos:
ctx.set_next_protos (next_protos)
return ctx

class sock (coro.sock):

def __init__ (self, ctx, fd=-1, verify=False, domain=socket.AF_INET):
coro.sock.__init__ (self, fd=fd, domain=domain)
self.ctx = ctx
# Note: this uses SSLv23_method(), which allows it to accept V2 client hello
# (which are common), but still limit to V3 or TLS1 via the 'proto' arg.
self.ssl = self.ctx.ssl()
self.ssl.set_fd (self.fd)
if verify:
self.ssl.set_verify (openssl.SSL_VERIFY_PEER)

def __repr__ (self):
return '<openssl sock fd=%d ssl@%x @%x>' % (self.fd, id (self.ssl), id (self))

def _non_blocking_retry (self, fun, *args):
while 1:
try:
return fun (*args)
except openssl.WantRead:
self.wait_for_read()
except openssl.WantWrite:
self.wait_for_write()

def accept (self):
conn, addr = coro.sock.accept (self)
try:
# hand the fd off to a new ssl sock object...
new = self.__class__ (self.ctx, domain=conn.domain, fd=conn.fd)
# ...avoid having socket.pyx close the fd
conn.fd = -1
# using set_accept_state() makes NPN very difficult
#new.ssl.set_accept_state()
new.ssl_accept()
return new, addr
except:
conn.close()
raise

def set_accept_state (self):
return self.ssl.set_accept_state()

def set_connect_state (self):
return self.ssl.set_connect_state()

def ssl_accept (self):
self._non_blocking_retry (self.ssl.accept)

def connect (self, addr):
coro.sock.connect (self, addr)
# using set_connect_state makes NPN very difficult
#self.ssl.set_connect_state()
return self.ssl_connect()

def ssl_connect (self):
self._non_blocking_retry (self.ssl.connect)

def recv (self, block_size):
return self._non_blocking_retry (self.ssl.read, block_size)

read = recv

def read_exact (self, size):
left = size
r = []
while left:
block = self.recv (left)
if not block:
break
else:
r.append (block)
left -= len (block)
return ''.join (r)

def recvfrom (self, block_size, timeout=30):
raise SystemError, "recvfrom not supported for SSL sockets"

def send (self, data):
return self._non_blocking_retry (self.ssl.write, data)

write = send

# SSL_write() makes this guarantee.
sendall = send

def sendto (self, data, addr):
raise SystemError, "sendto not supported for SSL sockets"

def writev (self, list_of_data):
_sum = 0
for data in list_of_data:
_sum += self._non_blocking_retry (self.ssl.write, data)
return _sum

def shutdown (self, how=None):
return self._non_blocking_retry (self.ssl.shutdown)

def close (self):
try:
self.shutdown()
finally:
coro.sock.close (self)

def getCipher (self):
return self.ssl.get_cipher()

# The following are taken from #defines in /usr/include/openssl/*.h
_protocol_str_map = {
0x0002: 'SSLv2', # SSL2_VERSION
0x0300: 'SSLv3', # SSL3_VERSION
0x0301: 'TLSv1', # TLS1_VERSION
}

def getProtocol (self):
prot_id = self.ssl.get_protocol()
try:
return self._protocol_str_map[prot_id]
except KeyError:
return '(UNKNOWN:%x)' % (prot_id,)

0 comments on commit d340e3c

Please sign in to comment.