add some basic logging output to certmaster
[certmaster.git] / certmaster / SSLConnection.py
1 # Higher-level SSL objects used by rpclib
2 #
3 # Copyright (c) 2002 Red Hat, Inc.
4 #
5 # Author: Mihai Ibanescu <misa@redhat.com>
6 # Modifications by Dan Williams <dcbw@redhat.com>
7
8
9 from OpenSSL import SSL
10 import time, socket, select
11 from CommonErrors import canIgnoreSSLError
12
13
14 class SSLConnection:
15 """
16 This whole class exists just to filter out a parameter
17 passed in to the shutdown() method in SimpleXMLRPC.doPOST()
18 """
19
20 DEFAULT_TIMEOUT = 20
21
22 def __init__(self, conn):
23 """
24 Connection is not yet a new-style class,
25 so I'm making a proxy instead of subclassing.
26 """
27 self.__dict__["conn"] = conn
28 self.__dict__["close_refcount"] = 0
29 self.__dict__["closed"] = False
30 self.__dict__["timeout"] = self.DEFAULT_TIMEOUT
31
32 def __del__(self):
33 self.__dict__["conn"].close()
34
35 def __getattr__(self,name):
36 return getattr(self.__dict__["conn"], name)
37
38 def __setattr__(self,name, value):
39 setattr(self.__dict__["conn"], name, value)
40
41 def settimeout(self, timeout):
42 if timeout == None:
43 self.__dict__["timeout"] = self.DEFAULT_TIMEOUT
44 else:
45 self.__dict__["timeout"] = timeout
46 self.__dict__["conn"].settimeout(timeout)
47
48 def shutdown(self, how=1):
49 """
50 SimpleXMLRpcServer.doPOST calls shutdown(1),
51 and Connection.shutdown() doesn't take
52 an argument. So we just discard the argument.
53 """
54 self.__dict__["conn"].shutdown()
55
56 def accept(self):
57 """
58 This is the other part of the shutdown() workaround.
59 Since servers create new sockets, we have to infect
60 them with our magic. :)
61 """
62 c, a = self.__dict__["conn"].accept()
63 return (SSLConnection(c), a)
64
65 def makefile(self, mode, bufsize):
66 """
67 We need to use socket._fileobject Because SSL.Connection
68 doesn't have a 'dup'. Not exactly sure WHY this is, but
69 this is backed up by comments in socket.py and SSL/connection.c
70
71 Since httplib.HTTPSResponse/HTTPConnection depend on the
72 socket being duplicated when they close it, we refcount the
73 socket object and don't actually close until its count is 0.
74 """
75 self.__dict__["close_refcount"] = self.__dict__["close_refcount"] + 1
76 return PlgFileObject(self, mode, bufsize)
77
78 def close(self):
79 if self.__dict__["closed"]:
80 return
81 self.__dict__["close_refcount"] = self.__dict__["close_refcount"] - 1
82 if self.__dict__["close_refcount"] == 0:
83 self.shutdown()
84 self.__dict__["conn"].close()
85 self.__dict__["closed"] = True
86
87 def sendall(self, data, flags=0):
88 """
89 - Use select() to simulate a socket timeout without setting the socket
90 to non-blocking mode.
91 - Don't use pyOpenSSL's sendall() either, since it just loops on WantRead
92 or WantWrite, consuming 100% CPU, and never times out.
93 """
94 timeout = self.__dict__["timeout"]
95 con = self.__dict__["conn"]
96 (read, write, excpt) = select.select([], [con], [], timeout)
97 if not con in write:
98 raise socket.timeout((110, "Operation timed out."))
99
100 starttime = time.time()
101 origlen = len(data)
102 sent = -1
103 while len(data):
104 curtime = time.time()
105 if curtime - starttime > timeout:
106 raise socket.timeout((110, "Operation timed out."))
107
108 try:
109 sent = con.send(data, flags)
110 except SSL.SysCallError, e:
111 if e[0] == 32: # Broken Pipe
112 self.close()
113 sent = 0
114 else:
115 raise socket.error(e)
116 except (SSL.WantWriteError, SSL.WantReadError):
117 time.sleep(0.2)
118 continue
119
120 data = data[sent:]
121 return origlen - len(data)
122
123 def recv(self, bufsize, flags=0):
124 """
125 Use select() to simulate a socket timeout without setting the socket
126 to non-blocking mode
127 """
128 timeout = self.__dict__["timeout"]
129 con = self.__dict__["conn"]
130 (read, write, excpt) = select.select([con], [], [], timeout)
131 if not con in read:
132 raise socket.timeout((110, "Operation timed out."))
133
134 starttime = time.time()
135 while True:
136 curtime = time.time()
137 if curtime - starttime > timeout:
138 raise socket.timeout((110, "Operation timed out."))
139
140 try:
141 return con.recv(bufsize, flags)
142 except SSL.ZeroReturnError:
143 return None
144 except SSL.WantReadError:
145 time.sleep(0.2)
146 except Exception, e:
147 if canIgnoreSSLError(e):
148 return None
149 else:
150 raise e
151 return None
152
153
154 class PlgFileObject(socket._fileobject):
155 def close(self):
156 """
157 socket._fileobject doesn't actually _close_ the socket,
158 which we want it to do, so we have to override.
159 """
160 try:
161 if self._sock:
162 self.flush()
163 self._sock.close()
164 finally:
165 self._sock = None