Package proton :: Module _transport
[frames] | no frames]

Source Code for Module proton._transport

  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  from __future__ import absolute_import 
 21   
 22  from cproton import PN_SASL_AUTH, PN_SASL_PERM, PN_SASL_SYS, PN_SSL_RESUME_REUSED, PN_SASL_NONE, PN_SSL_SHA1, \ 
 23      PN_SSL_CERT_SUBJECT_COUNTRY_NAME, PN_SASL_OK, PN_SSL_RESUME_UNKNOWN, PN_EOS, PN_SSL_ANONYMOUS_PEER, PN_SSL_MD5, \ 
 24      PN_SSL_CERT_SUBJECT_COMMON_NAME, PN_SSL_VERIFY_PEER, PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY, PN_SSL_MODE_SERVER, \ 
 25      PN_TRACE_DRV, PN_TRACE_RAW, pn_transport, PN_SSL_SHA256, PN_TRACE_FRM, PN_SSL_MODE_CLIENT, PN_SASL_TEMP, \ 
 26      PN_SSL_SHA512, PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT, PN_OK, PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE, \ 
 27      PN_SSL_VERIFY_PEER_NAME, PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME, PN_SSL_RESUME_NEW, PN_TRACE_OFF, \ 
 28      pn_transport_get_channel_max, pn_transport_capacity, pn_transport_push, pn_transport_get_user, pn_transport_tick, \ 
 29      pn_transport_set_max_frame, pn_transport_attachments, pn_transport_unbind, pn_transport_peek, \ 
 30      pn_transport_set_channel_max, pn_transport_close_tail, pn_transport_condition, pn_transport_is_encrypted, \ 
 31      pn_transport_get_frames_input, pn_transport_bind, pn_transport_closed, pn_transport_get_idle_timeout, \ 
 32      pn_transport_get_remote_idle_timeout, pn_transport_get_frames_output, pn_transport_pending, \ 
 33      pn_transport_set_pytracer, pn_transport_close_head, pn_transport_get_remote_max_frame, \ 
 34      pn_transport_is_authenticated, pn_transport_set_idle_timeout, pn_transport_log, pn_transport_get_pytracer, \ 
 35      pn_transport_require_auth, pn_transport_get_max_frame, pn_transport_set_server, pn_transport_remote_channel_max, \ 
 36      pn_transport_require_encryption, pn_transport_pop, pn_transport_connection, \ 
 37      pn_sasl, pn_sasl_set_allow_insecure_mechs, pn_sasl_outcome, pn_transport_error, pn_sasl_get_user, \ 
 38      pn_sasl_extended, pn_sasl_done, pn_sasl_get_allow_insecure_mechs, pn_sasl_allowed_mechs, \ 
 39      pn_sasl_config_name, pn_sasl_config_path, \ 
 40      pn_ssl, pn_ssl_init, pn_ssl_domain_allow_unsecured_client, pn_ssl_domain_free, \ 
 41      pn_ssl_domain, pn_transport_trace, pn_ssl_resume_status, pn_sasl_get_mech, \ 
 42      pn_ssl_domain_set_trusted_ca_db, pn_ssl_get_remote_subject_subfield, pn_ssl_present, \ 
 43      pn_ssl_get_remote_subject, pn_ssl_domain_set_credentials, pn_ssl_domain_set_peer_authentication, \ 
 44      pn_ssl_get_peer_hostname, pn_ssl_set_peer_hostname, pn_ssl_get_cipher_name, pn_ssl_get_cert_fingerprint, \ 
 45      pn_ssl_get_protocol_name, \ 
 46      pn_error_text 
 47   
 48  from ._common import millis2secs, secs2millis, unicode2utf8, utf82unicode 
 49  from ._condition import cond2obj 
 50  from ._exceptions import EXCEPTIONS, TransportException, SessionException, SSLException, SSLUnavailable 
 51  from ._wrapper import Wrapper 
