Package qpid :: Package messaging :: Module transports
[frames] | no frames]

Source Code for Module qpid.messaging.transports

  1  # 
  2  # Licensed to the Apache Software Foundation (ASF) under one 
  3  # or more contributor license agreements.  See the NOTICE file 
  4  # distributed with this work for additional information 
  5  # regarding copyright ownership.  The ASF licenses this file 
  6  # to you under the Apache License, Version 2.0 (the 
  7  # "License"); you may not use this file except in compliance 
  8  # with the License.  You may obtain a copy of the License at 
  9  # 
 10  #   http://www.apache.org/licenses/LICENSE-2.0 
 11  # 
 12  # Unless required by applicable law or agreed to in writing, 
 13  # software distributed under the License is distributed on an 
 14  # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 15  # KIND, either express or implied.  See the License for the 
 16  # specific language governing permissions and limitations 
 17  # under the License. 
 18  # 
 19   
 20  import socket 
 21  from qpid.util import connect 
 22   
 23  TRANSPORTS = {} 
 24   
25 -class SocketTransport:
26
27 - def __init__(self, conn, host, port):
28 self.socket = connect(host, port) 29 if conn.tcp_nodelay: 30 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
31
32 - def fileno(self):
33 return self.socket.fileno()
34
35 -class tcp(SocketTransport):
36
37 - def reading(self, reading):
38 return reading
39
40 - def writing(self, writing):
41 return writing
42
43 - def send(self, bytes):
44 return self.socket.send(bytes)
45
46 - def recv(self, n):
47 return self.socket.recv(n)
48
49 - def close(self):
50 self.socket.close()
51 52 TRANSPORTS["tcp"] = tcp 53 54 try: 55 from ssl import wrap_socket, SSLError, SSL_ERROR_WANT_READ, \ 56 SSL_ERROR_WANT_WRITE, CERT_REQUIRED, CERT_NONE 57 except ImportError: 58 59 ## try the older python SSL api: 60 from socket import ssl 61
62 - class old_ssl(SocketTransport):
63 - def __init__(self, conn, host, port):
64 SocketTransport.__init__(self, conn, host, port) 65 # Bug (QPID-4337): this is the "old" version of python SSL. 66 # The private key is required. If a certificate is given, but no 67 # keyfile, assume the key is contained in the certificate 68 ssl_keyfile = conn.ssl_keyfile 69 ssl_certfile = conn.ssl_certfile 70 if ssl_certfile and not ssl_keyfile: 71 ssl_keyfile = ssl_certfile 72 73 # this version of SSL does NOT perform certificate validation. If the 74 # connection has been configured with CA certs (via ssl_trustfile), then 75 # the application expects the certificate to be validated against the 76 # supplied CA certs. Since this version cannot validate, the peer cannot 77 # be trusted. 78 if conn.ssl_trustfile: 79 raise socket.error("This version of Python does not support verification of the peer's certificate.") 80 81 self.ssl = ssl(self.socket, keyfile=ssl_keyfile, certfile=ssl_certfile) 82 self.socket.setblocking(1)
83
84 - def reading(self, reading):
85 return reading
86
87 - def writing(self, writing):
88 return writing
89
90 - def recv(self, n):
91 return self.ssl.read(n)
92
93 - def send(self, s):
94 return self.ssl.write(s)
95
96 - def close(self):
97 self.socket.close()
98 99 TRANSPORTS["ssl"] = old_ssl 100 TRANSPORTS["tcp+tls"] = old_ssl 101 102 else:
103 - class tls(SocketTransport):
104
105 - def __init__(self, conn, host, port):
106 SocketTransport.__init__(self, conn, host, port) 107 if conn.ssl_trustfile: 108 validate = CERT_REQUIRED 109 else: 110 validate = CERT_NONE 111 112 self.tls = wrap_socket(self.socket, keyfile=conn.ssl_keyfile, 113 certfile=conn.ssl_certfile, 114 ca_certs=conn.ssl_trustfile, 115 cert_reqs=validate) 116 117 if validate == CERT_REQUIRED and not conn.ssl_skip_hostname_check: 118 match_found = False 119 peer_cert = self.tls.getpeercert() 120 if peer_cert: 121 peer_names = [] 122 if 'subjectAltName' in peer_cert: 123 for san in peer_cert['subjectAltName']: 124 if san[0] == 'DNS': 125 peer_names.append(san[1].lower()) 126 if 'subject' in peer_cert: 127 for sub in peer_cert['subject']: 128 while isinstance(sub, tuple) and isinstance(sub[0],tuple): 129 sub = sub[0] # why the extra level of indirection??? 130 if sub[0] == 'commonName': 131 peer_names.append(sub[1].lower()) 132 for pattern in peer_names: 133 if _match_dns_pattern( host.lower(), pattern ): 134 #print "Match found %s" % pattern 135 match_found = True 136 break 137 if not match_found: 138 raise SSLError("Connection hostname '%s' does not match names from peer certificate: %s" % (host, peer_names)) 139 140 self.socket.setblocking(0) 141 self.state = None 142 # See qpid-4872: need to store the parameters last passed to 143 # tls.recv_into() and tls.write() in case the calls fail with an 144 # SSL_ERROR_WANT_* error and we have to retry the call. 145 self.write_retry = None # buffer passed to last call of tls.write() 146 self.read_retry = None # buffer passed to last call of tls.recv_into()
147
148 - def reading(self, reading):
149 if self.state is None: 150 return reading 151 else: 152 return self.state == SSL_ERROR_WANT_READ
153
154 - def writing(self, writing):
155 if self.state is None: 156 return writing 157 else: 158 return self.state == SSL_ERROR_WANT_WRITE
159
160 - def send(self, bytes):
161 if self.write_retry is None: 162 self.write_retry = bytes 163 self._clear_state() 164 try: 165 n = self.tls.write( self.write_retry ) 166 self.write_retry = None 167 return n 168 except SSLError, e: 169 if self._update_state(e.args[0]): 170 # will retry on next invokation 171 return 0 172 self.write_retry = None 173 raise 174 except: 175 self.write_retry = None 176 raise
177
178 - def recv(self, n):
179 if self.read_retry == None: 180 self.read_retry = bytearray( n ) 181 self._clear_state() 182 try: 183 n = self.tls.recv_into( self.read_retry ) 184 r = str(self.read_retry[:n]) 185 self.read_retry = None 186 return r 187 except SSLError, e: 188 if self._update_state(e.args[0]): 189 # will retry on next invokation 190 return None 191 self.read_retry = None 192 raise 193 except: 194 self.read_retry = None 195 raise
196
197 - def _clear_state(self):
198 self.state = None
199
200 - def _update_state(self, code):
201 if code in (SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE): 202 self.state = code 203 return True 204 else: 205 return False
206
207 - def close(self):
208 self.socket.setblocking(1) 209 # this closes the underlying socket 210 self.tls.close()
211
212 - def _match_dns_pattern( hostname, pattern ):
213 """ For checking the hostnames provided by the peer's certificate 214 """ 215 if pattern.find("*") == -1: 216 return hostname == pattern 217 218 # DNS wildcarded pattern - see RFC2818 219 h_labels = hostname.split(".") 220 p_labels = pattern.split(".") 221 222 while h_labels and p_labels: 223 if p_labels[0].find("*") == -1: 224 if p_labels[0] != h_labels[0]: 225 return False 226 else: 227 p = p_labels[0].split("*") 228 if not h_labels[0].startswith(p[0]): 229 return False 230 if not h_labels[0].endswith(p[1]): 231 return False 232 h_labels.pop(0) 233 p_labels.pop(0) 234 235 return not h_labels and not p_labels
236 237 238 TRANSPORTS["ssl"] = tls 239 TRANSPORTS["tcp+tls"] = tls 240