Merge lp:~andrea.corbellini/beeseek/ssl-sockets into lp:beeseek/1.0

Proposed by Andrea Corbellini
Status: Merged
Approved by: Andrea Corbellini
Approved revision: 238
Merged at revision: not available
Proposed branch: lp:~andrea.corbellini/beeseek/ssl-sockets
Merge into: lp:beeseek/1.0
Diff against target: None lines
To merge this branch: bzr merge lp:~andrea.corbellini/beeseek/ssl-sockets
Reviewer Review Type Date Requested Status
Andrea Corbellini Approve
Review via email: mp+3968@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Andrea Corbellini (andrea.corbellini) wrote :

This branch enables sockets to receive and send data using SSL encryption.
It requires pyopenssl.

Revision history for this message
Andrea Corbellini (andrea.corbellini) wrote :

Approved after discussion with team members.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'beeseek/network/lowlevel.py'
2--- beeseek/network/lowlevel.py 2009-02-17 17:06:44 +0000
3+++ beeseek/network/lowlevel.py 2009-02-25 17:00:00 +0000
4@@ -18,12 +18,20 @@
5 from select import select
6 from beeseek.interfaces import Interface, implements
7
8+try:
9+ from cStringIO import StringIO
10+except ImportError:
11+ from StringIO import StringIO
12+
13+
14 __all__ = ('SocketError', 'IProtocol', 'BaseProtocol', 'ISocket',
15 'BaseSocket', 'IPSocket', 'IPv6Socket', 'UNIXSocket',
16- 'has_ipv6', 'has_unix')
17+ 'has_ipv6', 'has_unix', 'has_ssl', 'SSLContext')
18
19
20 SocketError = _socket.error
21+SSL_CLIENT = 0
22+SSL_SERVER = 1
23
24 class IProtocol(Interface):
25 """Base interface for all network connectors.
26@@ -142,6 +150,9 @@
27
28 # Other socket methods
29
30+ def fileno(self):
31+ """Return the fileno."""
32+
33 def waitinput(self, timeout=None):
34 """Wait for incoming data.
35
36@@ -150,6 +161,16 @@
37 :return: True if there is incoming data; else False.
38 """
39
40+ def enable_ssl(self, context=None, state=None):
41+ """Read and write data using an SSL connection.
42+
43+ :param context: The SSLContext to use.
44+ :param state: The connection state.
45+ """
46+
47+ def disable_ssl(self):
48+ """Read and write data using a standard connection."""
49+
50 # Close methods
51
52 def close(self):
53@@ -181,7 +202,8 @@
54 high-level network objects.
55 """
56
57- __slots__ = 'closed', '_sock', '_fileno', '_rfile', '_wfile'
58+ __slots__ = ('closed', '_sock', '_fileno', '_rfile', '_wfile',
59+ '_rbuf', '_ssl')
60 implements(IProtocol, isbase=True)
61
62
63@@ -190,6 +212,7 @@
64 self._fileno = fileno = self._sock.fileno()
65 self._rfile = os.fdopen(fileno, 'r', 0)
66 self._wfile = os.fdopen(fileno, 'w', bufsize)
67+ self._ssl = None
68 self.closed = False
69
70 # Connect/bind methods
71@@ -212,32 +235,111 @@
72
73 def raw_recv(self, size=-1):
74 if size < 0:
75- size = 5120
76- try:
77- data = os.read(self._fileno, size)
78- except IOError:
79- self.closed = True
80- return ''
81+ size = 8192
82+ if not self._ssl:
83+ try:
84+ data = os.read(self._fileno, size)
85+ except IOError:
86+ self.closed = True
87+ return ''
88+ else:
89+ rbuf = self._rbuf
90+ rbuf.seek(0, 0)
91+ data = rbuf.read(size)
92+ self._rbuf = StringIO(rbuf.read())
93+ if data:
94+ return data
95+ data = self._ssl.recv(size)
96 if not data:
97 self.closed = True
98 return data
99
100 def raw_read(self, size=-1):
101- try:
102- data = self._rfile.read(size)
103- except IOError:
104- self.closed = True
105- return ''
106+ if not self._ssl:
107+ try:
108+ data = self._rfile.read(size)
109+ except IOError:
110+ self.closed = True
111+ return ''
112+ else:
113+ ssl = self._ssl
114+ rbuf = self._rbuf
115+ if size < 0:
116+ self._rbuf = StringIO()
117+ while True:
118+ data = ssl.recv(8192)
119+ if not data:
120+ break
121+ rbuf.write(data)
122+ else:
123+ rbuf.seek(0, 2)
124+ buflen = rbuf.tell()
125+ if buflen >= size:
126+ rbuf.seek(0, 0)
127+ data = rbuf.read(size)
128+ self._rbuf = StringIO(rbuf.read())
129+ return data
130+ else:
131+ self._rbuf = StringIO()
132+ size -= buflen
133+ while size:
134+ data = ssl.recv(size)
135+ if not data:
136+ break
137+ rbuf.write(data)
138+ size -= len(data)
139+ data = rbuf.getvalue()
140 if not data:
141 self.closed = True
142 return data
143
144 def raw_readline(self, size=-1):
145- try:
146- data = self._rfile.readline(size)
147- except IOError:
148- self.closed = True
149- return ''
150+ if not self._ssl:
151+ try:
152+ data = self._rfile.readline(size)
153+ except IOError:
154+ self.closed = True
155+ return ''
156+ else:
157+ ssl = self._ssl
158+ rbuf = self._rbuf
159+ rbuf.seek(0, 0)
160+ if size < 0:
161+ data = rbuf.readline()
162+ if data.endswith('\n'):
163+ self._rbuf = StringIO()
164+ return data
165+ while True:
166+ data = ssl.recv(8192)
167+ if not data:
168+ self._rbuf = StringIO()
169+ break
170+ newline = data.find('\n') + 1
171+ if newline > 0:
172+ rbuf.write(data[:newline])
173+ self._rbuf = StringIO(data[newline:])
174+ break
175+ rbuf.write(data)
176+ data = rbuf.read()
177+ else:
178+ data = rbuf.readline(size)
179+ size -= len(data)
180+ if not size or data.endswith('\n'):
181+ self._rbuf = StringIO(rbuf.read())
182+ return data
183+ self._rbuf = StringIO()
184+ while size:
185+ data = ssl.recv(size)
186+ if not data:
187+ break
188+ newline = data.find('\n') + 1
189+ if newline > 0:
190+ rbuf.write(data[:newline])
191+ self._rbuf.write(data[newline:])
192+ break
193+ size -= len(data)
194+ rbuf.write(data)
195+ data = rbuf.getvalue()
196 if not data:
197 self.closed = True
198 return data
199@@ -245,16 +347,22 @@
200 # Send methods
201
202 def raw_write(self, data):
203- try:
204- self._wfile.write(data)
205- except IOError:
206- self.closed = True
207+ if not self._ssl:
208+ try:
209+ self._wfile.write(data)
210+ except IOError:
211+ self.closed = True
212+ else:
213+ self._ssl.sendall(data)
214
215 def raw_writelines(self, data):
216- try:
217- self._wfile.writelines(data)
218- except IOError:
219- self.closed = True
220+ if not self._ssl:
221+ try:
222+ self._wfile.writelines(data)
223+ except IOError:
224+ self.closed = True
225+ else:
226+ self._ssl.sendall(''.join(data))
227
228 def flush(self):
229 try:
230@@ -268,12 +376,29 @@
231 return self._fileno
232
233 def waitinput(self, timeout=None):
234- return bool(select((self._rfile,), (), (), timeout))
235+ return bool(select((self._rfile,), (), (), timeout)[0])
236+
237+ def enable_ssl(self, context=None, state=SSL_CLIENT):
238+ if not context:
239+ context = SSLContext()
240+ self._ssl = ssl = SSL.Connection(context, self._sock)
241+ if state == SSL_CLIENT:
242+ ssl.set_connect_state()
243+ elif state == SSL_SERVER:
244+ ssl.set_accept_state()
245+ self._rbuf = StringIO()
246+
247+ def disable_ssl(self):
248+ self._ssl.shutdown()
249+ self._ssl = None
250+ del self._rbuf
251
252 # Close methods
253
254 def close(self):
255 self.flush()
256+ if self._ssl:
257+ self.disable_ssl()
258 try:
259 self._sock.shutdown(_socket.SHUT_RDWR)
260 except SocketError:
261@@ -384,3 +509,20 @@
262 family = _socket.AF_UNIX
263 else:
264 has_unix = False
265+
266+
267+try:
268+ from OpenSSL import SSL
269+ from OpenSSL.SSL import ZeroReturnError
270+except ImportError:
271+ has_ssl = False
272+else:
273+ has_ssl = True
274+ class SSLContext(object):
275+
276+ def __new__(cls, pemfile=None, certfile=None):
277+ context = SSL.Context(SSL.SSLv23_METHOD)
278+ if pemfile:
279+ context.use_privatekey_file(pemfile)
280+ context.use_certificate_file(certfile)
281+ return context

Subscribers

People subscribed via source and target branches