52 53 54 -class TraceAdapter:
55
56 - def __init__(self, tracer):
57 self.tracer = tracer
58
59 - def __call__(self, trans_impl, message):
60 self.tracer(Transport.wrap(trans_impl), message)
61
62 63 -class Transport(Wrapper):
64 TRACE_OFF = PN_TRACE_OFF 65 TRACE_DRV = PN_TRACE_DRV 66 TRACE_FRM = PN_TRACE_FRM 67 TRACE_RAW = PN_TRACE_RAW 68 69 CLIENT = 1 70 SERVER = 2 71 72 @staticmethod
73 - def wrap(impl):
74 if impl is None: 75 return None 76 else: 77 return Transport(_impl=impl)
78
79 - def __init__(self, mode=None, _impl=pn_transport):
80 Wrapper.__init__(self, _impl, pn_transport_attachments) 81 if mode == Transport.SERVER: 82 pn_transport_set_server(self._impl) 83 elif mode is None or mode == Transport.CLIENT: 84 pass 85 else: 86 raise TransportException("Cannot initialise Transport from mode: %s" % str(mode))
87
88 - def _init(self):
89 self._sasl = None 90 self._ssl = None
91
92 - def _check(self, err):
93 if err < 0: 94 exc = EXCEPTIONS.get(err, TransportException) 95 raise exc("[%s]: %s" % (err, pn_error_text(pn_transport_error(self._impl)))) 96 else: 97 return err
98
99 - def _set_tracer(self, tracer):
100 pn_transport_set_pytracer(self._impl, TraceAdapter(tracer))
101
102 - def _get_tracer(self):
103 adapter = pn_transport_get_pytracer(self._impl) 104 if adapter: 105 return adapter.tracer 106 else: 107 return None
108 109 tracer = property(_get_tracer, _set_tracer, 110 doc=""" 111 A callback for trace logging. The callback is passed the transport and log message. 112 """) 113
114 - def log(self, message):
115 pn_transport_log(self._impl, message)
116
117 - def require_auth(self, bool):
118 pn_transport_require_auth(self._impl, bool)
119 120 @property
121 - def authenticated(self):
122 return pn_transport_is_authenticated(self._impl)
123
124 - def require_encryption(self, bool):
125 pn_transport_require_encryption(self._impl, bool)
126 127 @property
128 - def encrypted(self):
129 return pn_transport_is_encrypted(self._impl)
130 131 @property
132 - def user(self):
133 return pn_transport_get_user(self._impl)
134
135 - def bind(self, connection):
136 """Assign a connection to the transport""" 137 self._check(pn_transport_bind(self._impl, connection._impl))
138
139 - def unbind(self):
140 """Release the connection""" 141 self._check(pn_transport_unbind(self._impl))
142
143 - def trace(self, n):
144 pn_transport_trace(self._impl, n)
145
146 - def tick(self, now):
147 """Process any timed events (like heartbeat generation). 148 now = seconds since epoch (float). 149 """ 150 return millis2secs(pn_transport_tick(self._impl, secs2millis(now)))
151
152 - def capacity(self):
153 c = pn_transport_capacity(self._impl) 154 if c >= PN_EOS: 155 return c 156 else: 157 return self._check(c)
158
159 - def push(self, binary):
160 n = self._check(pn_transport_push(self._impl, binary)) 161 if n != len(binary): 162 raise OverflowError("unable to process all bytes: %s, %s" % (n, len(binary)))
163
164 - def close_tail(self):
165 self._check(pn_transport_close_tail(self._impl))
166
167 - def pending(self):
168 p = pn_transport_pending(self._impl) 169 if p >= PN_EOS: 170 return p 171 else: 172 return self._check(p)
173
174 - def peek(self, size):
175 cd, out = pn_transport_peek(self._impl, size) 176 if cd == PN_EOS: 177 return None 178 else: 179 self._check(cd) 180 return out
181
182 - def pop(self, size):
183 pn_transport_pop(self._impl, size)
184
185 - def close_head(self):
186 self._check(pn_transport_close_head(self._impl))
187 188 @property
189 - def closed(self):
190 return pn_transport_closed(self._impl)
191 192 # AMQP 1.0 max-frame-size
193 - def _get_max_frame_size(self):
194 return pn_transport_get_max_frame(self._impl)
195
196 - def _set_max_frame_size(self, value):
197 pn_transport_set_max_frame(self._impl, value)
198 199 max_frame_size = property(_get_max_frame_size, _set_max_frame_size, 200 doc=""" 201 Sets the maximum size for received frames (in bytes). 202 """) 203 204 @property
205 - def remote_max_frame_size(self):
206 return pn_transport_get_remote_max_frame(self._impl)
207
208 - def _get_channel_max(self):
209 return pn_transport_get_channel_max(self._impl)
210
211 - def _set_channel_max(self, value):
212 if pn_transport_set_channel_max(self._impl, value): 213 raise SessionException("Too late to change channel max.")
214 215 channel_max = property(_get_channel_max, _set_channel_max, 216 doc=""" 217 Sets the maximum channel that may be used on the transport. 218 """) 219 220 @property
221 - def remote_channel_max(self):
222 return pn_transport_remote_channel_max(self._impl)
223 224 # AMQP 1.0 idle-time-out
225 - def _get_idle_timeout(self):
226 return millis2secs(pn_transport_get_idle_timeout(self._impl))
227
228 - def _set_idle_timeout(self, sec):
229 pn_transport_set_idle_timeout(self._impl, secs2millis(sec))
230 231 idle_timeout = property(_get_idle_timeout, _set_idle_timeout, 232 doc=""" 233 The idle timeout of the connection (float, in seconds). 234 """) 235 236 @property
237 - def remote_idle_timeout(self):
238 return millis2secs(pn_transport_get_remote_idle_timeout(self._impl))
239 240 @property
241 - def frames_output(self):
242 return pn_transport_get_frames_output(self._impl)
243 244 @property
245 - def frames_input(self):
246 return pn_transport_get_frames_input(self._impl)
247
248 - def sasl(self):
249 return SASL(self)
250
251 - def ssl(self, domain=None, session_details=None):
252 # SSL factory (singleton for this transport) 253 if not self._ssl: 254 self._ssl = SSL(self, domain, session_details) 255 return self._ssl
256 257 @property
258 - def condition(self):
259 return cond2obj(pn_transport_condition(self._impl))
260 261 @property
262 - def connection(self):
263 from . import _endpoints 264 return _endpoints.Connection.wrap(pn_transport_connection(self._impl))
265
266 267 -class SASLException(TransportException):
268 pass
269
270 271 -class SASL(Wrapper):
272 OK = PN_SASL_OK 273 AUTH = PN_SASL_AUTH 274 SYS = PN_SASL_SYS 275 PERM = PN_SASL_PERM 276 TEMP = PN_SASL_TEMP 277 278 @staticmethod
279 - def extended():
280 return pn_sasl_extended()
281
282 - def __init__(self, transport):
283 Wrapper.__init__(self, transport._impl, pn_transport_attachments) 284 self._sasl = pn_sasl(transport._impl)
285
286 - def _check(self, err):
287 if err < 0: 288 exc = EXCEPTIONS.get(err, SASLException) 289 raise exc("[%s]" % (err)) 290 else: 291 return err
292 293 @property
294 - def user(self):
295 return pn_sasl_get_user(self._sasl)
296 297 @property
298 - def mech(self):
299 return pn_sasl_get_mech(self._sasl)
300 301 @property
302 - def outcome(self):
303 outcome = pn_sasl_outcome(self._sasl) 304 if outcome == PN_SASL_NONE: 305 return None 306 else: 307 return outcome
308
309 - def allowed_mechs(self, mechs):
310 pn_sasl_allowed_mechs(self._sasl, unicode2utf8(mechs))
311
313 return pn_sasl_get_allow_insecure_mechs(self._sasl)
314
315 - def _set_allow_insecure_mechs(self, insecure):
316 pn_sasl_set_allow_insecure_mechs(self._sasl, insecure)
317 318 allow_insecure_mechs = property(_get_allow_insecure_mechs, _set_allow_insecure_mechs, 319 doc=""" 320 Allow unencrypted cleartext passwords (PLAIN mech) 321 """) 322
323 - def done(self, outcome):
324 pn_sasl_done(self._sasl, outcome)
325
326 - def config_name(self, name):
327 pn_sasl_config_name(self._sasl, name)
328
329 - def config_path(self, path):
330 pn_sasl_config_path(self._sasl, path)
331
332 333 -class SSLDomain(object):
334 MODE_CLIENT = PN_SSL_MODE_CLIENT 335 MODE_SERVER = PN_SSL_MODE_SERVER 336 VERIFY_PEER = PN_SSL_VERIFY_PEER 337 VERIFY_PEER_NAME = PN_SSL_VERIFY_PEER_NAME 338 ANONYMOUS_PEER = PN_SSL_ANONYMOUS_PEER 339
340 - def __init__(self, mode):
341 self._domain = pn_ssl_domain(mode) 342 if self._domain is None: 343 raise SSLUnavailable()
344
345 - def _check(self, err):
346 if err < 0: 347 exc = EXCEPTIONS.get(err, SSLException) 348 raise exc("SSL failure.") 349 else: 350 return err
351
352 - def set_credentials(self, cert_file, key_file, password):
353 return self._check(pn_ssl_domain_set_credentials(self._domain, 354 cert_file, key_file, 355 password))
356
357 - def set_trusted_ca_db(self, certificate_db):
358 return self._check(pn_ssl_domain_set_trusted_ca_db(self._domain, 359 certificate_db))
360
361 - def set_peer_authentication(self, verify_mode, trusted_CAs=None):
362 return self._check(pn_ssl_domain_set_peer_authentication(self._domain, 363 verify_mode, 364 trusted_CAs))
365
366 - def allow_unsecured_client(self):
367 return self._check(pn_ssl_domain_allow_unsecured_client(self._domain))
368
369 - def __del__(self):
370 pn_ssl_domain_free(self._domain)
371
372 373 -class SSL(object):
374 375 @staticmethod
376 - def present():
377 return pn_ssl_present()
378
379 - def _check(self, err):
380 if err < 0: 381 exc = EXCEPTIONS.get(err, SSLException) 382 raise exc("SSL failure.") 383 else: 384 return err
385
386 - def __new__(cls, transport, domain, session_details=None):
387 """Enforce a singleton SSL object per Transport""" 388 if transport._ssl: 389 # unfortunately, we've combined the allocation and the configuration in a 390 # single step. So catch any attempt by the application to provide what 391 # may be a different configuration than the original (hack) 392 ssl = transport._ssl 393 if (domain and (ssl._domain is not domain) or 394 session_details and (ssl._session_details is not session_details)): 395 raise SSLException("Cannot re-configure existing SSL object!") 396 else: 397 obj = super(SSL, cls).__new__(cls) 398 obj._domain = domain 399 obj._session_details = session_details 400 session_id = None 401 if session_details: 402 session_id = session_details.get_session_id() 403 obj._ssl = pn_ssl(transport._impl) 404 if obj._ssl is None: 405 raise SSLUnavailable() 406 if domain: 407 pn_ssl_init(obj._ssl, domain._domain, session_id) 408 transport._ssl = obj 409 return transport._ssl
410
411 - def cipher_name(self):
412 rc, name = pn_ssl_get_cipher_name(self._ssl, 128) 413 if rc: 414 return name 415 return None
416
417 - def protocol_name(self):
418 rc, name = pn_ssl_get_protocol_name(self._ssl, 128) 419 if rc: 420 return name 421 return None
422 423 SHA1 = PN_SSL_SHA1 424 SHA256 = PN_SSL_SHA256 425 SHA512 = PN_SSL_SHA512 426 MD5 = PN_SSL_MD5 427 428 CERT_COUNTRY_NAME = PN_SSL_CERT_SUBJECT_COUNTRY_NAME 429 CERT_STATE_OR_PROVINCE = PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE 430 CERT_CITY_OR_LOCALITY = PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY 431 CERT_ORGANIZATION_NAME = PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME 432 CERT_ORGANIZATION_UNIT = PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT 433 CERT_COMMON_NAME = PN_SSL_CERT_SUBJECT_COMMON_NAME 434
435 - def get_cert_subject_subfield(self, subfield_name):
436 subfield_value = pn_ssl_get_remote_subject_subfield(self._ssl, subfield_name) 437 return subfield_value
438
439 - def get_cert_subject(self):
440 subject = pn_ssl_get_remote_subject(self._ssl) 441 return subject
442
444 # Pass in an unhandled enum 445 return self.get_cert_subject_subfield(10)
446 447 # Convenience functions for obtaining the subfields of the subject field.
448 - def get_cert_common_name(self):
450
451 - def get_cert_organization(self):
453 456 459
460 - def get_cert_country(self):
462 465
466 - def get_cert_fingerprint(self, fingerprint_length, digest_name):
467 rc, fingerprint_str = pn_ssl_get_cert_fingerprint(self._ssl, fingerprint_length, digest_name) 468 if rc == PN_OK: 469 return fingerprint_str 470 return None
471 472 # Convenience functions for obtaining fingerprint for specific hashing algorithms
474 return self.get_cert_fingerprint(41, 10)
475
477 return self.get_cert_fingerprint(41, SSL.SHA1)
478
480 # sha256 produces a fingerprint that is 64 characters long 481 return self.get_cert_fingerprint(65, SSL.SHA256)
482
484 # sha512 produces a fingerprint that is 128 characters long 485 return self.get_cert_fingerprint(129, SSL.SHA512)
486
487 - def get_cert_fingerprint_md5(self):
488 return self.get_cert_fingerprint(33, SSL.MD5)
489 490 @property
491 - def remote_subject(self):
492 return pn_ssl_get_remote_subject(self._ssl)
493 494 RESUME_UNKNOWN = PN_SSL_RESUME_UNKNOWN 495 RESUME_NEW = PN_SSL_RESUME_NEW 496 RESUME_REUSED = PN_SSL_RESUME_REUSED 497
498 - def resume_status(self):
499 return pn_ssl_resume_status(self._ssl)
500
501 - def _set_peer_hostname(self, hostname):
502 self._check(pn_ssl_set_peer_hostname(self._ssl, unicode2utf8(hostname)))
503
504 - def _get_peer_hostname(self):
505 err, name = pn_ssl_get_peer_hostname(self._ssl, 1024) 506 self._check(err) 507 return utf82unicode(name)
508 509 peer_hostname = property(_get_peer_hostname, _set_peer_hostname, 510 doc=""" 511 Manage the expected name of the remote peer. Used to authenticate the remote. 512 """)
513
514 515 -class SSLSessionDetails(object):
516 """ Unique identifier for the SSL session. Used to resume previous session on a new 517 SSL connection. 518 """ 519
520 - def __init__(self, session_id):
521 self._session_id = session_id
522
523 - def get_session_id(self):
524 return self._session_id
525