ia64/xen-unstable

view tools/python/xen/web/connection.py @ 17637:d5f24c99189b

Further minor ssl relo fixes.
Signed-off-by: Zhigang Wang <zhigang.x.wang@oracle.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue May 13 09:20:23 2008 +0100 (2008-05-13)
parents f8ce6e3d86c7
children 24af58657d8e
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU Lesser General Public License as published by
4 # the Free Software Foundation; either version 2.1 of the License, or
5 # (at your option) any later version.
6 #
7 # This library is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU Lesser General Public License for more details.
11 #
12 # You should have received a copy of the GNU Lesser General Public License
13 # along with this library; if not, write to the Free Software
14 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15 #============================================================================
16 # Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
17 # Copyright (C) 2005 XenSource Ltd.
18 #============================================================================
20 import sys
21 import os
22 import threading
23 import socket
24 import fcntl
26 from errno import EAGAIN, EINTR, EWOULDBLOCK
28 try:
29 from OpenSSL import SSL
30 except ImportError:
31 pass
33 from xen.xend.XendLogging import log
35 """General classes to support server and client sockets, without
36 specifying what kind of socket they are. There are subclasses
37 for TCP and unix-domain sockets (see tcp.py and unix.py).
38 """
40 BUFFER_SIZE = 1024
41 BACKLOG = 5
44 class SocketServerConnection:
45 """An accepted connection to a server.
46 """
48 def __init__(self, sock, protocol_class):
49 self.sock = sock
50 self.protocol = protocol_class()
51 self.protocol.setTransport(self)
52 threading.Thread(target=self.main).start()
55 def main(self):
56 try:
57 while True:
58 try:
59 data = self.sock.recv(BUFFER_SIZE)
60 if data == '':
61 break
62 if self.protocol.dataReceived(data):
63 break
64 except socket.error, ex:
65 if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
66 break
67 finally:
68 try:
69 self.sock.close()
70 except:
71 pass
74 def close(self):
75 self.sock.close()
78 def write(self, data):
79 self.sock.send(data)
82 class SocketListener:
83 """A server socket, running listen in a thread.
84 Accepts connections and runs a thread for each one.
85 """
87 def __init__(self, protocol_class):
88 self.protocol_class = protocol_class
89 self.sock = self.createSocket()
90 threading.Thread(target=self.main).start()
93 def close(self):
94 try:
95 self.sock.close()
96 except:
97 pass
100 def createSocket(self):
101 raise NotImplementedError()
104 def acceptConnection(self, sock, protocol, addr):
105 raise NotImplementedError()
108 def main(self):
109 try:
110 fcntl.fcntl(self.sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
111 self.sock.listen(BACKLOG)
113 while True:
114 try:
115 (sock, addr) = self.sock.accept()
116 self.acceptConnection(sock, addr)
117 except socket.error, ex:
118 if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
119 break
120 finally:
121 self.close()
124 class SSLSocketServerConnection(SocketServerConnection):
125 """An SSL aware accepted connection to a server.
127 As pyOpenSSL SSL.Connection fileno() method just retrieve the file
128 descriptor number for the underlying socket, direct read/write to the file
129 descriptor will result no data encrypted.
131 recv2fd() and fd2send() are simple wrappers for functions who need direct
132 read/write to a file descriptor rather than a socket like object.
134 To use recv2fd(), you can create a pipe and start a thread to transfer all
135 received data to one end of the pipe, then read from the other end:
137 p2cread, p2cwrite = os.pipe()
138 threading.Thread(target=connection.SSLSocketServerConnection.recv2fd,
139 args=(sock, p2cwrite)).start()
140 os.read(p2cread, 1024)
142 To use fd2send():
144 p2cread, p2cwrite = os.pipe()
145 threading.Thread(target=connection.SSLSocketServerConnection.fd2send,
146 args=(sock, p2cread)).start()
147 os.write(p2cwrite, "data")
148 """
150 def __init__(self, sock, protocol_class):
151 SocketServerConnection.__init__(self, sock, protocol_class)
154 def main(self):
155 try:
156 while True:
157 try:
158 data = self.sock.recv(BUFFER_SIZE)
159 if data == "":
160 break
161 if self.protocol.dataReceived(data):
162 break
163 except socket.error, ex:
164 if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
165 break
166 except (SSL.WantReadError, SSL.WantWriteError, \
167 SSL.WantX509LookupError):
168 # The operation did not complete; the same I/O method
169 # should be called again.
170 continue
171 except SSL.ZeroReturnError:
172 # The SSL Connection has been closed.
173 break
174 except SSL.SysCallError, (retval, desc):
175 if ((retval == -1 and desc == "Unexpected EOF")
176 or retval > 0):
177 # The SSL Connection is lost.
178 break
179 log.debug("SSL SysCallError:%d:%s" % (retval, desc))
180 break
181 except SSL.Error, e:
182 # other SSL errors
183 log.debug("SSL Error:%s" % e)
184 break
185 finally:
186 try:
187 self.sock.close()
188 except:
189 pass
192 def recv2fd(sock, fd):
193 try:
194 while True:
195 try:
196 data = sock.recv(BUFFER_SIZE)
197 if data == "":
198 break
199 count = 0
200 while count < len(data):
201 try:
202 nbytes = os.write(fd, data[count:])
203 count += nbytes
204 except os.error, ex:
205 if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
206 raise
207 except socket.error, ex:
208 if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
209 break
210 except (SSL.WantReadError, SSL.WantWriteError, \
211 SSL.WantX509LookupError):
212 # The operation did not complete; the same I/O method
213 # should be called again.
214 continue
215 except SSL.ZeroReturnError:
216 # The SSL Connection has been closed.
217 break
218 except SSL.SysCallError, (retval, desc):
219 if ((retval == -1 and desc == "Unexpected EOF")
220 or retval > 0):
221 # The SSL Connection is lost.
222 break
223 log.debug("SSL SysCallError:%d:%s" % (retval, desc))
224 break
225 except SSL.Error, e:
226 # other SSL errors
227 log.debug("SSL Error:%s" % e)
228 break
229 finally:
230 try:
231 sock.close()
232 os.close(fd)
233 except:
234 pass
236 recv2fd = staticmethod(recv2fd)
239 def fd2send(sock, fd):
240 try:
241 while True:
242 try:
243 data = os.read(fd, BUFFER_SIZE)
244 if data == "":
245 break
246 count = 0
247 while count < len(data):
248 try:
249 nbytes = sock.send(data[count:])
250 count += nbytes
251 except socket.error, ex:
252 if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
253 raise
254 except (SSL.WantReadError, SSL.WantWriteError, \
255 SSL.WantX509LookupError):
256 # The operation did not complete; the same I/O method
257 # should be called again.
258 continue
259 except SSL.ZeroReturnError:
260 # The SSL Connection has been closed.
261 raise
262 except SSL.SysCallError, (retval, desc):
263 if not (retval == -1 and data == ""):
264 # errors when writing empty strings are expected
265 # and can be ignored
266 log.debug("SSL SysCallError:%d:%s" % (retval, desc))
267 raise
268 except SSL.Error, e:
269 # other SSL errors
270 log.debug("SSL Error:%s" % e)
271 raise
272 except os.error, ex:
273 if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
274 break
275 finally:
276 try:
277 sock.close()
278 os.close(fd)
279 except:
280 pass
282 fd2send = staticmethod(fd2send)
285 def hostAllowed(addrport, hosts_allowed):
286 if hosts_allowed is None:
287 return True
288 else:
289 fqdn = socket.getfqdn(addrport[0])
290 for h in hosts_allowed:
291 if h.match(fqdn) or h.match(addrport[0]):
292 return True
293 log.warn("Rejected connection from %s (%s).", addrport[0], fqdn)
294 return False