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 SSLError("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
143 - def reading(self, reading):
144 if self.state is None: 145 return reading 146 else: 147 return self.state == SSL_ERROR_WANT_READ
148
149 - def writing(self, writing):
150 if self.state is None: 151 return writing 152 else: 153 return self.state == SSL_ERROR_WANT_WRITE
154
155 - def send(self, bytes):
156 self._clear_state() 157 try: 158 return self.tls.write(bytes) 159 except SSLError, e: 160 if self._update_state(e.args[0]): 161 return 0 162 else: 163 raise
164
165 - def recv(self, n):
166 self._clear_state() 167 try: 168 return self.tls.read(n) 169 except SSLError, e: 170 if self._update_state(e.args[0]): 171 return None 172 else: 173 raise
174
175 - def _clear_state(self):
176 self.state = None
177
178 - def _update_state(self, code):
179 if code in (SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE): 180 self.state = code 181 return True 182 else: 183 return False
184
185 - def close(self):
186 self.socket.setblocking(1) 187 # this closes the underlying socket 188 self.tls.close()
189
190 - def _match_dns_pattern( hostname, pattern ):
191 """ For checking the hostnames provided by the peer's certificate 192 """ 193 if pattern.find("*") == -1: 194 return hostname == pattern 195 196 # DNS wildcarded pattern - see RFC2818 197 h_labels = hostname.split(".") 198 p_labels = pattern.split(".") 199 200 while h_labels and p_labels: 201 if p_labels[0].find("*") == -1: 202 if p_labels[0] != h_labels[0]: 203 return False 204 else: 205 p = p_labels[0].split("*") 206 if not h_labels[0].startswith(p[0]): 207 return False 208 if not h_labels[0].endswith(p[1]): 209 return False 210 h_labels.pop(0) 211 p_labels.pop(0) 212 213 return not h_labels and not p_labels
214 215 216 TRANSPORTS["ssl"] = tls 217 TRANSPORTS["tcp+tls"] = tls 218